- Vue3笔记(一):MVVM设计模式、CompositionAPI、响应式数据
- Vue3笔记(三):class与style样式、表单处理与双向数据绑定
- Vue3笔记(四):组件、组件间通信、$attrs、插槽 - 掘金 (juejin.cn)
- Vue3笔记(五):单文件组件、Vue CLI - 掘金 (juejin.cn)
05 计算属性与侦听器区别与原理
计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,所以过于复杂的逻辑可以移植到计算属性中进行处理。
<div id="app">{{ reverseMessage }}</div>
<script>
let vm = Vue.createApp({
data() {
return {
message: "hello",
};
},
computed: {
reverseMessage() {
return this.message.split("").reverse().join("");
},
},
}).mount("#app");
</script>
计算属性与方法比较像,如下所示:
<div id="app">
{{ reverseMessageMethod() }}<br />
{{ reverseMessageMethod() }}<br />
{{ reverseMessage }}<br />
{{ reverseMessage }}<br />
</div>
<script>
let vm = Vue.createApp({
data() {
return {
message: "hello world",
};
},
methods: {
reverseMessageMethod() {
console.log(1);
return this.message.split(" ").reverse().join(" ");
},
},
computed: {
reverseMessage() {
console.log(2);
return this.message.split(" ").reverse().join(" ");
},
},
}).mount("#app");
</script>
计算属性跟方法相比,具备缓存的能力,而方法不具备缓存,所以上面代码执行完,控制台会显示两次 1 和一次 2。 计算属性虽然是对象方法,但是当做属性使用(调用时不加括号),并且计算属性是只读的。
注意:计算属性默认是只读的,一般不会直接更改计算属性,而是修改其依赖的数据从而触发计算属性的更新。如果想直接更改计算属性也是可以做到的,通过 Setter 写法实现,官方地址。
总结:计算属性computed和方法methods的区别
-
如果一个业务流程没有返回值,则用methods实现,有返回值,用computed和methods都可以实现
-
计算属性和方法都是函数,计算属性一定有返回值,它通过对数据进行处理,返回一个结果
-
在模板中调用时,计算属性不加(),而methods必须需要加()
-
计算属性和方法最主要的区别是计算属性有缓存功能。 方法被调用时每次都要重复执行函数,而计算属性初次调用时执行函数,然后会缓存结果。当再次被调用时,如果依赖的响应式数据没有发生改变,则直接返回之前缓存的结果。如果依赖发生了改变,则会再次执行函数并缓存结果。
既然计算属性编写的是一个函数,而调用的时候以函数名的形式进行使用,其实实现起来也不是特别难的事情:
let computed = {
num() {
return 123;
},
};
let vm = {};
for (let attr in computed) {
Object.defineProperty(vm, attr, {
value: computed[attr](),
});
}
监听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。侦听器的目的:侦听器用来观察和响应 Vue 实例上的数据变动,类似于临听机制+事件机制。当有一些数据需要随着其它数据变化而变化时,就可以使用侦听器。
<div id="app">{{ message }}</div>
<script>
let vm = Vue.createApp({
data() {
return {
message: "hello",
};
},
watch: {
message(newVal, oldVal) {
console.log(newVal)
},
},
}).mount("#app");
setTimeout(() => {
vm.message = "hi vue";
}, 2000)
</script>
有时候,计算属性和侦听器往往能实现同样的需求,那么他们有何区别呢?
看一个计算属性和侦听器分别实现拼接全名的例子:
- computed一定有返回值,而watch不需要返回值
- 计算属性适合:多个值去影响一个值的应用;而侦听器适合:一个值去影响多个值的应用(因为对每个值都要写个函数监听,比较麻烦)
- 计算属性在调用时需要在模板中使用,修改计算所依赖元数据;watch在调用时只需修改元数据
- 计算属性默认深度依赖,可以监测对象值的改变,watch默认浅度观测,只监测字面量(也可设置deep为true来开启深度监测)
- 计算属性在刚进入页面的时候,自动触发;watch默认不触发(也可设置immediate为true来触发)
- 计算属性适合做筛选,不可异步或DOM操作;watch适合做执行异步或开销较大的操作(比如DOM操作)。
当你更改了响应式状态,它可能会同时触发 Vue 组件更新和侦听器回调。
默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。
如果想在侦听器回调中能访问被 Vue 更新之后的 DOM,你需要指明 flush: 'post' 选项:
export default {
// ...
watch: {
key: {
handler() {},
flush: 'post'
}
}
}
为什么计算属性不能做异步而监听器可以异步?
因为计算属性必须return,而return发生在异步之前。而监听器不涉及return。
参考:
06 条件渲染与列表渲染
条件渲染
v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
在 JavaScript 中,truthy(真值)指的是在布尔值上下文中,转换后的值为真的值。所有值都是真值,除非它们被定义为 falsy 假值(即除 false、0、-0、0n、“”、null、undefined 和 NaN 以外皆为真值)。
<div id="app">
<div v-if="isShow">aaaaa</div>
<div v-else>bbbbb</div>
</div>
<script>
let vm = Vue.createApp({
data() {
return {
isShow: 0,
};
},
}).mount("#app");
</script>
v-if和v-else要连着用,中间不能插入其他标签。
列表渲染
v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
<div id="app">
<div v-for="item, index in list">{{ item }}, {{ index }}</div>
<div v-for="value, key, index in info">
{{ value }}, {{ key }}, {{ index }}
</div>
<div v-for="item in num">{{ item }}</div>
<div v-for="item in text">{{ item }}</div>
</div>
<script>
let vm = Vue.createApp({
data() {
return {
list: ["a", "b", "c"],
info: { username: "xiaoming", age: 20 },
num: 10,
text: "hello",
};
},
}).mount("#app");
</script>
条件渲染与列表渲染需要注意的点
- 列表渲染需要添加 key 属性,用来跟踪列表的身份
- v-if 和 v-for 尽量不要在一个标签内一起使用,可利用计算属性来完成筛选这类功能(因为 v-if 优先级高于 v-for,这样 v-if 拿不到 v-for 中的 item 属性)
- template 标签的作用:形成一个整体容器并且不会被渲染。常用在
v-if等标签,若使用div标签会多一个层级。