总结
Vue 初始化,添加实例成员、静态成员,并在原型上挂载patch方法和$mount方法。
初始化结束,调用new Vue()。在new Vue()的过程中,调用this._init()方法, 给vue的实例挂载各种功能。_init方法就相当于Vue入口
在 this.init() 内部最终会调用 entry-runtime-with-compiler.js中的 vm.$mount(),用于获取render函数。
$mount 获取 render 过程: 如果用户没有传入render,会将template编译为render,如果template也没有,则将el中的内容作为模版,通过 compileToFunctions() 生成render。
接下来调用 runtime/index.js 中的 $mount, 重新获取 el 并调用 mountComponent() 方法。
mountComponent 用于触发 beforeMount,定义 updateComponent,创建watcher实例,触发mounted,并最终返回vm实例。创建完watcher的实例后会调用一次 watcher.get() 方法,该方法会调用updateComponent(), updateComponent()又会调用 vm.render() 以及vm.update(),vm._update()会调用vm.patch()挂载真实dom,并将真实dom记录于vm.$el中。
Vue初始化过程
首先取出Vue的$mount,对$mount进行重写,给$mount增加新的功能
// src/platform/web/entry-runtime-with-compiler.js
// 保留 Vue实例的 $mount 方法
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
// 非ssr情况下的为false, ssr时候为true
hydrating?: boolean
): Component {
// 获取el对象
el = el && query(el)
...
}
判断是否有render选项,如果没有render选项,则会把模版template取出,把模版编译成render函数,接着调用mount方法,渲染DOM。
给Vue增加了一个静态的compile方法,作用是把HTML字符串编译成render函数
if (!options.render) {
let template = options.template
if (template) {
...
}
}
Vue.compile = compileToFunctions
export default Vue
这个文件主要是通过extend给Vue全局注册了指令和组件,组件是Transition和TransitionGroup,指令是v-model和v-show,接着在Vue的原型上注册了 patch 函数, patch 函数作用是将虚拟DOM转换成真实DOM,在给patch函数赋值的时候会判断是否是浏览器环境
- 我们继续找Vue的构造函数
// src/platforms/web/runtime/index.js
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
// 判断是否是浏览器环境
Vue.prototype.patch = inBrowser ? patch : noop
初始化静态成员
- 定义在src/core/index.js
在这个文件中调用initGlobalAPI(Vue)方法,给Vue的构造函数增加静态方法
initGlobalAPI(Vue)
- initGlobalAPI(Vue) 定义在src/core/global-api/index.js
- 初始化Vue.config对象
- 注册设置keep-alive 组件
- Vue.use()用来注册插件
- 注册Vue.mixin()实现混入
- 注册Vue.extend()基于传入的options返回一个组件的构造函数
- 注册Vue.directive(), Vue.component(), Vue.filter
```javascript
export function initGlobalAPI (Vue: GlobalAPI) {
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== ‘production’) {
configDef.set = () => {
warn(
) } } // 初始化 Vue.config对象 Object.defineProperty(Vue, ‘config’, configDef) // exposed util methods. // NOTE: these are not considered part of the public API - avoid relying on // them unless you are aware of the risk. // 这些工具方法不视作全局Api的一部分,除非你已经意识到某些风险,否则不要去依赖他们 Vue.util = { warn, extend, mergeOptions, defineReactive } // 静态方法 set/delete/nextTick Vue.set = set Vue.delete = del Vue.nextTick = nextTick // … Vue.options._base = Vue // 设置keep-alive 组件 extend(Vue.options.components, builtInComponents) // 注册Vue.use()用来注册插件 initUse(Vue) // 注册Vue.mixin()实现混入 initMixin(Vue) // 注册Vue.extend()基于传入的options返回一个组件的构造函数 initExtend(Vue) // 注册Vue.directive(), Vue.component(), Vue.filter initAssetRegisters(Vue) }'Do not replace the Vue.config object, set individual fields instead.'
<a name="fzVfF"></a>
## 初始化实例成员
- _init()
1. 定义在src/core/instance/index.js定义了构造函数
1. 调用了this._init(options)方法
1. 给Vue中混入了常用的实例成员
```javascript
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
// 调用 _init()方法
this._init(options)
}
// 注册vm的_init()方法, 初始化vm
initMixin(Vue)
// 注册vm 的$data/$props/$set/$delete/$watch
stateMixin(Vue)
// 初始化事件相关方法
//$on/$once/$off/$emit
eventsMixin(Vue)
// 初始化生命周期相关的混入方法
// _update/$forceUpdate/$destroy
lifecycleMixin(Vue)
// 混入 render
// $nextTick/_render
renderMixin(Vue)
export default Vue
初始化实例成员 init()
- 当静态成员和实例成员都初始化完成之后,接着调用Vue的构造函数
- 在构造函数中调用_init()方法_init是在initMixin中初始化的,主要对Vue实例初始化 ```javascript // vm的生命周期相关变量初始化 initLifecycle(vm) // vm的事件监听初始化,父组件绑定在当前组件上的事件 initEvents(vm) // vm的编译render初始化 // $slots/$scopedSlots/_c/$createElement/$attrs/$listeners initRender(vm) // beforeCreate 生命钩子的回调 callHook(vm, ‘beforeCreate’) // 把inject的成员注入到vm上 initInjections(vm) // resolve injections before data/props // 初始化vm的 _props/methods/_data/computed/watch initState(vm) // 初始化provide initProvide(vm) // resolve provide after data/props // created 生命钩子回调 callHook(vm, ‘created’)
作者:大白菜_ 链接:https://juejin.im/post/6855129007668035591 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
<a name="N3Umo"></a>
## 初始化实例成员 initState()
```javascript
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
- 在instance/state.js中,首先获取了Vue实例中的$options,然后判断options中是否有props,methods,data以及computed和watch这些属性,如果有的话,通过initProps进行初始化
initProps(vm, opts.props)接收了两个参数,一个是Vue实例,一个是Props属性,我们跳转到initProps函数中,首先给Vue实例定义了一个_Props对象, 并且把它存储到了常量里面
const props = vm._props = {}
紧接着,开始遍历PropsOptions的所有属性,它其实就是initProps方法中的第二个参数,遍历每个属性,然后通过defineReactive注入到Props这个对象上,这个props其实就是vm._props所有的成员都会通过defineReacttive转化为get和set,最后在Props对象上存储,
「注意」
- 在开发模式中,如果我们直接给这个属性赋值的话,会发出一个警告,
- 生产环境中直接通过defineReactive把props中的属性转化成get和set
- 最后判断了props属性是否在Vue实例中存在,不存在通过Proxy这个函数把我们的属性注入到Vue的实例中
- 在Proxy中,通过调用Object.defineProperty(target, key,sharePropertyDefinition)
initProps
initProps的作用就是把我们的Props成员转化成响应式数据,并且注入到Vue实例里面中
initMethods
initMethods作用就是把选项的methods注入到vue实例,在注入之前,会先判断我们命名是否在Props中存在,并且判断了命名的规范,不建议_和$开头