在分析之前我们先来看看,vue中都有哪些Watcher种类呢?以及分别在什么时候创建呢?从vue源码里面看,Watcher是一个公共类,在不同的地方去初始化Watcher就代表不同类的Watcher。主要分为以下三类:
- computed watcher:执行计算属性更新computed
- user watcher:用户注册的普通watcher
- render watcher:负责视图更新
注意者三种watcher的执行顺序为computed watcher
、user watcher
、render watcher
这么执行原因也很简单,当computed
、watcher
将数据更新完成之后,要渲染到我们的视图上的时候触发render watcher
好了,下面我们来看看vue中三种watcher的创建时机。
Vue的首次渲染
new Vue实例化时,执行_init方法(src\core\instance\init.js)
执行_init方法(在src\core\instance\init.js定义)
主要源码:
// 对传入的配置与默认配置合并
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {
},
vm
)
vm._self = vm
initLifecycle(vm) // 初始化组件的$parent\$root\$children\$ref属性
initEvents(vm) // 初始化组件的事件监听
initRender(vm) // 初始化$slots属性, 定义 $createElement和vm._c方法(用于生成虚拟dom),对组件的$listeners、$attrs做响应式处理
callHook(vm, 'beforeCreate') // 执行beforeCreate生命周期函数
initInjections(vm) // 获取组件的inject所有属性,并做响应式处理
initState(vm) // 组件数据的初始化以及响应式处理:按顺序 props => methods => data => computed => watch
initProvide(vm) // 获取组件的provide所有属性,并做响应式处理
callHook(vm, 'created') // 执行created生命周期函数
// 处理完组件相关的数据后,开始执行$mount方法
if (vm.$options.el) {
vm.$mount(vm.$options.el) // $mount 在src\platforms\web\entry-runtime-with-compiler.js文件中执行了扩展
}
Computed Watcher
在vue的初始化阶段,调用initState,判断对象的computed是否存在值,如果存在则调用initComputed初始化computed,在这个方法中对computed Watcher进行了初始化。
源码位置:src/core/instance/state.js
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
...
if (opts.computed) initComputed(vm, opts.computed)
...
}
在initComputed中,首先定义了一个空的watchers对象对象迎来存储所有的计算属性对应的watcher;接着遍历组件中定义的计算属性,且对其添加watcher进行监听(调用defineComputed添加get和set)
function initComputed (vm: Component, computed: Object) {
// 定义一个watchers空对象,用于存储所有计算属性对应的watcher
const watchers = vm._computedWatchers = Object.create(null)
// computed properties are just getters during SSR
const isSSR = isServerRendering()
//遍历每个计算属性
for (const key in computed) {
const userDef = computed[key] //userDef的每一项都是computed对应的回调函数
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (process.env.