首先需要了解vue的响应设计
Vue响应系统,其核心有三点:observe、watcher、dep:
observe:遍历data中的属性,使用 Object.defineProperty 的get/set方法对其进行数据劫持,
顺便在get加入了dep依赖收集,在set里面加入了触发dep依赖更新
dep:每个属性拥有自己的消息订阅器dep,用于存放所有订阅了该属性的观察者对象
watcher:观察者(对象),通过dep实现对响应属性的监听,监听到结果后,
主动触发自己的回调进行响应 (执行observe的时候会初始化这个观察者)
对响应式系统有一个初步了解后,我们再来分析计算属性。
首先我们找到计算属性的初始化是在src/core/instance/state.js
文件中的initState
函数中完成的
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 */)
}
// computed初始化
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
调用了initComputed
函数(其前后也分别初始化了initData
和initWatch
)并传入两个参数vm
实例和opt.computed
开发者定义的computed
选项,转到initComputed
函数:
const computedWatcherOptions = { computed: true }
function initComputed (vm: Component, computed: Object) {
// $flow-disable-line
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]
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (process.env.NODE_ENV !== 'production' && getter == null) {
warn(
`Getter is missing for computed property "${key}".`,
vm
)
}
if (!isSSR) {
// create internal watcher for the computed property.
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
// component-defined computed properties are already defined on the
// component prototype. We only need to define computed properties defined
// at instantiation here.
if (!(key in vm)) {
defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {
if (key in vm.$data) {
warn(`The computed property "${key}" is already defined in data.`, vm)
} else if (vm.$options.props && key in vm.$options.props) {
warn(`The computed property "${key}" is already defined as a prop.`, vm)
}
}
}
}
定义一个计算属性有两种写法,一种是直接跟一个函数,另一种是添加set
和get
方法的对象形式,所以这里首先获取计算属性的定义userDef
,再根据userDef
的类型获取相应的getter
求值函数。
defineComputed并将所有computed
属性通过defineProperties
绑定到vm
上,以便可以通过vm[key]
直接获取到值