【Vue】基础系列(三)计算属性与监视属性
数据代理
在Vue.js中,数据代理是Vue实现响应式数据的核心机制之一。它通过使用JavaScript的Object.defineProperty(在Vue 3中则是Proxy)来拦截对对象属性的访问和修改,从而实现数据和视图的双向绑定。这使得开发者可以更方便地使用简洁的语法来访问和操作数据,而Vue会自动更新相应的视图。
数据代理的概念
数据代理的核心是将组件的data对象中的属性代理到Vue实例上。这意味着你可以通过this.propertyName直接访问和修改data中的属性,而Vue会自动更新视图。
数据代理的实现原理
在Vue 2中,数据代理是通过Object.defineProperty实现的。Vue在实例化时,会遍历data对象中的所有属性,并为每个属性设置getter和setter。当你访问或修改这些属性时,Vue会自动触发相应的DOM更新。
数据代理的使用
示例代码
<!DOCTYPE html>
<html>
<head>
<title>Vue Data Proxy Example</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<p>Message: {{ message }}</p>
<input v-model="message" placeholder="Edit message">
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
});
</script>
</body>
</html>
解释:在这个示例中,message 是定义在 data 对象中的属性。通过Vue的数据代理机制,你可以直接通过 {{ message }} 访问它,或者通过 v-model 绑定到输入框上进行修改。
Vue会自动代理data中的message属性,使得你可以通过this.message来访问它,并在视图中使用{{ message }}绑定进行显示。
数据代理的工作流程
当你在模板中引用{{ message }}时,Vue实际上是访问了this.message,并通过数据代理机制去data对象中获取对应的值。
当你在输入框中输入新内容时,v-model指令会触发message属性的setter,Vue捕捉到这个变化后,会重新渲染与message相关的部分视图。
数据代理的好处
简洁性:你可以通过简洁的语法(例如this.message)直接访问数据,而不需要每次都通过
data对象。响应性:数据代理使得数据与视图之间的双向绑定更加直观和简单,Vue会自动处理数据变化引起的视图更新。
可维护性:通过数据代理,你可以将数据与视图的逻辑清晰地分离,代码更易于维护和理解。
计算属性与监视属性
在Vue.js中,计算属性(computed properties)和监视属性(watchers)是两种常用的响应式特性,用于在数据发生变化时自动更新页面的内容。它们虽然在功能上有相似之处,但适用于不同的场景。
计算属性(Computed Properties)
功能
计算属性是基于已存在的响应式数据计算出一个新值的属性。它会根据依赖的数据自动更新,并且有缓存机制,只有当依赖的数据发生变化时才会重新计算。
计算属性适合用于基于其他数据动态计算出新值的场景,比如对数据进行格式化、合并、筛选等操作。
特点
缓存:计算属性的结果会被缓存,直到它的依赖发生变化。这使得它在性能上比直接在模板中使用复杂表达式更优。
简洁:计算属性通常以getter和setter形式定义,getter用于获取计算值,setter用于响应用户的输入或其他操作。
插值语法示例代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_插值语法实现</title>
<!– 引入Vue –>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!– 准备好一个容器–>
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{firstName}}-{{lastName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
}
})
</script>
</html>
methods实现示例代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_methods实现</title>
<!– 引入Vue –>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!– 准备好一个容器–>
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{fullName()}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
methods: {
fullName(){
console.log('@---fullName')
return this.firstName + '-' + this.lastName
}
},
})
</script>
</html>
计算属性实现示例代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_计算属性实现</title>
<!– 引入Vue –>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!– 准备好一个容器–>
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
测试:<input type="text" v-model="x"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
<!– 全名:<span>{{fullName}}</span> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
全名:<span>{{fullName}}</span> –>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
x:'你好'
},
methods: {
demo(){
}
},
computed:{
fullName:{
//get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
//get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
get(){
console.log('get被调用了')
// console.log(this) //此处的this是vm
return this.firstName + '-' + this.lastName
},
//set什么时候调用? 当fullName被修改时。
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
</script>
</html>
解释:fullName 是一个计算属性,它通过 firstName 和 lastName 动态计算得出。如果用户输入一个新的全名,它也会自动更新 firstName 和 lastName。
计算属性实现简写
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_计算属性实现</title>
<!– 引入Vue –>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!– 准备好一个容器–>
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
},
computed:{
//完整写法
/* fullName:{
get(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
},
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
} */
//简写
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
}
})
</script>
</html>
原理
底层借助了Objcet.defineproperty方法提供的getter和setter。
get函数什么时候执行?
初次读取时会执行一次。
当依赖的数据发生改变时会被再次调用。
优势
与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
计算属性最终会出现在vm上,直接读取使用即可。
如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
监视属性(Watchers)
功能
监视属性用于监听某个数据属性的变化,并在其变化时执行回调函数。适合用于执行异步操作或在数据变化时需要执行复杂逻辑的场景。
特点
灵活性:可以监听单个数据属性的变化,也可以监听多个数据属性,甚至可以使用深度监听(deep)来监控对象内部的变化。
无缓存:与计算属性不同,监视属性没有缓存机制,每次数据变化都会触发回调。
语法:
new Vue时传入watch配置
通过vm.$watch监视
天气案例示例代码(计算属性)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例</title>
<!– 引入Vue –>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!– 准备好一个容器–>
<div id="root">
<h2>今天天气很{{info}}</h2>
<!– 绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句 –>
<!– <button @click="isHot = !isHot">切换天气</button> –>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
})
</script>
</html>
天气案例示例代码(监视属性)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_监视属性</title>
<!– 引入Vue –>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!–
监视属性watch:
1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作
2.监视的属性必须存在,才能进行监视!!
3.监视的两种写法:
(1).new Vue时传入watch配置
(2).通过vm.$watch监视
–>
<!– 准备好一个容器–>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
/* watch:{
isHot:{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
} */
})
vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
</script>
</html>
深度监视
Vue中的watch默认不监测对象内部值的改变(一层)。
配置deep:true可以监测对象内部值改变(多层)。
备注:(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!(2).使用watch时根据数据的具体结构,决定是否采用深度监视。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_深度监视</title>
<!– 引入Vue –>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!– 准备好一个容器–>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
<hr/>
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a+1</button>
<h3>b的值是:{{numbers.b}}</h3>
<button @click="numbers.b++">点我让b+1</button>
<button @click="numbers = {a:666,b:888}">彻底替换掉numbers</button>
{{numbers.c.d.e}}
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1,
c:{
d:{
e:100
}
}
}
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
isHot:{
// immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
},
//监视多级结构中某个属性的变化
/* 'numbers.a':{
handler(){
console.log('a被改变了')
}
} */
//监视多级结构中所有属性的变化
numbers:{
deep:true,
handler(){
console.log('numbers改变了')
}
}
}
})
</script>
</html>
监视属性简写
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_监视属性_简写</title>
<!– 引入Vue –>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!– 准备好一个容器–>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
//正常写法
/* isHot:{
// immediate:true, //初始化时让handler调用一下
// deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}, */
//简写
/* isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
} */
}
})
//正常写法
/* vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}) */
//简写
/* vm.$watch('isHot',(newValue,oldValue)=>{
console.log('isHot被修改了',newValue,oldValue,this)
}) */
</script>
</html>
计算属性 vs 监视属性
1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象
计算属性变量在computed中定义,监视属性监听的是已经在 data 中定义的变量, 当该变量变化时,会触发 watch 中的方法.
computed 具有缓存功能,可以监听对象某个具体属性。watch可以进行深度监听,监听对象的变化
计算属性是声明式的描述一个值依赖了其他值,依赖的值改变后重新计算结果更新DOM。监视属性的是定义的变量,当定义的值发生变化时,执行相对应的函数
