我们知道,在模板中可以直接通过插值语法显示一些data中的数据。
但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示;
我们有没有什么方法可以将逻辑抽离出去呢?
什么是计算属性呢?
那接下来我们通过案例来理解一下这个计算属性。
计算属性的用法:
我们来看三个案例:
我们可以有三种实现思路:
思路一的实现:模板语法
{{ firstName + lastName }}
{{ score >= 60 ? "及格": "不及格" }}
{{ message.split("").reverse().join(" ") }}
思路二的实现:method实现
{{ getFullName()}}
{{ getResult() }}
{{ getReverseMessage() }}
思路三的实现:computed实现
{{ fullName }}
{{ result }}
{{ reverseMessage }}
在上面的实现思路中,我们会发现计算属性和methods的实现看起来是差别是不大的,而且我们多次提到计算属性有缓存。
接下来我们来看一下同一个计算多次使用,计算属性和methods的差异:
{{getResult()}}
{{getResult()}}
{{getResult()}}
{{result}}
{{result}}
{{result}}
打印结果如下:
计算属性和methods对比
这是什么原因呢?
我们来看一下当分数变化时,方法和计算属性的反应:
score发生变化
计算属性在大多数情况下,只需要一个getter方法即可,所以我们会将计算属性直接写成一个函数。
但是,如果我们确实想设置计算属性的值呢?
{{fullName}}
你可能觉得很奇怪,Vue内部是如何对我们传入的是一个getter,还是说是一个包含setter和getter的对象进行处理的呢?
Vue源码内部的逻辑判断
侦听器的用法如下:
什么是侦听器呢?
举个栗子(例子):
我们先来看一个例子:
这是因为默认情况下,watch只是在侦听info的引用变化,对于内部属性的变化是不会做出相应的:
watch:
{
info:{
handler(newValue, oldValue){
console.log(newValue, oldValue);
},
deep: true
}
}
还有另外一个属性,是希望一开始的就会立即执行一次:
watch: {
info: {
handler(newValue, oldValue) {
console.log(newValue, oldValue);
},
deep: true,
immediate: true
}
}
我们直接来看官方文档的案例:
const app = Vue.createApp({
data() {
return {
a: 1,
b: 2,
c: { d: 4},
e: 'test',
f: 5
}
},
watch: {
a(val, oldVal){
console.log(`new: ${val}, old: ${oldVal}`)
},
// 字符串方法名
b: 'someMethod',
// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
c: {
handler(val, oldVal) {
console.log('c changed')
},
deep: true
},
// 该回调将会在侦听开始之后被立即调用
e: {
handler(val, oldVal) {
console.log('e changed')
},
immediate: true
},
// 你可以传入回调数组,它们会被逐一调用
f: [
'handle1',
function handle2(val, oldVal) {
console.log('handle2 triggered')
},
{
handler: function handle3(val, oldVal) {
console.log('handle3 triggered')
}
}
]
},
methods: {
someMethod(){
console.log('b changed')
},
handle1() {
console.log('handle 1 triggered')
}
}
})
另外一个是Vue3文档中没有提到的,但是Vue2文档中有提到的是侦听对象的属性:
'info.name': function(newValue, oldValue) {
console.log(newValue, oldValue);
}
还有另外一种方式就是使用 $watch 的API:
created() {
this.$watch('message', (newValue, oldValue) => {
console.log(newValue, oldValue);
},
{
deep: true,
immediate: true
}
)
}
阶段案例
现在我们来做一个相对综合一点的练习:书籍购物车
案例效果如下:
案例效果
案例说明:
我们先来搭建一下模板引擎:
在这里我们有一些数据和方法需要在组件对象(createApp传入的对象)中定义:
序号
书籍名称
出版日期
价格
购买数量
操作
{{index+1}}
{{book.name}}
{{book.date}}
{{formatPrice(book.price)}}
{{book.count}}
总价格:{{formatPrice(totalPrice)}}
为了让表格好看一点,我们来实现一些css的样式:
table{
border:1px solid #e9e9e9;
border-collapse:collapse;
border-spacing:0;
}
th,td{
padding:8px 16px;
border:1px solid #e9e9e9;
text-align:left;
}
th {
background-color: #f7f7f7;
color:#5c6b77;
font-weight:600;
}
span{
margin: 0 3px;
}
接下来我们来看一下代码逻辑的实现:
Vue.createApp({
template: "#my-app",
data () {
return {
books: [
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count:1
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count:1
},
{
id: 3,
name: '《编程珠玑》',
date: '2008-10',
price: 39.00,
count:1
},
{
id: 4,
name: '《代码大全》',
date: '2006-3',
price: 128.00,
count:1
},
]
}
},
computed: {
totalPrice () {
let finalPrice = 0;
for (let book of this.books){
finalPrice += book.count * book.price;
}
return finalPrice;
}
},
methods: {
decrement (index)
{
//获取索引值进行计算
this.books[index].count--;
},
increment (index)
{
//获取索引值进行计算
this.books[index].count++;
},
remove (index)
{
this.books.splice(index,1)
},
formatPrice (price) {
return "¥" + price;
}
}
}).mount("#app");
这里我们补充一个小的细节,当我们的数量减到1的时候就不能再减了:
image-20210519100522173
这里我们可以对button进行逻辑判断:
表单输入
表单提交是开发中非常常见的功能,也是和用户交互的重要手段:
这些都要求我们可以在代码逻辑中获取到用户提交的数据,我们通常会使用v-model指令来完成:
{{message}}
实现效果如下
官方有说到,v-model的原理其实是背后有两个操作:
v-model的原理
事实上v-model的原理会比上面的更加复杂:
v-model的编译过程
我们再来绑定一下其他的表单类型:textarea、checkbox、radio、select
article当前的值是: {{article}}
isAgree当前的值是: {{isAgree}}
hobbies当前的值是: {{hobbies}}
gender当前的值是: {{gender}}
fruit当前的值是: {{fruit}}
目前我们在前面的案例中大部分的值都是在template中固定好的:
在真实开发中,我们的数据可能是来自服务器的,那么我们就可以先将值请求下来,绑定到data返回的对象中,再通过v-bind来进行值的绑定,这个过程就是值绑定。
这里不再给出具体的做法,因为还是v-bind的使用过程。
v-model在使用的时候可以在后面跟一些修饰符,来完成一些特殊的操作。
lazy修饰符是什么作用呢?
{{message}}
我们先来看一下v-model绑定后的值是什么类型的:
{{message}}
如果我们希望转换为数字类型,那么可以使用 .number 修饰符:
另外,在我们进行逻辑判断时,如果是一个string类型,在可以转化的情况下会进行隐式转换的:
const score = "100";
if (score > 90) {
console.log("优秀");
}
如果要自动过滤用户输入的首尾空白字符,可以给v-model添加 trim 修饰符:
v-model也可以使用在组件上,Vue2版本和Vue3版本有一些区别,具体的使用方法,后面讲组件化开发再具体学习。