vue 2.0源码学习笔记—new Vue ( Vue 初始化过程 )

new Vue(Vue 初始化)

一个vue实例化到底经历了什么?已下是博主自己的总结,不正确的地方请指出,谢谢~

一、简述

从使用角度来看,挂载的顺序如下

1. $slots
2. $scopedSlots
3. beforeCreate
4. inject
5. props
6. methods
7. data
8. computed
9. watch
10. provide
11. created
12. beforeMount
13. mounted

从源码角度来看,运行的顺序如下

Vue实例创建的时候,主要运行了_init函数,他将会调用一系列init函数,以及生命周期函数
1. initEvents(vm)  
    初始化事件, 将_events(清空), _hasHookEvent(false)挂载到vm实例上, 初始化 listeners
2. initRender(vm) 
    挂载_vnode, _staticTrees, $slots, $scopedSlots
    挂载_c(用于定义虚拟节点), $createElement(用于定义虚拟节点)
    使用defineReactive定义 vm 响应式的 $attrs, $listeners
3. callHook(vm, 'beforeCreate')
    调用beforeCreate钩子函数
4. initInjections(vm)
    读取上一级inject(注入)的变量 
    用法: inject: ['reload']
5. initState(vm)
    依次挂载props, methods, data, computed, watch(都使用了proxy 代理到了vm实例上,所以可以this.的方式去访问)
6. initProvide(vm)
    输出本组件的数据,传给需要的子组件 
    用法: provide () {
            return {
              reload: this.reload,
              xxx: this.ooo,
            }
          }
7. callHook(vm, 'created')
    调用created钩子函数
8. vm.$mount(vm.$options.el) => mountComponent(this, el, hydrating)
    (1) callHook(vm, 'beforeMount')
        调用beforeMount钩子函数
    (2) 添加updateComponent的观察者
        new Watcher(vm, updateComponent, noop, { 
          before () {
            if (vm._isMounted && !vm._isDestroyed) {
              callHook(vm, 'beforeUpdate') // 调用beforeUpdate钩子函数
            }
          }
         }, true )
        
    (3) callHook(vm, 'mounted') 
        调用mounted钩子函数

使用new Vue

使用 vue-cli创建(vue create 项目名称)项目,找到main.js文件

...
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App />'
})
...

二、源码层面理解(vue源码)

1. 源码下载

前往git下载 [https://github.com/vuejs](vue-dev源码)

2. Vue 构造函数

文件位置:vue-dev/src/core/index.js

...
function Vue (options) {
  ...
  this._init(options)
}

// 为Vue原型(Vue.prototype.)添加各种函数
initMixin(Vue) // 添加 _init 函数
stateMixin(Vue) // 添加 $data, $props, $set, $delete, $watch(expOrFn, cb, options)
eventsMixin(Vue) // 添加 $on, $once, $off, $emit 事件
lifecycleMixin(Vue) // 添加 _update, $forceUpdate, $destroy 函数
renderMixin(Vue) // 添加 $nextTick, _render 函数

export default Vue

其中关键的就是this._init(options),它由initMixin(Vue)添加到Vue对象的原型上,在new Vue的最后调用,传入options

3. _init函数

文件位置:vue-dev/src/core/instance/init.js

Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++
    ...
    // merge options
    if (options && options._isComponent) {
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor), options || {}, vm)
    }
    ...
    vm._self = vm
    initLifecycle(vm) // 初始化vm
    initEvents(vm) // 初始化 _events(清空), _hasHookEvent, listeners
    initRender(vm) /* 初始化 _vnode, _staticTrees, $slots, $scopedSlots, _c, $createElement
                      使用defineReactive定义 vm 响应式的 $attrs, $listeners*/
    callHook(vm, 'beforeCreate') // 调用beforeCreate钩子函数
    initInjections(vm) // resolve injections before data/props
    initState(vm) /* 依次初始化props, methods, data, computed, watch 
                     initProps, initMethods, initData, initComputed, initWatch */
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created') // 调用created钩子函数
    ...

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
      /*
        $mount在vue-dev/src/platforms/web/runtime/index.js处挂载, 调用了 return mountComponent(this, el, hydrating)
        而mountComponent函数在vue-dev/src/core/instance/lifecycle.js处定义
        主要完成以下步骤
        1. callHook(vm, 'beforeMount') // 调用beforeMount钩子函数
        2. new Watcher(vm, updateComponent, noop, { // 添加updateComponent的观察者
            before () {
              if (vm._isMounted && !vm._isDestroyed) {
                callHook(vm, 'beforeUpdate') // 调用beforeUpdate钩子函数
              }
            }
           }, true )
        3. callHook(vm, 'mounted') // 调用mounted钩子函数
      */
    }
  }

initState(vm)详解

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)
  }
}

在initState中,完成了props, methods, data, computed, watch的挂载(props>methods>data)

以其中的initData为例:

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  // 判断是否为对象 Object.prototype.toString.call(obj) === '[object Object]'
  ...
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    ...
      proxy(vm, `_data`, key) // data中的属性代理到vm实例上去,这样就可以在vm实例中使用this.xx访问到data中的xx了
    ...
  }
  // observe data
  observe(data, true /* asRootData */) // 为data添加观察者,这样修改就能触发更新
}

完~

posted @ 2021-09-28 18:34  每天进步一点点OVO  阅读(586)  评论(0)    收藏  举报