读Vue源码1---new Vue初始化

143 阅读3分钟

入口文件src/core/instance/index

定义了Vue构造函数,初始化Vue相关属性

//this instanceof Vue是不是通过new Vue出来的实例
function Vue(options) {

  // 在initMixin中,初始化配置
  this._init(options)
}
// Vue 的 prototype 上扩展⼀些⽅法
initMixin(Vue)          //挂载初始化方法(_init)
//挂在状态处理方法:对Vue.prototype上的data、props属性进行劫持,并在上面挂载set、del方法
stateMixin(Vue)      
//在Vue.prototype上创建 on off once emit方法
eventsMixin(Vue)        //挂载事件的方法
//定义了_update方法、forceUpdate方法
lifecycleMixin(Vue)     //挂载生命周期方法
//原型上挂载nextTick、定义了_render(生成Vnode)
renderMixin(Vue)        //挂载与渲染有关的方法

initMixin

在Vue原型上定义了_init函数,该函数主要功能是

  • vm._uid = uid++ 给每一个组件进行标识
  • vm._isVue = true 判断是否是Vue实例,如果是则不需要被observed响应话
  • merge options 合并配置项
  • 定义了vm._renderProxy ,作用是在render中将this指向vm._renderProxy
  • 初始化生命周期,事件,Render函数等等
  • 组件挂载
let uid = 0;
export function initMixin(Vue: Class<Component>) {

  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    //每一个类型实例都会有唯一标识
    vm._uid = uid++
    // a flag to avoid this being observed
    // 是否需要响应化,
    // 如果是Vue的实例,则不需要被observe
    vm._isVue = true
    // merge options
    // 第一步: options参数的处理
    if (options && options._isComponent) {
      initInternalComponent(vm, options)
    } else {
      // mergeOptions合并属性,为options增加属性
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    // 第二步: renderProxy
    if (process.env.NODE_ENV !== 'production') {
      // 定义了vm._renderProxy,这是后期为render做准备的,作用是在render中将this指向vm._renderProxy
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm)             //初始化生命周期的 一些状态变量
    initEvents(vm)                //初始化事件的容器
    initRender(vm)                //初始化创建元素的方法
    callHook(vm, 'beforeCreate')  //调用生命周期函数
    // 初始化注入器
    initInjections(vm) // resolve injections before data/props
    // 初始化数据,传入vm实例
    // vm的状态初始化,prop/data/computed/method/watch都在这里完成初始化,因此也是Vue实例create的关键。
    initState(vm)                 //重点,初始化状态数据 (data property等)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    if (vm.$options.el) {
      // 组件的挂载
      /**
       * 先调用扩展的$mount方法,生成render
       * 在调用原始的$mount     获得元素,在调用mountComponent方法
       */
      vm.$mount(vm.$options.el)
    }
  }
}

为什么在created之后才能拿到数据?

因为在beforeCreate之前没有initstate,而调用initstate之后,data生成了,
然后组件实例创建成功执行的created

initLifecycle(vm)             //初始化生命周期的 一些状态变量
initEvents(vm)                //初始化事件的容器
initRender(vm)                //初始化创建元素的方法
callHook(vm, 'beforeCreate')  //调用生命周期函数
// 初始化注入器
initInjections(vm) // resolve injections before data/props
// 初始化数据,传入vm实例
// vm的状态初始化,prop/data/computed/method/watch都在这里完成初始化,因此也是Vue实例create的关键。
initState(vm)                 //重点,初始化状态数据 (data property等)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')

initState

初始化props、methods、data、computed、watch,对其属性创建watcher响应化

// 初始化数据,传入vm实例
export function initState(vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  // 处理options.props成员
  if (opts.props) initProps(vm, opts.props)
  // 处理options.methods成员
  if (opts.methods) initMethods(vm, opts.methods)

  // vm存在属性data
  // 处理options.data 响应式化
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }

  // 处理options.computed 计算属性
  if (opts.computed) initComputed(vm, opts.computed)
  // 处理options.watch
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

initProps

// 属性响应式化
  defineReactive(props, key, value)
  if (!(key in vm)) {
  // 将_proxy上的成员映射到Vue实例上,不需要app._props.xxx直接app.xxx
  proxy(vm, `_props`, key)
}

initData

 // 对应的data数据
  let data = vm.$options.data
  // 判断data是函数还是对象
  // getData  改变指向data.call(vm, vm),相当于返回一个新的data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  // 如果不是function就报一个警告
  //循环data中每一个属性,定义在data中的变量不能和props methods重名,
  hasOwn(methods, key)
  hasOwn(props, key)
  //将data代理到实例上
  proxy(vm, `_data`, key)
  //将data响应话
    observe(data, true /* asRootData */)

initComputed

循环computed对象里的每一项,对其创建watcher响应化,不能和data props methods 重名 
否则报出警告

initMethods

- 判断是否是函数
- 是否与props重名
// 将methods属性的中方法绑定上下文后挂载到Vue实例上
vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)

initWatch

对组件内watcher对象的每个属性建立一个watcher
props、methods、data、computed 重名问题
首先会有警告,但是页面显示数据优先级如下

prop>data
data>computed>methods
computed>methods>props

有个比较特别的,根组件中好像methods>computed,大家可以试一下

stateMixin

  对Vue.prototype上的data、props属性进行劫持,对其响应化,并在上面挂载set、del方法
  Object.defineProperty(Vue.prototype, "$data", dataDef);
  Object.defineProperty(Vue.prototype, "$props", propsDef);

  Vue.prototype.$set = set;
  Vue.prototype.$delete = del;

eventsMixin

在Vue.prototype上创建 on off once emit方法,进行事件监听,关闭,监听一次,触发事件

lifecycleMixin

  • 定义了_update方法
  • forceUpdate方法,就是调用vm._watcher.update(),触发依赖更新
  • Vue.prototype上定义destroy方法
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
    if (!prevVnode) {
      // 首次渲染
      // initial render
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
    } else {
      // 数据更新,diff 返回新的DOM
      // updates
      vm.$el = vm.__patch__(prevVnode, vnode);
    }
  };
  Vue.prototype.$forceUpdate = function () {
    const vm: Component = this;
    if (vm._watcher) {
      vm._watcher.update();
    }
  };

renderMixin

  • 原型上挂载nextTick
  • 定义了_render
Vue.prototype.$nextTick = function (fn: Function) {
    return nextTick(fn, this);
  };
// render函数,主要实现:vnode = render.call(vm._renderProxy, vm.$createElement)
  Vue.prototype._render = function (): VNode {
    const vm: Component = this;
    const { render, _parentVnode } = vm.$options;
    
    vm.$vnode = _parentVnode;
    // render self
    let vnode;
    try {
      //调用_renderProxy生成Vnode
      vnode = render.call(vm._renderProxy, vm.$createElement);
    } 
    if (Array.isArray(vnode) && vnode.length === 1) {
      vnode = vnode[0];
    }
    // return empty vnode in case the render function errored out
    //如果说render函数报错了就返回一个空的Vnode
    if (!(vnode instanceof VNode)) {
      vnode = createEmptyVNode();
    }
    // set parent
    //将vnode的parent指向_parentVnode
    vnode.parent = _parentVnode;
    return vnode;
  };