vue 组件源码分析
组件注册
1.全局组件:全局使用
2.局部组件:只能在内部使用
Vue.component() 的实现
位置:src/core/global-api/index.js 内的initAssetRegisters()注册了compoennt方法
作为一个字符串定义在ASSET_TYPES 中,函数接收Vue实例作为参数
component接收id和definition做参数
之后判断如果是组件或是指令,作出特殊处理,如果是过滤器记录到Vue全局配置上
判断type是component的情况,获取组件的name属性作为名称,如果没有的话,使用id作为名称
最终调用options._base.extend(),就是Vue的构造函数
Vue.extend()
是将其转换为vue构造函数的子类,就是对应组件的构造函数
是Vue构造函数的静态方法,在src/core/global-api/extend.js下
- 接收extendOptions作为参数,是组件的选项对象
- super是vue的构造函数,获取选项的构造函数或是初始化空对象
- 获取组件名称,验证组件名称是否合法
- 调用组件的_init的方法,利用Sub实现了对vue的原型继承
- 初始化子组件的props,computed,filter等静态方法继承
- 把组件构造函数保存到Ctor.options.component中
- 返回该组件的构造函数
Vue 首次渲染过程
- 就是Vue的构造函数
- this._init()
- this.$mount()
- mountComponent()
- new Watcher()渲染 Watcher
- updateComponent()
- vm._render() -> createElement()
- vm._update()
Vue 组件的创建过程
- createElement 的定义位置
createElement先去处理参数,在调用_createElement函数 - _createElement中处理组件位置
判断tag 如果是字符串,则代表是普通标签
如果不是字符串,则是 代表组件,调用createComponent创建组件 - createComponent先获取Vue构造函数合并选项,
如果ctor没有cid,就认为是异步组件
通过vue.mixin合并混入的选项,resolveConstructorOptions
处理组件上的v-model
提取props
安装组件上的钩子函数,installComponentHooks,获取用户传入的钩子函数,通过mergeHook合并本身和用户传入钩子函数
获取组件名称,创建组件对应的VNode对象
Vue 组件的patch过程
-
patch函数位置:src/core/vdom/patch.js
patch函数最终会返回createElm创建新的VNode挂载到dom树
createElement会调用createComponent处理组件的vnode -
会先获取vnode.data中的hook:钩子函数 和获取 init的钩子函数
调用init钩子的时候传递vnode和fasle,init处理keepalive和处理组件,
调用组件的vnode.componentOptions.Ctor,创建组件的实例,在向下创建子组件过程
通过initLifecycle函数的中的 parent过程建立父子组件的关系 -
在lifecycleMixin中的_updata方法 通过 setActiveInstance将当前组件的实例缓存起来,
会先执行父组件的updata方法,它内部调用patch,在patch中,创建子组件,调用子组件的update方法
整个过程是解决了组件嵌套,找父子组件过程 -
此时,子组件的选项中没有el属性,$mount是不执行的
找到组件的钩子函数,在执行componentInstance过程时候,调用子组件构造函数,
在子组件的构造函数中,调用子组件_init方法
子组件执行过后,调用insert将组件插入到父组件上,initComponent函数就是用来初始化组件的事件/样式/钩子函数的
是先挂载子组件再挂载父组件因为过程太多,不易理解
需要记住
组件创建是先父组件再子组件
组件的挂载是先挂载子组件再挂载父组件
组件的粒度不是越小越好,因为嵌套一层组件需要在重新执行一边组件的创建过程,比较消耗性能