什么是响应式
- 我给你一哈,你会疼,那你就是响应式
- 若一个物体能对外界的刺激作出反应,它就是响应式
getter 和 setter
//这样写是为了引用完整版 Vue
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
const myData = {
n: 0
}
// console.log(myData) // 精髓
new Vue({
data: myData,
template: `
<div>{{n}}</div>
`
}).$mount("#app");
setTimeout(()=>{
myData.n += 10
// console.log(myData) // 精髓
},3000)
以上代码说明我如果在外面去变更 myData 也是可以的
我们平时一般是如何变的?
// 3秒后 0 => 10 当我点击的时候 +10 ,除了用 this.n 之外,还可以用 myData.n += 10
const vm = new Vue({
data: myDate,
template: `
<div>
{{n}}
<button @click="add">+10</button>
</div>
`,
methods: {
add(){
this.n+=10
// myData.n += 10
}
},
}).$mount('#App')
优先使用 this.n , 因为很多时候我们不会去声明 myData
什么是getter 和 setter
get 就是在函数前面加一个 get , 然后调用它的时候不需要加 ()
set 必须要接收一个新的值,这个值叫什么无所谓
let obj0 = {
姓: "高",
名: "圆圆",
age: 18
};
// 需求一,得到姓名
let obj1 = {
姓: "高",
名: "圆圆",
姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log("需求一:" + obj1.姓名());
// 姓名后面的括号能删掉吗?不能,因为它是函数
// 怎么去掉括号?
// 需求二,姓名不要括号也能得出值
let obj2 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log("需求二:" + obj2.姓名);
// 总结:getter 就是这样用的。不加括号的函数,仅此而已。
// 需求三:姓名可以被写
let obj3 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xxx){
this.姓 = xxx[0]
this.名 = xxx.slice(1)
},
age: 18,
};
obj3.姓名 = '刘诗诗'
console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`)
// 总结:setter 就是这样用的。用 = xxx 触发 set 函数
// log的值
// 需求一:高圆圆
// 需求二:高圆圆
// 需求三:姓 刘,名 诗诗
姓名: (...)
它的意思是我确实可以对 姓名 进行读和写,但是并不存在一个叫做 姓名 的属性,这个读写是通过下面那个get 姓名 set 姓名 来完成的
Object.defineProperty
// 假设我想在 obj3 身上在添加新的 get 和 set 该如何添加???
// 以下就是在定义完一个对象之后,你想在它(obj3)身上在额外的添加新的 get 和 set 的时候,
// 就只能通过 Object.defineProperty 来做了
// 一般来说你不要以为 "xxx" 定义的那些属性是存在的,"xxx" 定义的那些属性是不存在的,
// 我定义的这个 get set 属性是不存在的
// 我如果 get(){ return this.xxx} 会死循环
// 因为我在 return this.xxx 时,就会调用 get ,调用 get 的时候又会调用 this.xxx,如此反复,死循环
// 所以要先声明一个局部变量, 只要不要去使用 xxx 就好了
var _xxx = 0 // 用来存放 xxx 的值的
// 我们给 obj3 添加了一个虚拟属性 xxx ,它有一个 get 和一个 set
Object.defineProperty(obj3, 'xxx', {
// 这里不用写成 get xxx(){}, 这里的 get(){} 就是 get xxx(){}
get(){
return _xxx
},
set(value)
_xxx = value
}
})
代理和监听
需求一:用 Object.defineProperty 定义 n
给data1 添加一个叫做 n 的属性,这个属性的值为 0 ,注意它的值就是 0 ,它的值不是 value: 0
let data1 = {}
Object.defineProperty(data1, 'n', {
value: 0
})
需求二:n 不能小于 0
let data2 = {}
data2._n = 0 // _n 用来存 n 的值
Object.defineProperty(data2, 'n', {
get(){
return this._n
},
set(value){
if(value<0)return
this._n = value
}
})
console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 1 成功`)
如果对方直接使用 data2._n 呢?
那就不暴露,使用代理
需求三:使用代理
如何做到不让别人访问我的 _n
let data3 = proxy({ data: {n: 0} })
function proxy({data}){ // const {data} = options
const obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data._n
},
set(value){
if(value<0)return
data._n = value
}
})
return obj // obj 就是代理
}
{n: 0} 这个对象无法去做修改, 它没有名字
代理举例: 我对中介说啥,中介就对房东说啥,这个中介就是代理,中介会帮房东把关(n<0的时候 return)
需求四:就算用户擅自修改 myData, 也要拦截他
先拿原始的值,然后删掉,这样你就没有办法去操控原始的数据了
然后我在创建一个虚拟的 n, delete data.n 可以不写,因为当我写这句话的时候,我声明的虚拟 n 和之前的 n
是重名的,它就会用新的直接覆盖之前的
Vue 的 data 是响应式
const vm = new Vue({data: {n: 0}})
- 我如果修改 vm.n,那么 UI 中的 n 就会响应我
- Vue2 通过 Object.defineProperty 来实现数据响应式
- 所有的数据放到一个对象里,然后去操作对象,对象就会改变数据,来自动更新视图
- 只需关注页面数据如何变化,因为数据变化后,视图也会自动更新
- 数据改变后,使用该数据的地方被动发生响应,更新视图
- Vue不能检测到对象属性的添加或删除,如果一开始没有在Data上声明属性,就算你对这个属性做出更改,也不会更新UI。解决的方法是手动调用Vue.set或者this.$set
Vue 的 data 的 bug
Object.defineProperty 的问题
我们要用这个函数的时候需要给它一个对象
Object.defineProperty(obj, 'n', {...})
上面的问题就是 必须要有一个 'n',才能监听&代理 obj.n
如果没有给 n 怎么办?
- Vue会给出警告
2. 此时我点击 set b,视图中不会显示 1 因为 Vue 没办法监听一开始不存在的 obj.b
解决办法
- 把 key 都声明好,后面不再加属性
- 使用
Vue.set
或者this.$set
,它们两是没有任何区别的加 $ 是为了防止和 data 里的重名
Vue.set 和 this.$set
Vue.set(this.obj, 'b', 1)
this.$set(this.obj, 'b', 1)
作用
- 新增 key
- 自动创建代理和监听(如果没有创建过)
- 触发 UI 更新(但不会立即更新)
举例
this.$set(this.object, 'm', 100)
这里的 this 指的是 vm
数组的变异方法
如果data里有数组咋办?
没有办法提前声明所有 key
数组的长度可以一直增加,下标就是 key
把数组理解为 [0: 'a'; 1: 'b'; 2: 'c'] ,我现在添加一个 d ,就相当于添加一个下标,和之前我们添加b,没有区别,所以 Vue 观察不到这个现象,因为你一开始告诉我这个数组的下标是 0 1 2,现在加一个 3 ,我无法知道
改成 this.$set(this.array, 3, 'd')
这样是可以成功添加,那是不是说明我只要操作数组我就要 set,因为数组不确定有多少没有办法提前声明
Vue 会篡改这个数组,它会在中间加一层原型,这个原型有7种方法,这个和以前的数组方法不一样,他们首先会调以前的数组方法,调完以后通知 Vue 你给我添加一个监听和代理
篡改数组 API ,见文档 👉🏻 变异方法
怎么篡改的
有点类似以下这种
总结
对象中新增的key
- Vue没有办法事先监听和代理
- 要使用set来新增key,创建监听和代理,更新UI
- 最好提前把属性都写出来,不要新增key
- 但数组做不到不新增key
数组中新增的key
- 也可用set来新增key,更新UI
- 不过尤雨溪篡改了7个API方便我们对数组进行增删
- 这7个API会自动处理监听和代理,并更新UI
结论:数组新增key最好通过7个API
注意
- this.$set 作用于数组时,并不会自动添加监听和代理。
- 使用 Vue 提供的数组变异 API 时,会自动添加监听和代理
set 了之后再用
this.array[n] += 1
是否会触发 UI 更新(答案是不会)