computed 计算属性
定义:计算属性就是根据其他属性被计算出来的值!
语法
computed :{
[key: string]: Function | { get: Function, set: Function }
}
是个对象,键名是字符串,键值可以是
①仅读取:函数形式,返回计算出的值(也就是getter)
②读取和设置:对象形式,getter返回计算出来的值,setter修改
缓存
- 计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。注意,如果某个依赖 (比如非响应式属性) 在该实例范畴之外,则计算属性是不会被更新的。
- 如果依赖的属性没有变化,就不会重新计算
- 但是getter / setter默认不会做缓存,Vue做了特殊处理
var vm = new Vue({
data: { a: 1 },
computed: {
// 仅读取
aDouble: function () {
return this.a * 2
},
// 读取和设置
aPlus: {
get: function () {
return this.a + 1
},
set: function (v) {
this.a = v - 1
}
}
}
})
vm.aPlus // => 2
vm.aPlus = 3
vm.a // => 2
vm.aDouble // => 4
//在template里,当数据来用,默认为返回值
{{aPlus}}
{{aDouble}}
例一:用户名展示
// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
data: {
user: {
email: "fangyinghang@qq.com",
nickname: "方方",
phone: "13812312312"
}
},
computed: {
displayName(){
const user = this.user
return user.nickname || user.phone || user.email
}
},
// DRY don't repeat yourself
// 不如用 computed 来计算 displayName
template: `
<div>
{{displayName}}
</div>
`,
methods: {
add() {
console.log("add");
this.displayName = "圆圆";
}
}
}).$mount("#app");
若想对计算属性读而且修改可以用get set
// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
data: {
user: {
email: "fangyinghang@qq.com",
nickname: "方方",
phone: "13812312312"
}
},
//用对象形式
computed: {
displayName: {
get() {
const user = this.user;
return user.nickname || user.email || user.phone;
},
set(value) {
console.log(value);
this.user.nickname = value;
}
}
},
// 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");
例二:列表展示
方法一:不用计算属性
// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
//数据中的users列表,每一项都是一个对象,每个对象都有id属性、name属性和gender属性。所以本来要写四个对象,事不过三,那就写一个函数createUsers吧。
let id = 0;
const createUser = (name, gender) => { //函数createUsers接受两个参数name和gender,给id加一,之后返回一个我们想写的对象
id += 1;
return { id: id, name: name, gender: gender };
};
new Vue({
data() {
return {
users: [ //users列表,原始数据不能改
createUser("方方", "男"),
createUser("圆圆", "女"),
createUser("小新", "男"),
createUser("小葵", "女")
],
displayUsers: [] //不能直接使用users列表,要用可以修改的专门用来展示的展示列表
};
},
created() {
this.displayUsers = this.users; //当实例出现在内存中就让展示列表等于users列表
},
methods: {
showMale() { //当点击男按钮时,展示列表应为users列表中性别为男的对象
this.displayUsers = this.users.filter(u => u.gender === "男");
},
showFemale() { //当点击女按钮时,展示列表应为users列表中性别为女的对象
this.displayUsers = this.users.filter(u => u.gender === "女");
},
showAll() {
//当点击全部按钮时,展示列表就是users列表
this.displayUsers = this.users;
}
},
//视图就是 遍历展示列表中的每一个对象,取这些对象的名字和性别
template: `
<div>
<div>
<button @click="showAll">全部</button>
<button @click="showMale">男</button>
<button @click="showFemale">女</button></div>
<ul>
<li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
</ul>
</div>
`
}).$mount("#app");
方法二:优化,把distplayUsers变成计算属性
-
distplayUsers这个属性是怎么计算出来的呢?把Users列表每一个元素用该元素的属性gender筛选出来的
result = f(users,gender) -
所以要有两个数据Users和gender
-
我点击哪个按钮,就会把数据gender赋值为
male或female或空字符串。然后用数据Users列表和数据gender计算出计算属性displayUsers的值,这样视图就是显示displayUsers
// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
let id = 0;
const createUser = (name, gender) => {
id += 1;
return { id: id, name: name, gender: gender };
};
new Vue({
data() {
return {
users: [
createUser("方方", "男"),
createUser("圆圆", "女"),
createUser("小新", "男"),
createUser("小葵", "女")
],
gender: "" 新增加一个数据gender
};
},
//distplayUsers这个属性是怎么计算出来的呢?把Users列表每一个元素用该元素的属性gender筛选出来的。
computed: {
displayUsers() {
const { users, gender } = this;
if (gender === "") {
return users;
} else if (gender === "male") {
return users.filter(u => u.gender === '男');
} else if (gender === "female") {
return users.filter(u => u.gender === '女');
} else {
throw new Error("gender 的值是意外的值");
}
}
},
methods: {
showMale() { //当点击男按钮时,gender数据为male
this.gender = 'male'
},
showFemale() { //当点击女按钮时,gender数据为female
this.gender = 'female'
},
showAll() //当点击全部按钮时,gender数据为""
this.gender = ""
}
},
//视图就是 遍历被算出来的计算属性displayUsers中的每一个对象,取这些对象的名字和性别
template: `
<div>
<div>
<button @click="showAll">全部</button>
<button @click="showMale">男</button>
<button @click="showFemale">女</button></div>
<ul>
<li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
</ul>
</div>
`
}).$mount("#app");
优化代码:把methods里的很简单的事件监听函数直接写入视图
// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
let id = 0;
const createUser = (name, gender) => {
id += 1;
return { id: id, name: name, gender: gender };
};
new Vue({
data() {
return {
users: [
createUser("方方", "男"),
createUser("圆圆", "女"),
createUser("小新", "男"),
createUser("小葵", "女")
],
gender: "" 新增加一个数据gender
};
},
//distplayUsers这个属性是怎么计算出来的呢?把Users列表每一个元素用该元素的属性gender筛选出来的。
computed: {
displayUsers() {
const { users, gender } = this;
if (gender === "") {
return users;
} else if (gender === "male") {
return users.filter(u => u.gender === '男');
} else if (gender === "female") {
return users.filter(u => u.gender === '女');
} else {
throw new Error("gender 的值是意外的值");
}
}
},
//原来的methods已被删掉
//视图就是 遍历被算出来的计算属性displayUsers中的每一个对象,取这些对象的名字和性别
//gender不用写this
template: `
<div>
<div>
<button @click="gender = """>全部</button>
<button @click="gender = 'male'">男</button>
<button @click="gender = 'female'">女</button></div>
<ul>
<li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
</ul>
</div>
`
}).$mount("#app");
优化代码:哈希表优化
// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
let id = 0;
const createUser = (name, gender) => {
id += 1;
return { id: id, name: name, gender: gender };
};
new Vue({
data() {
return {
users: [
createUser("方方", "男"),
createUser("圆圆", "女"),
createUser("小新", "男"),
createUser("小葵", "女")
],
gender: "" 新增加一个数据gender
};
},
//distplayUsers这个属性是怎么计算出来的呢?把Users列表每一个元素用该元素的属性gender筛选出来的。
computed: {
displayUsers() {
const hash = { //哈希表
male: "男",
female: "女"
};
const { users, gender } = this;
if (gender === "") {
return users;
} else if (typeof gender === "string") { //只要数据gender的值是字符串(female或male)
return users.filter(u => u.gender === hash[gender]);
} else {
throw new Error("gender 的值是意外的值");
}
}
},
//原来的methods已被删掉
//视图就是 遍历被算出来的计算属性displayUsers中的每一个对象,取这些对象的名字和性别
template: `
<div>
<div>
<button @click="gender = """>全部</button>
<button @click="gender = 'male'">男</button>
<button @click="gender = 'female'">女</button></div>
<ul>
<li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
</ul>
</div>
`
}).$mount("#app");
watch 监听/侦听
定义:当数据变化时执行一个函数
- 异步,所有代码执行完才会执行
- 何为变化?简单类型看值,复杂类型(对象) 看地址 这其实就是===的规则(示例代码)
- obj原本是{a:'a'},现在
obj = {a:'a'},那么obj变了,obj里的a也变了。因为obj储存了一个对象的地址,现在重新存了一个对象的地址。当然变了 - obj原本是{a:'a'},现在obj.a = 'b',那么obj.a变了(简单类型,而且值变了),obj没有变(复杂类型,保存的地址没变),obj存的那个对象还是那个对象。
n:0, 现在n = 1,那么n变了(简单类型,而且值变了)
语法(官方文档)
watch : { [key: string]: string | Function | Object | Array }
具体的
//在new Vue的options里
watch: {
o1: ()> {}, //别用这种,这里的this是全局对象,因为箭头函数没有this,那就回去他外面的函数找this,但是new Vue不是他外面的函数,外面的函数是指外面定义的函数。所以该箭头函数外面没有函数,所以this指的就是window或者global
o2: function(value, oldValue){}, //vue规定了,参数是一个新值和一个旧值
o3(){},
o4:[f1,{o6的值}], //其他写法的结合
o5:'methodName'
o6: {handler:fn/'methodName', deep :true, immediate:true}, //handler里写要执行的函
//deep表示要不要深入的侦听这个对象,true深入的话该对象里面的属性变化也算变化
//immediate表示第一次渲染时要不要执行函数。全都默认false。
'object.a' : function( ){ } //对这个对象里面的属性a侦听
}
//或者在new Vue外面这样写
vm.$watch('xxx',fn,{deep: ;immetiate: })
//如果非要把这样新式的放到new Vue的options里,那就挂在生命周期钩子里
created(){
this.$watch('xxx',fn,{deep: ;immetiate: })
}
例子
// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
data: {
n: 0,
history: [],
inUndoMode: false //首先默认应该在非撤销模式
},
watch: { //侦听n
n: function(newValue, oldValue) {
console.log(this.inUndoMode);
if (!this.inUndoMode) {
//当inUndoMode为false,也就是非撤销模式,才往history里添加
this.history.push({ from: oldValue, to: newValue });
}
}
},
// 不如用 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() { //撤销函数undo会做
//把history里最后的一项拿出来
const last = this.history.pop();
//变成撤销状态,那么就不会往history里加东西了
this.inUndoMode = true;
console.log("ha" + this.inUndoMode);
//把最后一项的from拿出来,就是要变回去的数组
const old = last.from;
//把n变回去
this.n = old; // 注意,这里n变了,所以就会watch n 但是!watch是异步的,所以得等所有代码执行完才会执行watch里的函数!
//所以,如果直接就变回非撤销模式,然后在执行watch n 函数,还是回往history里加东西
//所以,我们过一会再回到非撤销状态。那么就按照顺序,先执行watch n 函数,在执行变回非撤销状态
this.$nextTick(() => {
this.inUndoMode = false;
});
}
}
}).$mount("#app");
computed和watch的区别
- computed是计算属性;watch是侦听器
- computed是依赖其他属性计算出一个值的,这个值在调用时不需要加括号,可以当一个属性用;根据依赖自动缓存,依赖不变这个值就不会重新计算
- watch有两个选项,immediate表示是否在第一次渲染时执行这个函数,deep监听一个对象是否要监听这个对象里面的属性的变化。
- watch某个属性变化了就执行一个函数
- 如果一个数据依赖于其他数据,那么把这个数据设计为computed的
- 如果你需要在某个数据变化时做一些事情, 使用watch来观察这个数据变化