vue2源码分析-vue入口文件global-api分析_vuecli2多入口文件golb插件(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

// 如果是数组,则key是数组的索引
// 如果是对象,则key是对象的键值
to[key] = _from[key]
}
//当我们还在苦逼地用 Array.isArray 判断数据类型是否是数组来区分代码写两套逻辑时, vue源代码已经实现了,一个代码同时兼容了数组和对象的拷贝,简直不要太优秀啊
return to
}

  • 优秀代码设计思想
    • 当我们还在苦逼地用 Array.isArray 判断数据类型是否是数组来区分代码写两套逻辑时, vue源代码已经实现了,一个代码同时兼容了数组和对象的拷贝,简直不要太优秀啊

initUse 初始化插件

  • 代码路径 src\vue-2.6.14\src\core\global-api\use.js
  • 入参说明
  • plugin 参数 有可能是函数也有能是对象
    • 当是对象时需要访问install属性并且使用apply修改this为plugin后调用
    • 当是函数时则直接调用
  • 核心代码说明
  • installedPlugins
    • 定义 installedPlugins 参数,
    • 读取vue实例上的_installedPlugins属性,如果读取不到则赋值为空数组
    • 缓存插件,将插件添加到已安装插件的数组中
  • 防止插件的重复加载
    • 如果插件已经安装,则将插件直接返回

import { toArray } from ‘…/util/index’

export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
// _installedPlugins 函数内部的变量 用来存储已经注册的插件
// _installedPlugins 不存在时,初始化为一个空数组
// vue源码中并没有声明 _installedPlugins 并且此处是非箭头函数
if (installedPlugins.indexOf(plugin) > -1) {
// 防止插件的重复加载
return this
}
const args = toArray(arguments, 1)
// toArray 方法将参数转化为数组
// arguments 是一个类数组对象,有length属性,但是没有数组的方法
// 通过toArray方法将arguments转化为数组,并且去掉第一个参数
args.unshift(this)//再将this添加到args数组的头部
if (typeof plugin.install === ‘function’) {
// 如果插件有install方法,则调用install方法
plugin.install.apply(plugin, args)
} else if (typeof plugin === ‘function’) {
// 如果插件是一个函数,则直接调用
plugin.apply(null, args)
}
installedPlugins.push(plugin)//缓存插件,将插件添加到已安装插件的数组中
return this
}
}

initMixin 初始化混入

  • 初始化混入 逻辑有点多,后期单独出一篇文章详细补充

initExtend 初始化vue组件对象

  • 代码路径 src\vue-2.6.14\src\core\global-api\extend.js
    函数说明
  • 基于构造函数Vue,创建了一个Vue组件对象,并初始化vue选项式api
    核心代码说明
  • 获取父类的cid,也就是组件的id,即组件的标识
  • cachedCtors,定义一个缓存对象
    • 如果缓存对象中已经有了当前类的id,则表明在此操作前已经创建过了
    • 通过id访问到缓存对象中的子类,并直接返回
    • 这样做可以提高性能,避免重复的子类创建
  • 获取到组件的name属性
    • 如果当前传入的组件选项中有name属性则直接读取,否则访问Super(父类的)name属性
    • name是组件的标识,一般确实永不到,但是组件递归的时候必须要写,否则无法调用子组件
  • 调用validateComponentName校验name属性是否合法
  • sub类的处理
    • 通过函数的方式创建一个sub类,并将父类的原型链继承到子类中(原型链继承方式),同时还需要修正constructor的指向
    • 将父类的options字段和传入组件对象选项进行合并,并保存在子类sub的options字段中
    • 将父类保存到子类的 super 字段中,确保子类能拿到父类
  • 初始化 props
    • props 是当前组件的props属性
    • const in props 遍历对象的属性
    • 将 组件传入的props属性代理到组件实例的_props属性上
  • 初始化 computed
    • computed 是当前组件的computed属性
    • const in props 遍历对象的属性
    • defineComputed 逻辑暂时没看,后期单独出一期文章补充
  • extend,mixin,use等api添加到子类中
  • 新增 superOptions,extendOptions,sealedOptions等选项
  • 将子类返回

/* @flow */

import { ASSET_TYPES } from ‘shared/constants’
import { defineComputed, proxy } from ‘…/instance/state’
import { extend, mergeOptions, validateComponentName } from ‘…/util/index’

export function initExtend (Vue: GlobalAPI) {
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped “child
* constructors” for prototypal inheritance and cache them.
*/
// sjf-note-best-code
Vue.cid = 0//初始化cid为0 可以理解为组件的标识
let cid = 1

/**
* Class inheritance
*/
Vue.extend = function (extendOptions: Object): Function {
// 基于构造函数Vue,创建了一个Vue组件对对象,并初始化vue选项式api
extendOptions = extendOptions || {}
// extendOptions是一个对象,如果没有传入,则初始化为空对象
// extendOptions 是组件传入的选项
const Super = this//将this(vue的实例)赋值给Super
const SuperId = Super.cid

const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
//定义一个缓存对象,用生成的id作为属性键,如果cachedCtors[SuperId]存在则表明该组件的构造函数已经生成,直接返回即可
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}

const name = extendOptions.name || Super.options.name
// extendOptions 当前组件的选项 的
// Super 是 Vue的实例 options 是Vue的选项
// 如果传入了extendOptions.name 则使用extendOptions.name 否则使用Super.options.name
if (process.env.NODE_ENV !== ‘production’ && name) {
//判断是否是开发环境
validateComponentName(name)
//校验组件名称是否合法, 如果不合法会抛出异常
}
const Sub = function VueComponent (options) {

this._init(options)
//此处的this是vue实例 ,调用了_init方法,初始化组件实例
}
Sub.prototype = Object.create(Super.prototype)
// Super.prototype 是Vue的原型对象
// Object.create(Super.prototype) 创建了一个新的对象,该对象的原型是Super.prototype
// Object.create() 方法创建一个新对象,不会有原型链上的属性和方法

Sub.prototype.constructor = Sub// consturctor指向自身 sjf-todo
// 通过函数的方式创建一个sub类,并将父类的原型链继承到子类中(原型链继承方式),同时还需要修正constructor的指向
Sub.cid = cid++//组件的cid自增1

Sub.options = mergeOptions(
//将父类的options字段和传入组件对象选项进行合并,并保存在子类sub的options字段中
Super.options,//Vue的选项
extendOptions//当前组件的选项
)
Sub[‘super’] = Super//将Super(根组件的实例)赋值给当前组件的super属性
// 将父类保存到子类的 super 字段中,确保子类能拿到父类
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
//如果当前组件有props属性,则初始化props
initProps(Sub)
}
if (Sub.options.computed) {
//如果当前组件有computed属性,则初始化computed
initComputed(Sub)
}

// allow further extension/mixin/plugin usage
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// Vue的一些原生API 等扩展到子构造器上边
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}

// keep a reference to the super options at extension time.
// later at instantiation we can check if Super’s options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// Vue的一些原生API 等扩展到子构造器上边
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
}
}

function initProps (Comp) {
const props = Comp.options.props
// props 是当前组件的props属性
for (const key in props) {
// const in props 遍历对象的属性
proxy(Comp.prototype, \_props, key)
// proxy 代理数据
// 此处是将 组件传入的props属性代理到组件实例的_props属性上
}
}

function initComputed (Comp) {
const computed = Comp.options.computed
// computed 是当前组件的computed属性
for (const key in computed) {
// const in computed 遍历对象的属性
defineComputed(Comp.prototype, key, computed[key])
// defineComputed 代理数据
// 此处是将 组件传入的computed属性代理到组件实例的_props属性上
}
}

initAssetRegisters 注册或者获取全局组件、指令、过滤器

  • src\vue-2.6.14\src\core\global-api\assets.js
    函数说明
  • 注册或者获取全局 component 组件、directive指令、 filter 过滤器
    函数入参说明
  • id 标识名称id
  • definition 定义类型是函数或者对象
    • 当type 是directive或者filter的时候,是函数
    • 当type是components的时候是,对象
      核心代码说明
  • 判断是是否有传入 definition 参数
    • 无则表明不是注册全局组件、指令、过滤器,而直接读取,直接通过标识名称id读取返回即可
    • 有则标明是注册
  • 校验组件名是否合规
    • 非生产环境 并且type值是component组件名称,则需要校验组件名是否合规
    • 如果不合规则在 validateComponentName函数中抛出异常,下文有说明
  • 根据type类型分别做逻辑处理
  • component
  • type为 component,并且 isPlainObject 判断 definition类型是 object,则执行下边逻辑
    • 读取 name 属性,如果读不到definition的name属性,则使用标识名称id
    • 调用 vue 的extend扩展定义,并重新赋值
  • directive
    • type值为 directive ,并且 definition的类型为 function
    • 定义一个对象有bindupdate属性,赋值为definition
  • 以上逻辑处理好了directive和component选项下的definition,filter选项不需要特殊处理
  • 根据选项式api对象读取到指定id的component 组件、directive指令、 filter 过滤器并赋值为 definition
  • 将 definition返回给调用者

/* @flow */

import { ASSET_TYPES } from ‘shared/constants’
import { isPlainObject, validateComponentName } from ‘…/util/index’

export function initAssetRegisters (Vue: GlobalAPI) {
/**
* Create asset registration methods.
*/
/*
‘component’,
‘directive’,
‘filter’
*/
// 注册或者获取全局组件、指令、过滤器
debugger
ASSET_TYPES.forEach(type => {//定义资源注册方法
Vue[type] = function (
id: string,//标识名称id
definition: Function | Object//定义函数或者对象
// 当type 是directive或者filter的时候,是函数
// 当type是components的时候是,对象
): Function | Object | void {
//
console.log(“id_definition”,type,id,definition)
if (!definition) {
// 没有传入definition 的时候,则之直接读取并返回
return this.options[type + ‘s’][id]
} else {
/* istanbul ignore if */
if (process.env.NODE_ENV !== ‘production’ && type === ‘component’) {
// 非生产环境 交易案组件名称,如果不合规,则抛出异常
validateComponentName(id)
}
if (type === ‘component’ && isPlainObject(definition)) {
definition.name = definition.name || id//设置 definition.name属性
// 值为组件传入的name || id
definition = this.options._base.extend(definition)
// 调用 vue 的extend扩展定义,并重新赋值
}
if (type === ‘directive’ && typeof definition === ‘function’) {
definition = { bind: definition, update: definition }
// 自定义指令的处理逻辑
}
this.options[type + ‘s’][id] = definition
return definition
}
}
})
}

源码中使用到的小代码片段补充

ASSET_TYPES

export const ASSET_TYPES = [
‘component’,
‘directive’,
‘filter’
]

proxy代理函数

  • 代码引用地址 src\vue-2.6.14\src\core\instance\state.js
  • 使用defineProperty代理数据

const sharedPropertyDefinition = {//代理数据的配置
enumerable: true,//是否可枚举
configurable: true,//是否可修改删除
get: noop,//get方法,用于读取数据 noop 定义一个临时的空的函数
set: noop//set方法,用于设置数据 noop 定义一个临时的空的函数
}

export function proxy (target: Object, sourceKey: string, key: string) {
// 代理数据
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key] //返回数据
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val//设置数据
}
Object.defineProperty(target, key, sharedPropertyDefinition)
// 通过Object.defineProperty方法,将sharedPropertyDefinition的get和set方法绑定到target对象的key属性上
}

toArray 将目标数组中的数据,从指定位置开始拷贝

  • 代码引用地址 src\vue-2.6.14\src\shared\util.js
  • 将目标数组中的数据,从指定位置开始拷贝

代码实现原理

  1. 接收字段
    • list 源数据
    • start 从源数据的哪个位置开始拷贝 默认为 0
  1. 声明一个变量i值为 list.length - start
  2. while 循环
    • 然后将 i + start
    • 则 最后的 i + start 仍然等于 list.length
    • 当i的值为0时,循环结束
  1. 则实现了将原数组从指定位置拷贝到目标数组的操作
    代码优秀设计思想分析,它凭什么优秀
  2. 第一点,一般拷贝数组,
  • 常规的操作就是 for 循环,遍历数组中的每一个数据,然后根据条件判断,将数组中满足条件的数据拿到或者将不满足条件的数据过滤掉,这样确实能够处理数据,但是却需要将每条数据都循环一遍 .
  • 优秀的写法 此处,使用了while循环,不需要循环每一条数据,在调件变量为false的时候就跳出了循环,提高了代码的执行效率
  1. 可拷贝从指定位置开始的数据
  2. 语法层面上使用了 new Array(i)
  • 创建了指定数组,并且指定了位数,同时生明其中的每个变量都为empty
  • 很多人都知道new Array 却不知道 可以传入个数参数

当我们还开为这中需求苦恼,并且写一堆js时,vue源码中已经写出来如此优秀的代码,值得参考

export function toArray (list: any, start?: number): Array {
// sjf-note-best-code
start = start || 0
let i = list.length - start
const ret: Array = new Array(i)
// new Array() 创建一个空数组
// new Array(i) 创建i个为空的数组
// 相当于 Array.fill(0,i,undefined)

while (i–) {
ret[i] = list[i + start]
//先将 i = list.length - start

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
const ret: Array = new Array(i)
// new Array() 创建一个空数组
// new Array(i) 创建i个为空的数组
// 相当于 Array.fill(0,i,undefined)

while (i–) {
ret[i] = list[i + start]
//先将 i = list.length - start

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-CrJJXRsF-1713423377856)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值