1.计算属性
计算属性例子:
<template>
<div>
<p>消息内容:{{message}}<br>转换后的内容{{reMessage}}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: "Hello World"
};
},
computed: {
reMessage: function() {
return this.message.split("").reverse().join("");
}
}
};
</script>
显示结果:
注释:上述例子中,我们声明了一个计算属性reMessage,我们提供的函数将用作属性reMessage的getter函数,reMessage的值变化始终取决于message的值,当message发生变化时,所有依赖reMessage的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。
注:计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。
计算属性的setter
计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter :
如下例子:
<template>
<div>
我的firstname是:{{firstName}}<br>
我的lastname是:{{lastName}}<br>
<!--我的全名是:{{fullName}}-->
我的全名是:{{fullName='jack lisa'}}
</div>
</template>
<script>
export default {
data() {
return {
firstName:'Jone',
lastName:"coco"
};
},
computed: {
fullName:{
get:function(){
return this.firstName+this.lastName;
},
set:function(v){
var names=v.split(' ');
this.firstName=names[0];
this.lastName=names[names.length-1];
}
}
}
};
</script>
上述例子,输出结果为:
上述例子中如果给fullName赋值,则会调用setter,如果不赋值则会调用getter,同时,他们的firstName和lastName也会被响应更新。
2.侦听器
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
如下例子:
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<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>
在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
如下简单的例子:
<template>
<div class="main">
输入内容:<input type="text" v-model="question">
<p>
<span>旧值:{{myolddata}}</span>
<span>新值:{{mynewdata}}</span>
</p>
</div>
</template>
<script>
export default {
data() {
return {
question: "",
myolddata: "",
mynewdata: ""
};
},
watch: {
//watch语法,第一个参数表示新值,第二个参数默认表示旧值,即记录上一次的值
question: function(newVal, oldVal) {
this.myolddata = oldVal;
this.mynewdata = newVal;
}
}
};
</script>
新值和旧值,两个值始终监听文本框值的变化。
例子2:
<template>
<div class="main">
输入内容:<input type="text" v-model="cityName">
</div>
</template>
<script>
export default {
data() {
return {
cityName: "BeiJing"
};
},
watch: {
cityName(newVal,oldVal)
{
console.log(`新值是${newVal},旧值是:${oldVal}`)
}
}
};
</script>
每当cityName的值发生变化的时候,就会执行watch中的cityName中的动作。
也可以直接写一个监听处理函数,每当监听到cityName值发送改变时,执行函数,也可以在所监听的数据后面直接加字符串形式的方法名:
watch:{
cityName:'nameChange'//值可以为methods的方法名
}
watch中immediate和handler用法
我们在使用watch的时候有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。
watch只有当值发生改变时才会执行,如果初始就需要执行,可以添加属性immediate:true。
如果是对象键值变化进行监听时,需要使用deep:true。深度监听。
比如当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也需要执行函数,此时就需要将immediate设为true。
new Vue({
el: '#root',
data: {
cityName: ''
},
watch: {
cityName: {
handler(newName, oldName) {
// ...
},
immediate: true
}
}
})
监听的数据后面写成对象形式,包含handler方法和immediate,之前我们写的函数其实就是在写这个handler方法;
immediate表示在watch中首次绑定的时候,是否执行handler,值为true则表示在watch中声明的时候,就立即执行handler方法,值为false,则和一般使用watch一样,在数据发生变化的时候才执行handler。
watch中的deep用法
当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听。
<input type="text" v-model="cityName.name"/>
new Vue({
el: '#root',
data: {
cityName: {id: 1, name: 'shanghai'}
},
watch: {
cityName: {
handler(newName, oldName) {
// ...
},
deep: true,
immediate: true
}
}
})
设置deep: true 则可以监听到cityName.name的变化,此时会给cityName的所有属性都加上这个监听器,当对象属性较多时,每个属性值的变化都会执行handler。如果只需要监听对象中的一个属性值,则可以做以下优化:使用字符串的形式监听对象属性:
watch: {
'cityName.name': {
handler(newName, oldName) {
// ...
},
deep: true,
immediate: true
}
}
这样只会给对象的某个特定的属性加监听器。
数组(一维、多维)的变化不需要通过深度监听,对象数组中对象的属性变化则需要deep深度监听。
例子3:
<template>
<div class="main">
<!--观察数据为字符串或数组-->
例子1:
<input type="text" v-model="example01">
例子2:
<input type="text" v-model="example02">
<!--example03是一个对象,当单观察数据为对象时,如果键值发生变化,为了监听到数据变化,需要添加deep:true参数-->
例子3:
<input type="text" v-model="example03.name">
</div>
</template>
<script>
export default {
data() {
return {
example01: "",
example02: "",
example03: {
name: "lisa",
age: 12
}
};
},
watch: {
example01(newVal, oldVal) {
console.log(`新值${newVal},旧值${oldVal}`);
},
example02: "myfun",//值可以是methods的方法名
'example03.name': {
//注意:当观察的数据为对象或数组时,curVal和oldVal是相等的,因为这两个形参指向的是同一个数据对象
handler(newV, oldV) {
console.log(`新值${newV},旧值${oldV}`);
},
deep: true
}
},
methods: {
myfun(newVV, oldVV) {
console.log(`新值${newVV},旧值${oldVV}`);
}
}
};
</script>
注:对应一个对象,键是观察表达式,值是对应回调。值也可以是方法名,或者是对象,包含选项。在实例化时为每个键调用 $watch() ;
deep深度监听
deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改obj里面任何一个属性都会触发这个监听器里的 handler。
优化,我们可以是使用字符串形式监听。
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
// deep: true
}
}```
这样Vue.js才会一层一层解析下去,直到遇到属性a,然后才给a设置监听函数。