const vm = new Vue(
options
)
继续学习Vue构造选项options
options按重要程度分为五大类
重点学习入门属性 和 进阶属性。
二、进阶属性
1、computed-计算属性
new Vue({
data: {
user: {}
},
computed: {
displayName: {
get() {},
set(value) {}
}
},
template: `
<div>
{{displayName}}
<div>
{{displayName}}
<button @click="add">set</button>
</div>
</div>
`,
methods: {
add() {}
}
}).$mount("#app");
2、watch-侦听-撤销功能
回顾知识点1,异步是什么?
异步的特点是:我不等你反馈结果我就要往后走了。
异步示例代码
new Vue({
data:{}
watch: {}
template: ``
methods:{
undo(){
const last = this.history.pop();
//把当前history[]的最后一个{from: to:}值截取出来,赋值给last
const old = last.from;
//last{}里面的from值定义给old
this.inUndoMode = true;
this.n = old;
//把old值赋值给新的n
// watch n 的函数会异步调用
this.inUndoMode = false;
}
}
}).$mount("#app");
异步即代码执行的顺序问题,比如上述undo(){}里面的
this.inUndoMode = true;
this.n = old;
this.inUndoMode = false;
要先执行完undo(){},再去执行watch: {}。这就是异步。
而不是执行完
this.inUndoMode = true;
this.n = old;
再去执行watch: {},最后执行this.inUndoMode = false;
这样的话,因为异步原理,
this.inUndoMode = true;
this.n = old;
this.inUndoMode = false;
回撤模式的变化没有任何意义。需要对this.inUndoMode = false;也做一个异步。
撤销功能示例代码
new Vue({
data: {
n: 0,//展示数字
history: [],//记录操作和撤销历史
inUndoMode: false//默认撤销模式为false(undo是撤销的意思)
},
watch: {
//实时监听n的变化,watch是完美实现历史功能的函数/方法
n: function(newValue, oldValue) {
//es6语法": function"可以省略掉。
//newValue最新的值,oldValue之前的值
console.log(`在不在撤销模式:${this.inUndoMode ? '在' : '不在'}`);
//问号冒号表达式'a > b ? a : b;',如果'this.inUndoMode'为真,打出'在',如果为假,打出'不在'
//学会用console.log来debugger
if (!this.inUndoMode) {
//如果当前在撤销模式,就执行下一句push()
this.history.push({ from: oldValue, to: newValue });
//用push()把n的新值和旧值记录到this.history
}
//省略了else(this.inUndoMode){什么也不做}
//如果不在撤销模式,就不执行push()
}
},
// 不如用 computed 来计算 displayName
template: `
<div>
{{n}}
<hr />
<button @click="add1">+1</button>
<button @click="add2">+2</button>
<button @click="minus1">-1</button>
<button @click="minus2">-2</button>
<hr/>
<button @click="undo">撤销</button>
<hr/>
{{history}}
</div>
`,
methods: {
add1() {
this.n += 1;
},
add2() {
this.n += 2;
},
minus1() {
this.n -= 1;
},
minus2() {
this.n -= 2;
},
undo() {//撤销函数
const last = this.history.pop();
//把当前history[]的最后一个{from: to:}值截取出来,赋值给last
this.inUndoMode = true;
console.log("ha" + this.inUndoMode);
const old = last.from;
//last{}里面的from值定义给old
this.n = old;
//把old值赋值给新的n
// watch n 的函数会异步调用
//即在这一行立即触发watch的异步操作
//watch所使用的也是this.$nextTick(() => {watch},0);
this.$nextTick(() => {
this.inUndoMode = false;
},0);
//用nextTick()把'this.inUndoMode=false'也设置为异步操作
//这样watch的异步操作一定是优先于'this.inUndoMode = false'的异步操作的.
}
}
}).$mount("#app");
watch:{}和this.inUndoMode = false都使用同级别的异步操作,那么watch:{}一定比this.inUndoMode = false优先执行。
回顾知识点2pop()
pop()方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。 developer.mozilla.org/zh-CN/docs/…
回顾知识点3,问号冒号表达式
a > b ? a : b;
3、watch-侦听-模拟computed
watch可以模拟计算属性
new Vue({
data: {
user: {
email: "fangyinghang@qq.com",
nickname: "方方",
phone: "13812312312"
}
},
watch: {
"user.email"(){
handler(){
const {user: {email, nickname, phone}} = this;
//从this里面解析出user,从user里面解析出这三个变量。
this.displayName = nickname||email||phone
},
immediate: true
},
"user.nickname"(){
handler(){
const {user: {email, nickname, phone}} = this;
this.displayName = nickname||email||phone
},
immediate: true
},
"user.phone"(){
handler(){
const {user: {email, nickname, phone}} = this;
this.displayName = nickname||email||phone
},
immediate: true
},
// DRY don't repeat yourself
// 不如用 computed 来计算 displayName
template: `
<div>
{{displayName}}
<div>
{{displayName}}
<button @click="add">set</button>
</div>
</div>
`,
methods: {
add() {
console.log("add");
this.displayName = "圆圆";
}
}
}).$mount("#app");
- 可以看出用watch模拟computed是非常低效的。
- 因此,如果遇到既可以用watch又可以用computed做的需求,优先使用computed。
- 他们都是在数据变化的时候去执行一个函数
- computed主要着重于依赖之间的变化以及缓存,得出一个结果。
- watch主要着重于
数据变化
的时候去执行什么函数,记录过程。而不是结果。
4、对Vue的所有监听器(比如watch)来说什么叫做数据变化?如何使用deep:true来监听所有数据变化?
回顾内存图知识
obj: {
a: "a"
}
obj作为一个属性,存的不是对象{ a: "a"},而是这个对象的地址。
如果执行obj.a += 'hi',obj.a变为了'ahi',也就是obj.a变了,但是obj是没有变的,因为obj的所存的{}的地址没变。
如果执行obj = {a:'a'},obj.a没变,因为a的值还是'a',但是obj变了,因为上一个{a: "a"}和下面的{a:'a'}地址不一样,下面的{a:'a'}是重新复制的一份,存在另外的地址。obj存的对象的地址变了。
简单类型变没变,看值:比如obj.a
复杂类型(对象)变没变,看地址:比如obj:{}
由于上述原理,需要引入deep:true来监听所有数据变化
浅比较
watch: {
obj() {
console.log("obj 变了");
}
//默认是一个浅比较,只有obj地址变了,才认为它变了。
}
}).$mount("#app");
深比较引入deep:true
data: {
obj: {
a: "a"
}
},
watch: {
obj: {
handler(){
console.log("obj 变了");
}
deep: true//往obj里面深处看,不但监听第一层的地址,还监听第二层-第n层的数据变化。
//deep: false//如果不需要监听第二层-第n层,写'deep: false'
}
}
}).$mount("#app");
5、watch的语法
语法1
watch:{
o1:不能使用箭头函数,箭头函数里面的this是全局对象
o2:function(value, oldValue){},//两个参数(value, oldValue)是vue传给function的
o3(){},//省略':function'
o4:[f1,f2],//两个函数f1 , f2,在o4变化的时候,依次执行f1 , f2
o5:'methodName',//去methods{}里面找对应的名字的函数
o6:{handler:fn, deep:true, immediate:true}
//'deep:true'表示是否往深了看
//'immediate:true'表示第一次运行的时候,就render这个函数
o7:"object.a": function(){}//字符串"object.a"作为key,function(){}为函数
}
watch文档 cn.vuejs.org/v2/api/#wat…
语法2
vm.$watch("xxx", fn, {deep:..., immediate:...})//"xxx"可以改为一个返回字符串的函数
语法2代码示例
new Vue({
data:{
n:0
}
created(){
this.$watch("n",function(){
console.log("n 变2了")
},{immediate:true})
//'immediate:true'表示第一次运行的时候,就render这个函数
},
})
面试:简述computed和watch的区别
1、computed就是计算属性的意思,watch就是监听的意思
2、各自描述
computed
- computed是用来计算出一个值的,这个值在调用的时候不需要加括号。
- 这个值根据依赖会自动缓存,即如果依赖不变,computed值就不会再重新计算。
new Vue({
computed: {
displayName: {
get() {},
set(value) {}
}
},
})
watch
- immediate,表示在第一次渲染的时候是否要执行这个函数
- deep,如果监听一个对象,我是否要看这个对象里面的(第二层到最后一层)属性的变化
- 定义:如果某个属性变化了,我就去执行一个函数。
总结
1、如果一个数据依赖于其他数据,那么就使用computed设计这个数据。
2、如果需要在某个数据变化时做一些事情,使用watch来观察这个数据的变化。
关于这个问题可参见:juejin.cn/post/684490…