总结

  1. Vue 初始化,添加实例成员、静态成员,并在原型上挂载patch方法和$mount方法。

  2. 初始化结束,调用new Vue()。在new Vue()的过程中,调用this._init()方法, 给vue的实例挂载各种功能。_init方法就相当于Vue入口

  3. 在 this.init() 内部最终会调用 entry-runtime-with-compiler.js中的 vm.$mount(),用于获取render函数。

  4. $mount 获取 render 过程: 如果用户没有传入render,会将template编译为render,如果template也没有,则将el中的内容作为模版,通过 compileToFunctions() 生成render。

  5. 接下来调用 runtime/index.js 中的 $mount, 重新获取 el 并调用 mountComponent() 方法。
    mountComponent 用于触发 beforeMount,定义 updateComponent,创建watcher实例,触发mounted,并最终返回vm实例。

  6. 创建完watcher的实例后会调用一次 watcher.get() 方法,该方法会调用updateComponent(), updateComponent()又会调用 vm.render() 以及vm.update(),vm._update()会调用vm.patch()挂载真实dom,并将真实dom记录于vm.$el中。

Vue初始化过程

  • 首先取出Vue的$mount,对$mount进行重写,给$mount增加新的功能

    1. // src/platform/web/entry-runtime-with-compiler.js
    2. // 保留 Vue实例的 $mount 方法
    3. const mount = Vue.prototype.$mount
    4. Vue.prototype.$mount = function (
    5. el?: string | Element,
    6. // 非ssr情况下的为false, ssr时候为true
    7. hydrating?: boolean
    8. ): Component {
    9. // 获取el对象
    10. el = el && query(el)
    11. ...
    12. }
  • 判断是否有render选项,如果没有render选项,则会把模版template取出,把模版编译成render函数,接着调用mount方法,渲染DOM。

  • 给Vue增加了一个静态的compile方法,作用是把HTML字符串编译成render函数

    1. if (!options.render) {
    2. let template = options.template
    3. if (template) {
    4. ...
    5. }
    6. }
    7. Vue.compile = compileToFunctions
    8. export default Vue
  • 这个文件主要是通过extend给Vue全局注册了指令和组件,组件是Transition和TransitionGroup,指令是v-model和v-show,接着在Vue的原型上注册了 patch 函数, patch 函数作用是将虚拟DOM转换成真实DOM,在给patch函数赋值的时候会判断是否是浏览器环境

  • 我们继续找Vue的构造函数
    1. // src/platforms/web/runtime/index.js
    2. extend(Vue.options.directives, platformDirectives)
    3. extend(Vue.options.components, platformComponents)
    4. // 判断是否是浏览器环境
    5. Vue.prototype.patch = inBrowser ? patch : noop

初始化静态成员

  • 定义在src/core/index.js

在这个文件中调用initGlobalAPI(Vue)方法,给Vue的构造函数增加静态方法
initGlobalAPI(Vue)

  • initGlobalAPI(Vue) 定义在src/core/global-api/index.js
  1. 初始化Vue.config对象
  2. 注册设置keep-alive 组件
  3. Vue.use()用来注册插件
  4. 注册Vue.mixin()实现混入
  5. 注册Vue.extend()基于传入的options返回一个组件的构造函数
  6. 注册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(
    1. 'Do not replace the Vue.config object, set individual fields instead.'
    ) } } // 初始化 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) }
  1. <a name="fzVfF"></a>
  2. ## 初始化实例成员
  3. - _init()
  4. 1. 定义在src/core/instance/index.js定义了构造函数
  5. 1. 调用了this._init(options)方法
  6. 1. 给Vue中混入了常用的实例成员
  7. ```javascript
  8. function Vue (options) {
  9. if (process.env.NODE_ENV !== 'production' &&
  10. !(this instanceof Vue)
  11. ) {
  12. warn('Vue is a constructor and should be called with the `new` keyword')
  13. }
  14. // 调用 _init()方法
  15. this._init(options)
  16. }
  17. // 注册vm的_init()方法, 初始化vm
  18. initMixin(Vue)
  19. // 注册vm 的$data/$props/$set/$delete/$watch
  20. stateMixin(Vue)
  21. // 初始化事件相关方法
  22. //$on/$once/$off/$emit
  23. eventsMixin(Vue)
  24. // 初始化生命周期相关的混入方法
  25. // _update/$forceUpdate/$destroy
  26. lifecycleMixin(Vue)
  27. // 混入 render
  28. // $nextTick/_render
  29. renderMixin(Vue)
  30. export default Vue

初始化实例成员 init()

  1. 当静态成员和实例成员都初始化完成之后,接着调用Vue的构造函数
  2. 在构造函数中调用_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 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  1. <a name="N3Umo"></a>
  2. ## 初始化实例成员 initState()
  3. ```javascript
  4. export function initState (vm: Component) {
  5. vm._watchers = []
  6. const opts = vm.$options
  7. if (opts.props) initProps(vm, opts.props)
  8. if (opts.methods) initMethods(vm, opts.methods)
  9. if (opts.data) {
  10. initData(vm)
  11. } else {
  12. observe(vm._data = {}, true /* asRootData */)
  13. }
  14. if (opts.computed) initComputed(vm, opts.computed)
  15. if (opts.watch && opts.watch !== nativeWatch) {
  16. initWatch(vm, opts.watch)
  17. }
  18. }
  • 在instance/state.js中,首先获取了Vue实例中的$options,然后判断options中是否有props,methods,data以及computed和watch这些属性,如果有的话,通过initProps进行初始化
  • initProps(vm, opts.props)接收了两个参数,一个是Vue实例,一个是Props属性,我们跳转到initProps函数中,首先给Vue实例定义了一个_Props对象, 并且把它存储到了常量里面

    1. 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中存在,并且判断了命名的规范,不建议_和$开头

initData

  • options中有data选项时,会调用initData(vm)
  • 当没有的时候此时会给vm初始化一个_data属性observe(vm._data = {}, true)然后调用observe函数,observe是响应式中的一个函数
  • initData中获取了optionsdata选项,判断了data选项是否是function,如果是调用getData(data,vm)接着获取data中的所有属性,同时获取了props,methods中所有的属性

    initComputed

    initWatch