现在有一段代码,在Vue中定义了firstName和lastName,现在需要在屏幕上显示全名fullName
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<title>Document</title>
</head>
<body>
<div id="app">hello</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: "Dell",
lastName: "Smith"
}
})
</script>
</body>
</html>
使用插值表达式
首先想到的应该是写一个插值表达式:
<body>
<div id="app">{{firstName+" "+lastName}}</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: "Dell",
lastName: "Smith"
}
})
</script>
</body>
很容易就完成了。使用插值表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中多包含此处的翻转字符串时,就会更加难以处理。
使用方法
既然使用插值表达式不是一个好的选择。那可以选择使用方法来完成,也很简单。直接定义一个fullName函数,在插值表达式中调用它即可:
<body>
<div id="app">{{fullName()}}</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: "Dell",
lastName: "Smith"
},
methods: {
fullName: function() {
return this.firstName+" "+this.lastName
}
}
})
</script>
</body>
这样做很好,但是我们还有更优的方法。
计算属性
我们可以使用计算属性来解决上面的问题:
<body>
<div id="app">{{fullName}}</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: "Dell",
lastName: "Smith"
},
computed: {
fullName: function() {
return firstName+" "+lastName
}
}
})
</script>
</body>
从上面的写法可以看到,和方法对比,计算属性只是在computed里面定义,而方法是在methods中定义。在div中展示时,方法后面要加(),表示调用此方法,而计算属性不用。除这些外,再没有其他区别。 但是相比于方法,计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。比如我们可以在上面的代码中添加另一个属性age,并在app中展示它:
<body>
<div id="app">{{fullName+" "+age}}</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: "Dell",
lastName: "Smith",
age: 30
},
computed: {
fullName: function() {
return this.firstName+" "+this.lastName
}
}
})
</script>
</body>
页面会展示出Dell Smith 30,如果现在将age更改,页面重新渲染时只会改变age的值,前面的fullName没有发生改变,多次访问 fullName计算属性会立即返回之前的计算结果,而不必再次执行函数。
<body>
<div id="app">{{fullName()}}</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: "Dell",
lastName: "Smith",
age: 30
},
methods: {
fullName: function() {
return this.firstName+" "+this.lastName+" "+this.age
}
}
})
</script>
</body>
而方法则不会这样,它没有缓存可以依赖,每当触发重新渲染时,调用方法将总会再次执行函数。也就是age更新一次,整个fullName都要重新计算一次。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
侦听器
除了计算属性,我们也可以用侦听器来完成:
<body>
<div id="app">{{fullName}}</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstName: "Dell",
lastName: "Smith",
fullName: "Dell Smith"
},
watch: {
firstName: function(){
this.fullName = this.firstName+" "+this.lastName
},
lastName: function(){
this.fullName = this.firstName+" "+this.lastName
}
}
})
</script>
</body>
上面代码是命令式且重复的。将它与计算属性的版本进行比较,很明显计算属性更好。
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。例如:
<body>
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
// 请参考:https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
</body>
在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的.