简介
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
虽然可以通过函数、事件等解决上面问题,但vue提供了更便捷的计算属性computed。
<body>
<div id="app">
<input type="text" v-model="xing" placeholder="姓" />
<input type="text" v-model="ming" placeholder="名" />
<div>
<input type="text" v-model="xingming" placeholder="姓 名" />
</div>
</div>
</body>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
xing: "",
ming: "",
// xingming:"" //不要在data定义计算属性名,否则computed里会失效
},
computed: {
xingming: function() {
return this.xing + " " + this.ming
}
}
});
</script>
运行:
getter与setter
计算属性默认只有 getter 用来返回计算出来的值,但我们也可以提供一个为计算属性设置值时候调用的函数setter。以上面的例子来说,如果我们直接输入【姓 名】栏,【姓】【名】都不会变,如果需要【姓】随之响应,则可以用到setter:
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
xing: "",
ming: "",
},
computed: {
//为了方便展示,有小修改
xingming: {
//getter
get: function() {
return this.xing + this.ming
},
//setter
set: function(newValue) {
this.xing = newValue.charAt(0)
this.ming = newValue.charAt(1)
},
}
}
});
</script>
运行,此时设置【姓名】会按set方法中写的逻辑改变【姓】:
缓存
上面的例子如果使用methods可以达到同样的效果,但不同的是,如果使用了computed,值是有缓存的,只在相关响应式依赖发生改变时它们才会重新求值,那么在多次调用时,computed会执行更少次数:
<body>
<div id="app">
<input type="text" v-model="xing" placeholder="姓" />
<input type="text" v-model="ming" placeholder="名" />
<div v-for="index in 6" :key="index">
<!-- v-for中用methods打印 -->
<div style="background-color: red;">方法打印{{funXingMing()}}</div>
<!-- v-for中用computed打印 -->
<div style="background-color: blue;">计算属性打印{{xingming}}</div>
</div>
</div>
</body>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
xing: "",
ming: "",
},
methods: {
funXingMing() {
console.log("methods执行");
return this.xing + this.ming;
}
},
computed: {
xingming: {
get: function() {
console.log("computed get执行");
return this.xing + this.ming;
},
}
}
});
</script>
运行会发现,改变input后,循环中每个div都会调用funXingMing(),而computed只调用1次。
深入响应式原理
下面的例子中用computed属性绑定了v-model,v-model中的输入将被用于和data中属性组成新的属性obj1中元素,并在下方<div>展示,而最上方<button>用于切换隐藏/显示该<div>。
<body>
<div id="content">
<button @click="show=!show">点一下</button>
<input type="text" v-model="myObj" />
<div v-show="show">{{myObj}}</div>
</div>
</body>
<script>
var vm = new Vue({
el: '#content',
data: {
show: true,
obj: [
{"name": "张三"},
{"name": "李四"},
{"name": "王五"},
],
obj1: []
},
computed: {
myObj: {
get() {
if (this.obj1[0]) {
return this.obj1[0].name;
} else {
return "";
}
},
set(newValue) {
this.obj1[0] = {
"id": this.obj[0],
"name": newValue
}
}
}
}
});
</script>
运行,当<input>中输入时,变量obj1和<div>没有任何响应,但当我们隐藏/展示后,obj1和<div>都能正确显示——因为受js的限制,Vue.js 不能检测到对象属性的添加或删除,因为 Vue.js 在初始化实例时将属性转为 getter/setter,所以属性必须在 data 对象上才能让 Vue.js 转换它,才能让它是响应的(详细原理可以参看下方相关资料)。
可以修改computed的set来达到预期效果: