大家好,今天文章由团队的 Uncle13 老师提供,题目也是来源于真实的面试。
“computed 和 watch 有哪些区别?”,今天的文章除了介绍两者的区别和使用场景,还会通过源码的剖析,带大家了解具体的实现原理。
以下是正文。
相信 Vue 技术栈的小伙伴都了解,Vue 提供了两种处理响应式数据的方式:计算属性(Computed Properties)和侦听属性(Watchers)。
这两种方式都用于监视数据的变化并执行相应的逻辑,但它们在使用场景和功能上有一些区别。
计算属性是 Vue 提供的一种属性,用于在模板中放置逻辑处理,以便计算和返回一个新的响应式属性。
计算属性会缓存计算结果,在依赖的数据未改变时会直接返回缓存的结果,避免不必要的重复计算。
在模板中可以像访问普通属性一样访问计算属性。
<template>
<div>
<p>Original: {{ message }}</p>
<p>Computed: {{ reversedMessage }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, Vue!'
};
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('');
}
}
};
</script>
侦听属性允许你在数据变化时执行异步或开销较大的操作。
通过使用侦听属性,你可以监听特定的数据变化并执行自定义的逻辑,比如发起一个网络请求、更新其他数据等。
<template>
<div>
<p>Message: {{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, Vue!'
};
},
watch: {
message(newValue, oldValue) {
console.log('Message changed:', newValue, oldValue);
// 在这里执行其他逻辑操作
}
}
};
</script>
接着让我们再来深入源码,看看 Computed
和 Watchers
是如何实现的。
在 Vue 中,计算属性的工作原理可以概括为以下几个步骤:
计算属性的核心在于定义一个具有 get
方法的函数,该函数在属性被访问时自动执行,返回计算结果。
Vue 内部会创建一个依赖追踪系统,使得计算属性能够自动依赖于其所引用的响应式属性,当这些属性发生变化时,计算属性会重新计算。
计算属性的实现主要在 core/instance/state.js
中,其中的核心逻辑是在 initState
函数中处理的:
function initState(vm) {
// ...
if (opts.computed) initComputed(vm, opts.computed);
// ...
}
// 初始化计算属性
function initComputed(vm, computed) {
const watchers = (vm._computedWatchers = Object.create(null));
for (const key in computed) {
const userDef = computed[key];
const getter = typeof userDef === 'function' ? userDef : userDef.get;
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
);
// 将计算属性代理到 vm 实例上
if (!(key in vm)) {
defineComputed(vm, key, userDef);
}
}
}
// 定义计算属性
export function defineComputed(target, key, userDef) {
const shouldCache = !isServerRendering();
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: userDef;
sharedPropertyDefinition.set = noop;
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: userDef.get
: noop;
sharedPropertyDefinition.set = userDef.set || noop;
}
// ...
Object.defineProperty(target, key, sharedPropertyDefinition);
}
在 Vue 中,侦听属性的工作原理可以概括为以下几个步骤:
侦听属性的核心在于创建一个 Watcher 实例来监视特定的数据变化,当数据发生变化时,执行相应的回调函数。侦听属性可以用于处理异步操作、复杂逻辑、多属性的变化等场景。
侦听属性的实现也是在 core/instance/state.js
中的 initState
函数中处理:
function initState(vm) {
// ...
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch);
}
// ...
}
// 初始化侦听属性
function initWatch(vm, watch) {
for (const key in watch) {
const handler = watch[key];
createWatcher(vm, key, handler);
}
}
// 创建侦听属性的 Watcher
function createWatcher(vm, expOrFn, handler, options) {
return vm.$watch(expOrFn, handler, options);
}
// Vue 实例的 $watch 方法
Vue.prototype.$watch = function (expOrFn, cb, options) {
const vm = this;
options = options || {};
const watcher = new Watcher(vm, expOrFn, cb, options);
if (options.immediate) {
cb.call(vm, watcher.value);
}
return function unwatchFn() {
watcher.teardown();
};
};
根据具体的需求,可以灵活选择使用计算属性或侦听属性来管理响应式数据的变化。
顺便也给我们的辅导服务打个广告: