初始化过程
初始化过程:init -> $mount -> compile -> new Watcher -> render -> update
- src/platforms/web/runtime/index.js:实现$mount
- src/core/index:全局api
- src/core/instance/index:声明vue构造函数
- src/platforms/web/entry-runtime-with-compiler:覆盖了$mount
- src/core/instance/lifecycle.js mountComponent:执行渲染和更新,虚拟dom -》真实dom
1、定义$mount,patch
src/platforms/web/runtime/index.js
执行挂载方法
Vue.prototype.__patch__ = inBrowser ? patch : noop // 定义一个补丁函数,执行patching算法进行更新
Vue.prototype.$mount = function ( // 定义一个$mount 方法 (挂载根组件到指定宿主元素)
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating) // 执行挂载
}
2、定义全局api
src/core/index.js
initGlobalAPI(Vue)
// src/core/global-api/index.js
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
initUse(Vue) // 实现Vue.use函数
initMixin(Vue) // 实现Vue.mixin函数
initExtend(Vue) // 实现Vue.extend函数
initAssetRegisters(Vue) // 注册并实现指令,组件,过滤器
3、定义Vue构造函数
src/core/instance/index.js
function Vue (options) { // vue构造函数
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options) // 初始化
}
initMixin(Vue) // 实现_init函数
stateMixin(Vue) // 状态相关api,$data,$props,$set,$delete,$watch
eventsMixin(Vue) // 事件相关api,$on,$once,$off,$emit
lifecycleMixin(Vue) // 生命周期,_update,$forceUpdate,$destory
renderMixin(Vue) // 渲染api, _render,$nextTick
src/core/instance/init.js
实现initMixin()
方法,创建组件实例,初始化数据、属性以及事件等等。
export function initMixin (Vue: Class<Component>) { // 创建组件实例,初始化其数据、属性和事件等等
Vue.prototype._init = function (options?: Object) { // 在Vue原型中添加_init方法
...
initLifecycle(vm) // 定义了$parent,$root,$children,$refs,
initEvents(vm) // 处理父组件传递的监听器($on,$emit)
initRender(vm) // $slots,$scopedSlots,_c,$createElement
callHook(vm, 'beforeCreate') // 在beforeCreate生命周期之前执行了以上三个方法
initInjections(vm) // resolve injections before data/props 获取注入数据
initState(vm) // 初始化props,methods,data,computed,watch
initProvide(vm) // resolve provide after data/props 注入数据
callHook(vm, 'created')
...
}
}
4、入口
src/platforms/web/entry-runtime-with-compiler.js
这个文件主要是扩展了$mount的方法,用于处理template和el选项,同时将获取的模板进行编译。
const mount = Vue.prototype.$mount // 获取Vue原型中的 $mount 方法
Vue.prototype.$mount = function ( // 添加一个处理template或者el选项的功能
el?: string | Element, // el 属性其实就是挂载点,所有的挂载元素会被Vue生成的DOM替换掉,如果render跟template都不存在,那么挂载元素的html就会被拿出来当作模板使用。
hydrating?: boolean
): Component {
el = el && query(el) // 查找对应的dom,没有就自己创建一个div
const options = this.$options // 获取Vue实例中的参数
if (!options.render) { // 如果不存在render函数,
//因此可以看到render的优先级高于template高于el
let template = options.template // 才去找template
...
...
// 获取模板后执行编译
//compileToFunctions() 将template字符串转换成render函数
const { render, staticRenderFns } = compileToFunctions(template, { // 编译模板
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render // 将render函数保存的options中
options.staticRenderFns = staticRenderFns
...
}
return mount.call(this,el,hydrating)
}
无论通过template还是el的方式最终生成的都是render函数。
5、mountComponent
src/core/instance/lifecycle.js
export function mountComponent ( // 执行挂载
...
}