学如逆水行舟,不进则退!当今社会,很美好,也很残酷。一般软件或互联网公司,大多数的业务其实是没有很高的难度,所以员工也没有什么机会去提升自己。不过值得庆幸的是,编程界开源风气盛行,功夫秘籍随手可得,而代价仅是你的时间。
本次文章的主题是 computed(计算属性) 和 watch(侦听器),代码基于 Vue 2.6.11 版本,和 2.5 还是有一些区别。
用法回顾
我们先来简单回顾下 computed 和 watch 的用法。
computed
export default {
name: 'App',
data() {
return {
user: {
name: 'Jack',
job: 'UI',
},
};
},
computed: {
name() {
return 'I am ' + this.user.name;
},
},
};
在 computed 属性中,我们定义了 name,其值来自 data 中 user.name。只要 user.name 的值发生了变化,那么 computed 中的 name 就行改变。
定义 computed 的好处就是我们可以定一个变量,很方便引用“其它的对象数据封装处理”后的值。并且 computed 中的属性是响应式的,值改变了,会触发 DOM 的更新。如果依赖的值没有改变,computed 的值不会重新计算,而是复用上一次缓存的值。
watch
export default {
name: 'App',
data() {
return {
color: 'red'
};
},
watch: {
color(newVal) {
alert('Color is changed', newVal)
}
}
};
computed 主要关注的是值改变后的新值和缓存功能,而 watch 主要关注值改变后,我要做什么动作。
源码分析
如果读者从来没有阅读过 Vue 相关的源码,接下来的内容可能有点云里雾里。博主会尽量把内容放在 computed 和 watch 这一块的流程上,辅助少量的其他模块的细节。
computed
Vue 在初始化时,会执行 _init,该方法在 Vue 原型对象上定义的。
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// 省略了一大波细节
// expose real self
vm._self = vm
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)
}
}
这个方法里面有执行一个函数 initState(vm)
,包含如 data、computed、watch 等属性的处理逻辑。
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 */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
从上面代码中,我们在最后几行代码可以看到 computed 和 watch 的处理。顺便一提,这里的 nativeWatch 判断主要是用来处理火狐浏览器上的一个兼容性问题,火狐浏览器中其默认对象的原型上有一个原生的 watch 属性。
我们先来看看 initComputed()
这个方法。
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 (