精读 Vue 官方文档系列 🎉
使用 Computed 进行值的转换。
- 非异步的基于数据变更监听来进行值的转换完全可以使用
computed来代替watch。 - 如果转换的值会被多个地方使用到,更应该使用计算属性,因为计算属性具有缓存的特性。
- 如果不需要缓存,则可以考虑直接在模板中使用方法进行转换,因为响应式数据的变更必定会触发模板的重新渲染,然后方法便可以被重新执行。
<template>
<div>
<p><input type="text" v-model="str" /></p>
<p>
<b>{{ btoa }}</b>
</p>
<p>repeated:{{ btoa }}</p>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
str: "hello world!",
};
},
computed: {
btoa() {
return window.btoa(this.str);
},
},
};
</script>
因为模板中无法访问 window 对象,所以需要借助计算属性调用 window.btoa 方法,如果你是用的外部导入的方法,那么在转换数据无需多次使用的情况下,可以更加的精简代码:
<template>
<div>
<p><input type="text" v-model="str" /></p>
<p>
<b>{{ changeStr(str) }}</b>
</p>
</div>
</template>
<script>
import {myBtoA} from './myBtoA.js';
import {ref} from '@vue/composition-api';
export default defineComponent({
name: "UseComputed",
setup(){
const {value:str, changeStr:change} = myBtoA();
return {
str,
changeStr
}
});
};
</script>
其实最常见的需求场景应该属接收组件外的
props然后转换为组件内的data
<script>
export default defineComponent({
name:"UseComputed",
props:["propData"],
setup(props){
const data = computed(()=>props.propData);
return {data};
}
})
</script>
当然,实现的方式有很多,例如 watch 监听命令式执行转换,或者是基于事件回调,但都没有 computed 的那么优雅。
扩展一下,如果此时需要为数据的变更增加防抖呢?解决的办法:
- 将
v-model拆分为 value 与 input 两个,然后专门为 input 事件应用debounce方法,来防抖的进行数据变更。 - 自定义指令,劫持
input事件进行防抖。 - 使用
v-model.lazy进行一定小幅度的优化。
使用 computed 进行条件过滤。
{
computed: {
filterData() {
let dataSource = this.dataSource;
let searchFilter = this.searchFilter;
let sortKey = this.sortKey;
if (searchFilter) {
dataSource = dataSource.filter((record) => {
return Object.keys(record).some((key) => {
return String(record[key]).toLowerCase().indexOf(searchFilter) > -1;
});
});
}
if (sortFiter) {
dataSource = dataSource.sort((a, b) => {
const first = a[sortKey];
const last = b[sortKey];
return a === b ? 0 : a > b ? 1 : -1;
});
}
return dataSource;
},
},}
如果你的数据源受多种响应式条件影响,那么将它们放在一个 computed 中进行处理,将是一个非常适合的方式。
watch 与 computed 结合
我们总是强调,computed 用在一个数据受多个数据影响的场景,而 watch 则用在一个数据的变更会影响多个数据的情况,但实际上,二者可以结合使用,将 computed 作为 watch 的监听数据,可以将它们的优势发挥到极致。
<template>
<div>
<input type="text" v-model="value1" />
<input type="radio" value="a" v-model="value2" /><input type="radio" value="b" v-model="value2" />
<hr/>
<button @click="download">download</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
value1: "",
value2: "a",
};
},
computed: {
filter() {
const { value1, value2 } = this;
return { value1, value2 };
},
},
methods: {
download() {
console.log("download", this.filter);
},
getData() {
console.log("fetch data", this.filter);
},
},
created() {
this.$watch("filter", this.getData, { immediate: true });
},
};
</script>