Vue 的模板内的表达式非常便利,但是万物应有度。在模板中放入太多的逻辑会使代码过于繁重和难以维护。例如:
<h1>
{{title.split('').reverse().join('')}}
</h1>
在这个地方 这个表达式可能就不再是简单的声明式逻辑,我们必须要看一段时间,才能意识到这里是想要显示title值得翻转字符串,如果在代码中大量的引用title的翻转字符串时就会更为难以处理。换言之,如果我们更改下这个表达式,翻转后再次去掉某个字符,页面所有引用该翻转字符串的地方都要重复的进行表达式的更改
所以,对于任何的对对象原属性(值)的重复或复杂逻辑,二次/多次 处理后进行引用的属性 都应该统一封装,统一声明引用 而在VUE内 我们可以视情况而定 选择 计算属性、侦听器、自定义函数 三种方式进行处理封装。下面具体介绍这三种方式处理数据的方式和引用方式
计算属性
计算属性处理数据定义在
computed
内 ,在模板内使用和使用data内的属性方式一致,可以视为 data的一个包装属性吧
代码示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body >
<div id="person">
<label>原名称:</label>
<input v-model="person.name" />
<label>翻转名称:</label>
<input v-model="reversePersonName" />
<label>性别:</label>
<input v-model="person.sex" />
<label>年龄:</label>
<input v-model="person.age" />
</div>
</body>
<script>
new Vue({
el:"#person",
data:{
person:{
name:'Tom',
sex:'男',
age:18
}
},
computed:{
reversePersonName:function(){
return this.person.name.split('').reverse().join('')
}
}
})
</script>
</html>
以计算属性的方式对数据进行处理,即第一次渲染该计算属性时,对该数据进行处理后会基于它们的响应式依赖进行缓存,只在相关响应式依赖发生改变后才会进行重新求值。这就意味着 如果name
的值没有发生改变 ,多次对reversePersonName
进行访问都会立即返回之前的计算结果,而不会进行重复的进行计算。
而当我们对 原名称 person.name
属性的值进行改变时reversePersonName
这个计算属性也会重新执行函数 对数据进行修改并更新缓存 重新渲染数据
但是 官方文档也有提到 计算属性并不支持异步操作比如 调用一个异步的API(AJAX)
下面我们模仿下异步操作 代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body >
<div id="person">
<label>原名称:</label>
<input v-model="person.name" />
<label>翻转名称:</label>
<input v-model="reversePersonName" />
<label>性别:</label>
<input v-model="person.sex" />
<label>年龄:</label>
<input v-model="person.age" />
</div>
</body>
<script>
new Vue({
el:"#person",
data:{
person:{
name:'Tom',
sex:'男',
age:18
}
},
computed:{
reversePersonName:function(){
var value=this.person.name.split('').reverse().join('')
console.log('计算属性监听到Name发生变化:'+value)
// 模仿异步请求接口调用 假设该请求需要1000毫秒才能返回数据
setTimeout(function () {
return value
}, 1000);
}
}
})
</script>
</html>
代码执行渲染后发现 计算属性已经无法正常渲染数据 甚至初始化渲染时 都无法正常渲染数据
image-20190531232246827.png侦听器(侦听属性)
侦听器/侦听属性 顾名思义
watch
: 这个东西就是一个类似html里的onchange
事件的东西,不同的是onchange是用来监听html元素/值的变化 ,watch是用来监听vue实例里的属性的变化的
不同的是侦听器,只是一个用来监听数据变化的并不能直接在模板直接引用或调用
使用侦听器想要达到和计算属性同样的效果,必须要增加一个属性来存储进行计算后的结果 随后进行渲染
代码示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body >
<div id="person">
<label>原名称:</label>
<input v-model="name" />
<label>翻转名称:</label>
<input v-model="reversePersonName" />
<label>性别:</label>
<input v-model="sex" />
<label>年龄:</label>
<input v-model="age" />
</div>
</body>
<script>
var alt = new Vue({
el:"#person",
data:{
name:'Tom',
sex:'男',
age:18,
reversePersonName:'moT'
},
watch:{
name:function(val){
this.reversePersonName=val.split('').reverse().join('')
}
}
})
</script>
</html>
运行结果:
image-20190531225105857.png
这段代码运行渲染后,结果看起来很正常并且对原名称进行修改 翻转名称也会随之改变 对不对!但是! 还没完!
当我们把这些属性包装到一个对象内:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body >
<div id="person">
<label>原名称:</label>
<input v-model="person.name" />
<label>翻转名称:</label>
<input v-model="person.reversePersonName"/>
<label>性别:</label>a
<input v-model="person.sex" />
<label>年龄:</label>
<input v-model="person.age" />
</div>
</body>
<script>
var alt = new Vue({
el:"#person",
data:{
person:{
name:'Tom',
sex:'男',
age:18,
reversePersonName:'moT'
}
},
watch:{
person:function(val){
console.log(val)
this.person.reversePersonName=val.name.split('').reverse().join('')
}
}
})
</script>
</html>
这个时候渲染结果看起来还是很正常,但是!当你对person的name进行重新赋值或者直接改变input值时,你会发现。 翻转名称的值竟然没有同步更新!!!
image-20190531225357661.png
后来查了官方给的文档和部分网上资料,发现了原因
侦听器默认情况下必须要对侦听的属性 进行更改时才会正常更新缓存 重新渲染数据,因为它默认是没开启深度监听的,这种情况下 不会监听对象内部的属性变化,数组是可以的
侦听器可以定义三个参数
- 第一个 handler:其值是一个回调函数。即监听到变化时应该执行的函数。
- 第二个 deep:其值是true或false;确认是否深入监听。(一般监听时是不能监听到对象属性值的变化的,数组的值变化可以听到。)
- 第三个 immediate:其值是true或false;确认是否以当前的初始值执行handler的函数
所以我们侦听器可以换成这种写法:
watch:{
person:{
handler:function(newVal,oldVal){
this.person.reversePersonName=val.name.split('').reverse().join('')
},
deep:true,
immediate:true
}
}
并且侦听器是支持异步操作的(执行一个异步API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
模仿异步API访问 代码示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body >
<div id="person">
<label>原名称:</label>
<input v-model="person.name" />
<label>翻转名称:</label>
<input v-model="person.reversePersonName"/>
<label>性别:</label>a
<input v-model="person.sex" />
<label>年龄:</label>
<input v-model="person.age" />
</div>
</body>
<script>
var alt = new Vue({
el:"#person",
data:{
person:{
name:'Tom',
sex:'男',
age:18,
reversePersonName:'moT'
}
},
watch:{
person:{
handler:function(newval,oldval){
this.person.reversePersonName='计算中:模拟API访问...'
setTimeout(function () {
this.reversePersonName=newval.name.split('').reverse().join('')
}, 0)
},
deep:true,
immediate:true
}
}
})
</script>
</html>
自定义函数
自定义函数处理数据方式如下,我们在模板内 调用函数来获取并渲染数据
使用自定义函数来处理并渲染数据是不会使用缓存的,即:无论原属性的值 有没有发生变化 每次在模板内引用该函数,都会重新计算并渲染数据,这样在大量的数据操作时对网页渲染性能造成的影响降是灾难级的,慎用。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body >
<div id="person">
<label>名称:{{reversePersonName()}}</label>
<label>性别:</label>
<input v-model="person.sex" />
<label>年龄:</label>
<input v-model="person.age" />
</div>
</body>
<script>
new Vue({
el:"#person",
data:{
person:{
name:'Tom',
sex:'男',
age:18
}
},
methods:{
reversePersonName:function(){
return this.person.name.split('').reverse().join('');
}
}
})
</script>
</html>
网友评论