以下是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
- 一定情况下保持声明key原来的getter和setter的切面影响
- 通过shallow传入来控制对象该key的值是否需要继续纳入响应式管理
- 具体通过闭包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类
- Dep类有一个静态属性target,代表当前watcher,全局唯一,每次只会有一个正在进行的watcher,但是目标watcher是一个栈并且有配套的入栈和出栈操作,分别对应pushTarget和popTarget,这种设计可以支持嵌套的调用和watcher处理。
- Dep类通过depend方法建立和watcher的双向关联,并且watcher关联dep是通过dep的addSub方法,反之取消关联是通过dep的removeSub方法
- Dep类通过notify方法来通知关联wathcer[]来进行更新及动作
- Dep代表响应式系统中的被观察者
- 所有Dep实例统一进行Uid分配,即Uid++
Watcher类
- watcher分为renderWatcher和userWatcher,二者都会关联在vm的_watchers[]列表中,而且renderWatcher还会关联在vm的_watcher
- lazy-watcher的value会通过Watcher的evaluate设置
- Dep的notify触发watcher的update会区分几种情况,一种是lazy的话会设置dirty的flag,如果是sync的话会直接同步执行getter并且执行cb回调,其它情况会queueWatcher
- Watcher的get执行在执行初始化解析的getter之前会pushTarget,这样在getter执行中若有相关的dep.depend,则会关联当前watcher和dep
- Watcher监听path的解析算法是简单的a.b.c属性点分割,但解析后的结果是一个闭包
- watcher的回调执行只有在其value发生变化,或者value是对象,或者是deep时才会执行,具备一定的惰性
- queueWatcher的算法是若watcher队列还没有被flush则添加即可,若已经被flush了但是还没有到它,则对刚flush的待执行队列进行查找替换,否则安排至nextTick下一波队列
- Watcher代表响应式系统中的订阅者
- 所有Watcher实例统一进行Uid分配,即Uid++
callHook: 执行相应的钩子函数及vm自身的钩子事件监听(如vm.$on('hook:created'))
initInjections: 略
initState: 声明vm实例属性[_watchers],对props、methods、data、computed、watch等状态系统中的概念进行初始化
initProvide: 略
callHook: 同上
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。