以下是Vue构造声明核心代码,是跨平台共用的核心代码,为什么说跨平台共用呢,这里主要指的是不同平台(web和weex)在vue.prototype上还会丰富自己的特性(属性和方法),如比较重要的__patch__方法、以及平台特定的组件和指令。

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

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')
  }
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

其中各种Mixin主要是丰富原型链上的属性或方法:

initMixin: 声明vue.prototype.init,后面会细看,见原型init
stateMixin: 声明vue.prototype.[$data | $props]属性,以及$set、$delete、$watch方法
eventsMixin: 声明vue.prototype.[$on | $off | $once | $emit]方法,让vm自身获得了事件的注册触发能力
lifecycleMixin: 声明vue.prototype.[_update | $forceUpdate | $destroy]方法
renderMixin: 声明vue.prototype.[_render | $nextTick]方法,以及一系列内部helpers工具函数,以(_字母)别名方法

切换到Web平台视角

先暂且只看web平台目录给vue.prototype上增添[_patch_ | $mount]方法,并且将平台特定指令和组件安装到构造Vue.options上。

原型init

其中主要部分,一部分是:

initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')

另一部分是:

if (vm.$options.el) {
  vm.$mount(vm.$options.el)
}

其它包含vue的options的解析、合并及合并策略。

第一部分:
initLifecycle: 声明vm实例属性[$parent | $root | $children | $refs | _watcher | _inactive | _directInactive | _isMounted | _isDestroyed | _isBeingDestroyed],其中$parent会涉及parent的解析和关系绑定,即在parent的$children中注册子vm

initEvents: 声明vm实例属性[_events | _hasHookEvent],以及更新组件的事件listeners或者注销事件

initRender: 声明vm实例属性[_vnode | _staticTrees | $options | $vnode | $slots | $scopedSlots | _c | $createElement | $attrs | $listeners],其中$attrs和$listeners是响应式的属性,但是浅响应的。即在
defineReactive声明时指定shallow为true

看一看defineReactive

  1. 一定情况下保持声明key原来的getter和setter的切面影响
  2. 通过shallow传入来控制对象该key的值是否需要继续纳入响应式管理
  3. 具体通过闭包Dep实例将该key纳入响应式系统

通过dep.depend将target:[Watcher]放入自己的subs
看上去是调用watcher的addDep方法,并且该方法会判断是否已经依赖该dep

depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

实质上最后还是调用了dep的addSub方法

addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
        dep.addSub(this)
      }
    }
  }

这样一来,dep中有watcher(通过subs),watcher中也有dep(通过newDepIds)

Dep类

  1. Dep类有一个静态属性target,代表当前watcher,全局唯一,每次只会有一个正在进行的watcher,但是目标watcher是一个栈并且有配套的入栈和出栈操作,分别对应pushTarget和popTarget,这种设计可以支持嵌套的调用和watcher处理。
  2. Dep类通过depend方法建立和watcher的双向关联,并且watcher关联dep是通过dep的addSub方法,反之取消关联是通过dep的removeSub方法
  3. Dep类通过notify方法来通知关联wathcer[]来进行更新及动作
  4. Dep代表响应式系统中的被观察者
  5. 所有Dep实例统一进行Uid分配,即Uid++

Watcher类

  1. watcher分为renderWatcher和userWatcher,二者都会关联在vm的_watchers[]列表中,而且renderWatcher还会关联在vm的_watcher
  2. lazy-watcher的value会通过Watcher的evaluate设置
  3. Dep的notify触发watcher的update会区分几种情况,一种是lazy的话会设置dirty的flag,如果是sync的话会直接同步执行getter并且执行cb回调,其它情况会queueWatcher
  4. Watcher的get执行在执行初始化解析的getter之前会pushTarget,这样在getter执行中若有相关的dep.depend,则会关联当前watcher和dep
  5. Watcher监听path的解析算法是简单的a.b.c属性点分割,但解析后的结果是一个闭包
  6. watcher的回调执行只有在其value发生变化,或者value是对象,或者是deep时才会执行,具备一定的惰性
  7. queueWatcher的算法是若watcher队列还没有被flush则添加即可,若已经被flush了但是还没有到它,则对刚flush的待执行队列进行查找替换,否则安排至nextTick下一波队列
  8. Watcher代表响应式系统中的订阅者
  9. 所有Watcher实例统一进行Uid分配,即Uid++

callHook: 执行相应的钩子函数及vm自身的钩子事件监听(如vm.$on('hook:created'))
initInjections: 略
initState: 声明vm实例属性[_watchers],对props、methods、data、computed、watch等状态系统中的概念进行初始化
initProvide: 略
callHook: 同上


疾风剑豪
6 声望1 粉丝

往事随风