响应式就是一个物体能对外界的刺激做出反应
Vue 的 data 就是响应式
- const vm = new Vue ({data:{n:0}})
- 如果修改 vm.n,那么 UI 的 n 就会响应
- Vue 是通过 Object.defineProperty 来实现数据响应式
Vue 的数据响应式原理依赖 Object.defineProperty,Vue 通过设定对象属性的 setter/getter 方法来监听数据的变化,通过 getter 进行依赖收集,而每个 setter 方法就是一个观察者,在数据变更的时候通知订阅者更新视图。Vue 不能检测到对象属性的添加或删除,解决方法是手动调用 Vue.set 或者 this.$set。
何为响应式网页?
改变窗口大小,网页内容会做出响应,就是响应式网页
getter / setter
get 语法将对象属性绑定到查询该属性时将被调用的函数
当尝试设置属性时,set 语法将对象属性绑定到要调用的函数
let obj = {
姓: "徐",
名: "冬冬",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xxx){
this.姓 = xxx[0]
this.名 = xxx.substring(1)
},
age: 18
};
obj3.姓名 = '徐冬冬'
Object.defineProperty
可以给对象添加属性 value
可以给对象添加 getter / setter
getter / setter 用于对属性的读写进行监控
var _xxx = 0 //用来放 xxx 的值
Object.defineProperty(obj,'xxx',{ // 定义的属性 xxx 是不存在的,如果在后面 return 会死循环
get(){
return _xxx
},
set(value){
_xxx = value
}
})
需求一:用 Object.defineProperty 定义 n
let data = {}
Object.defineProperty(data1, 'n', {
value: 0
})
需求二:n 不能小于 0 (即 data2.n = -1 应该无效,但 data2.n = 1 有效)
结论:可以给 set 做判断,不满足就不 set
问题:无法阻止别人修改 _n 的值
let data = {}
data._n = 0 // _n 用来存储 n 的值
Object.defineProperty(data, 'n', {
get(){ // 读属性
return this._n // 防止没有data,所以推荐使用this隐式传参
},
set(value){ // 写属性
if(value < 0) return
this._n = value
}
})
需求三:使用代理
结论:因为只暴露了代理对象,别人无法接触真实的对象
let data = proxy({ data:{n:0} }) // 括号里是匿名对象,无法访问
function proxy({data}){ // 解构赋值,原本应该是(options){const {data} = options}
const obj = {}
// 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0)return
data.n = value
}
})
return obj // obj 就是代理
}
需求四:绕过代理,直接在初始化时申明引用
let myData = {n:0}
let data = proxy({ data:myData }) // 括号里是匿名对象,无法访问
myData.n = -1
需求五:就算用户擅自修改 myData,也要拦截他
let myData = {n:0}
let data = proxy({ data:myData }) // 括号里是匿名对象,无法访问
function proxy({data}){
let value = data.n
Object.defineProperty(data, 'n', { // 申明的 n 和之前的 n 重名,会直接用新的覆盖之前的
get(){
return value
},
set(newValue){
if(newValue<0)return
value = newValue
}
})
// 上面几句会监听 data
const obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0)return//这句话多余了
data.n = value
}
})
return obj // obj 就是代理
}
vm = new Vue({data: myData})
一、会让 vm 成为 myData 的代理 (proxy)
二、会对 myData 的所有属性进行监控
监控是为了防止 myData 的属性变了,vm 不知道
vm 知道属性变了就可以调用 render(data)
Ul= render(data) UI 就可以自动刷新
什么是代理?
代理是一种设计模式,对 mydata 对象的属性的读写,全权由另一个对象 vm 负责
那么 vm 就是 mydata 的代理
Object.defineProperty 的问题
Object.defineProperty(obj,'n',{...})
必须要有 n 才能监听&代理
如果没有给 n 会有如下问题:
- Vue 会给出一个警告
new Vue({ data: {}, template: ` <div>{{n}}</div> ` }).$mount("#app"); [Vue warn]: Property or method "n" is not defined on theinstance but referenced during render.
- Vue 只会检查第一层属性,会绕过警告,点击 set b 视图不会显示1,因为 Vue 没法监听一开始不存在的 obj.b
new Vue({ data: { obj: { a: 0 // obj.a 会被 Vue 监听 & 代理 } }, template: ` <div> {{obj.b}} <button @click="setB">set b</button> </div> `, methods: { setB() { this.obj.b = 1 } } }).$mount("#app")
解决方法:
- 将 key 都申明好,后面不再加属性
- 使用 Vue.set 或 this.$set
Vue.set 和 this.$set 的作用
- 新增 key
- 自动创建代理和监听
- 触发 UI 更新(但不会立刻更新)
this.$set(this.object,'m',100)
数组中新增的 key
- 可用 set 来新增 key,更新 Ul
- Vue 篡改了7个 API 方便对数组进行增删
- 这7个 API 会自动处理监听和代理,并更新 Ul
- 数组新增 key 最好通过7个 API
push() pop() shift() unshift() splice() sort() reverse()
*本文为鲲游北冥的原创文章,著作权归本人和饥人谷所有,转载务必注明来源