VueJS - 计算属性
VueJS - 计算属性
Section titled “VueJS - 计算属性”模板非常适合简单的表达式,但将过多的复杂逻辑直接放在模板中会使模板变得混乱且难以维护。对于任何涉及响应式(reactive)数据的复杂逻辑,您都应该使用计算属性(computed properties)。
计算属性允许您定义一个属性,该属性的值派生自其他响应式状态。它们基于其响应式依赖项(dependencies)进行缓存(cache),这意味着只有当这些依赖项中的任何一个发生变化时,它们才会重新计算。
假设您有一个包含嵌套响应式数据的对象,并且您想根据它是否有项目来显示一条消息。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Computed Property Example</title> <script src="https://unpkg.com/vue@3"></script></head><body> <div id="app"> <h2>Author: {{ author.name }}</h2>
<!-- Directly in template (can get complex) --> <!-- 直接放在模板中(可能会变得复杂) --> <p>Has published books? {{ author.books.length > 0 ? 'Yes' : 'No' }}</p>
<!-- Using computed property (cleaner) --> <!-- 使用计算属性(更整洁) --> <p>Has published books (Computed)? {{ publishedBooksMessage }}</p>
<h3>Books:</h3> <ul> <li v-for="(book, index) in author.books" :key="index">{{ book }}</li> </ul> <button @click="addBook">Add Book</button> <button @click="removeBook">Remove Book</button> </div>
<script> const { createApp, reactive, computed } = Vue;
createApp({ setup() { const author = reactive({ name: 'Jane Doe', books: ['Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide'] });
// Define a computed property // 定义一个计算属性 const publishedBooksMessage = computed(() => { console.log('Computed property executing...'); // Log to see when it runs // 打印日志以查看何时执行 // It depends on author.books // 它依赖于 author.books return author.books.length > 0 ? 'Yes' : 'No'; });
function addBook() { author.books.push(`New Book #${author.books.length + 1}`); } function removeBook() { if (author.books.length > 0) { author.books.pop(); } }
return { author, publishedBooksMessage, // Expose computed property to template // 将计算属性暴露给模板 addBook, removeBook }; } }).mount('#app'); </script></body></html>在这个示例中:
- 我们有一个响应式的
author对象。 - 第一个段落直接在模板中计算消息。这样做是可行的,但如果逻辑更复杂,它就会变得难以阅读。
publishedBooksMessage是一个使用Vue.computed()定义的计算属性。它接受一个 getter 函数。- 该计算属性依赖于
author.books.length。 - 在模板中,我们像访问常规属性一样访问
publishedBooksMessage(不需要())。 - 打开您的浏览器控制台(console)。注意 “Computed property executing…” 仅在您添加或删除书籍时(即当
author.books改变时)打印日志,而不是在组件每次重新渲染时都打印。这展示了其缓存行为。
计算属性的缓存 vs. 方法
Section titled “计算属性的缓存 vs. 方法”您可能想知道为什么需要计算属性,而使用方法(method)也能达到同样的效果。
// Using a method instead// 改用方法// setup() {// ...// function calculateBooksMessage() {// console.log('Method executing...');// return author.books.length > 0 ? 'Yes' : 'No';// }// return { author, calculateBooksMessage };// }// Template: <p>Has published books (Method)? {{ calculateBooksMessage() }}</p>// 模板: <p>Has published books (Method)? {{ calculateBooksMessage() }}</p>尽管方法可以产生相同的结果,但关键区别在于缓存(caching)。计算属性基于其响应式依赖项进行缓存。计算属性只有当它的一个依赖项发生变化时才会重新计算。相比之下,每当重新渲染发生时,方法调用总是会运行其函数。
何时使用哪个?
- 当您需要基于其他响应式状态派生状态时,请使用计算属性,特别是当计算成本较高或您想从缓存中获益时。
- 当您不需要缓存时,当您需要响应事件(例如在
@click中)执行操作时,或者当您需要向函数传递参数时,请使用方法。
示例:缓存演示
Section titled “示例:缓存演示”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Computed vs Method Caching</title> <script src="https://unpkg.com/vue@3"></script></head><body> <div id="app"> <p>Computed Value: {{ computedValue }}</p> <p>Computed Value: {{ computedValue }}</p> <!-- Accessing multiple times --> <!-- 多次访问 -->
<p>Method Value: {{ methodValue() }}</p> <p>Method Value: {{ methodValue() }}</p> <!-- Calling multiple times --> <!-- 多次调用 -->
<button @click="triggerReRender">Trigger Re-render (Change unrelated state)</button> <!-- 触发重新渲染(改变不相关的状态) --> <p>Unrelated Value: {{ unrelatedValue }}</p> </div>
<script> const { createApp, ref, computed } = Vue;
createApp({ setup() { const unrelatedValue = ref(0);
const computedValue = computed(() => { console.log('COMPUTED function running'); return Date.now(); // Example of a value that changes if re-run // 每次重新运行时都会改变的值的示例 });
function methodValue() { console.log('METHOD function running'); return Date.now(); }
function triggerReRender() { unrelatedValue.value++; }
return { computedValue, methodValue, triggerReRender, unrelatedValue }; } }).mount('#app'); </script></body></html>运行此示例并打开控制台:
- 初始加载时,‘COMPUTED function running’ 和 ‘METHOD function running’ 都将打印日志(方法可能会因为模板中的多次调用而打印两次)。
- 注意计算属性显示的时间戳在两个地方是相同的,而方法的时间戳可能会略有不同。
- 点击 ‘Trigger Re-render’ 按钮。这将改变
unrelatedValue,导致重新渲染。 - 观察控制台:‘METHOD function running’ 再次打印日志(两次),但 ‘COMPUTED function running’ 不打印日志。计算属性返回其缓存的值,因为其依赖项没有改变。
- 计算属性显示的时间戳保持不变,而方法显示的时间戳更新了。
可写的计算属性 (Getters 和 Setters)
Section titled “可写的计算属性 (Getters 和 Setters)”默认情况下,计算属性是只读的(getter-only)。如果您尝试为计算属性赋新值,您会收到运行时警告。但是,您可以通过提供 get 和 set 函数来创建一个可写的计算属性。
示例:带 Setter 的计算属性 FullName
Section titled “示例:带 Setter 的计算属性 FullName”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Vue 3 Writable Computed</title> <script src="https://unpkg.com/vue@3"></script></head><body> <div id="app"> <div>First Name: <input v-model="firstName"></div> <div>Last Name: <input v-model="lastName"></div> <hr> <div>Full Name (Computed): <input v-model="fullName"></div> </div>
<script> const { createApp, ref, computed } = Vue;
createApp({ setup() { const firstName = ref('John'); const lastName = ref('Doe');
// Writable computed property // 可写的计算属性 const fullName = computed({ // Getter: calculates value from source state // Getter:从源状态计算值 get() { return firstName.value + ' ' + lastName.value; }, // Setter: called when fullName is assigned a new value // Setter:当 fullName 被赋予新值时调用 set(newValue) { console.log('Setter called with:', newValue); // Update the source state based on the new value // 根据新值更新源状态 const names = newValue.split(' '); firstName.value = names[0] || ''; // Handle potential empty input // 处理潜在的空输入 lastName.value = names.slice(1).join(' ') || ''; // Handle names with multiple parts // 处理包含多个部分的名字 } });
return { firstName, lastName, fullName }; } }).mount('#app'); </script></body></html>在这个示例中:
fullName计算属性同时拥有get和set。get函数组合了firstName和lastName。- 当您在“Full Name”输入字段中输入内容时(因为
v-model试图设置fullName),setter 函数会被触发。 - setter 函数会分割输入的值,并更新原始的
firstName和lastNamerefs。 - 这创建了一种双向绑定(two-way binding)效果:改变姓或名会更新全名,而改变全名会更新姓和名字段。
计算属性是一个强大的特性,它通过将派生状态逻辑封装在组件的脚本中,使您的模板更加整洁和易于维护。
延伸阅读:Vue 官方文档关于计算属性的内容:https://vuejs.org/guide/essentials/computed.html