1.Object.definedProperty()
数据响应式文档:深入响应式原理 — Vue.js
Object.defineProperty文档:Object.defineProperty() - JavaScript | MDN
#get/set 模拟对象属性的读写操作
let obj = {
姓: "高",
名: "圆圆",
get name() {
return this.姓 + this.名;
},
set name(str){
this.姓 = str[0]
this.名 = str.substring(1)
},
age: 20
};
/* getter 使用方法是不加括号的函数 */
console.log("getter: " + obj.name);
/* setter 使用方法 properties = value,触发 set 函数 */
obj.name = '王明明'
console.log(`setter:姓 ${obj.姓},名 ${obj.名}`)
/* name(...) obj并不存在name属性,但是可以通过 get/set 对name属性进行读写 */
console.log(obj)
#Object.defineProperty() 添加新的get/set属性
/* _id存储真实id属性值(id属性并不存在) */
var _id=0
Object.defineProperty(obj,"id",{
get(){
return _id
},
set(value){
_id=value
}
})
3.options.data 代理与监听
#直接定义属性n
let data0 = {
n: 0
}
#用 Object.defineProperty 定义 n
let data1 = {}
Object.defineProperty(data1, 'n', {
value: 0
})
console.log(`data1.n: ${data1.n}`)
#使用访问器属性 get/set 添加额外逻辑
/* 目标: 使得 n 不能小于 0, 即 data2.n = -1 无效;但 data2.n = 1 有效 */
let data2 = {}
/* _n 存储 n 的值 */
data2._n = 0
Object.defineProperty(data2, 'n', {
get(){
return this._n
},
set(value){
if(value < 0) return
this._n = value
}
})
console.log(`data2.n: ${data2.n}`)
data2.n = -1
console.log(`set data2.n: ${data2.n} ,设置为 -1 失败`)
data2.n = 1
console.log(`set data2.n: ${data2.n} ,设置为 1 成功`)
#使用代理, 用代理对象操作数据
let data3 = proxy({ data:{n:0} })
/* 为了防止 data2._n = -1 被直接修改的情况
* 传入匿名对象 { data:{n:0} } 进行数据读写,无法访问,避免被修改
* 匿名对象{} 中 data 属性存了 {n:0} 对象的地址
* */
function proxy({data}/* 解构赋值 */){
/* 添加代理对象 obj */
const obj = {}
/* 理论上应该遍历 data 的所有 key,这里做了简化,写死只访问 'n' 这个属性 */
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0) return
data.n = value
}
})
/* obj 就是 data3 */
return obj
}
/* 代理对象 obj.n 的所有操作都是对 data.n 的读写 */
console.log(`data3.n: ${data3.n}`)
data3.n = -1
console.log(`代理 data3.n: ${data3.n},设置为 -1 失败`)
data3.n = 1
console.log(`代理 data3.n: ${data3.n},设置为 1 成功`)
#杠精: 给匿名对象命名, 通过直接更改对象名的属性值可以绕开代理
let myData = {n:0}
let data4 = proxy({ data:myData })
console.log(`杠精 data4.n: ${data4.n}`)
myData.n = -1
console.log(`杠精 data4.n: ${data4.n},设置为 -1 失败了吗!?`)
#添加监听, 拦截用户擅自修改 myData 引用绕开代理
let myData5 = {n:0}
let data5 = proxy2({ data:myData5 })
function proxy2({data}/* 解构赋值 */){
/* 监听逻辑,监听 data */
let value = data.n
/* delete data.n 这句不用写,get/set 新添加的虚拟属性n代替 data.n */
Object.defineProperty(data, 'n', {
get(){
return value
},
set(newValue){
if(newValue<0) return
value = newValue
}
})
/* 代理逻辑,添加代理 obj */
const obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
data.n = value
}
})
return obj // obj 就是代理对象
}
console.log(`data5.n: ${data5.n}`)
myData5.n = -1
console.log(`监听代理 data5.n: ${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`监听代理 data5.n: ${data5.n},设置为 1 成功了`)
# Vue 就是用类似的逻辑对 options.data 所有数据进行监听,再渲染:
# let data5 = proxy2({ data:myData5 })
# let vm = new Vue({data: myData})
3.Vue.set (this.$set) 与数组变异方法
#报错:n is not defined on the instance but referenced during render
new Vue({
data: {},
template: `
<div>{{n}}</div>
`
}).$mount("#app");
#绕开vue警告,存在第一层对象但操作不存在的嵌套数据,视图不更新(嵌套的数据不会自动变为响应式)
new Vue({
data: {
obj: {
a: 0, // obj.a 会被 Vue 监听 & 代理
/* [方法一: 提前声明key] b: undefined */
}
},
template: `
<div>
{{obj.b}}
<button @click="setB">set b</button>
<button @click="add">+10</button>
</div>
`,
methods: {
setB() {
this.obj.b = 1; // 页面中不会显示 1
/* [方法二] 使用 Vue.set/this.$set 新增 key */
/*
* Vue.set(this.obj,'b',1)
* this.$set(this.obj,'b',5)
* console.log(Vue.set === this.$set) true
* */
},
add(){
return this.obj.b += 10
}
}
}).$mount("#app");
数组变异方法文档:列表渲染 — Vue.js
#数组的更新检测,Vue数组变异方法mutator
#Vue无法检测直接索引修改数组元素 or 使用 length 属性修改数组长度的变化
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
this.array[3] = "d"; // 页面中不会显示 'd'
/* 添加单个数组元素 */
/* this.$set(this.array,3,'d') */
/* [Vue.Array.mutator()数组变异方法] 添加单个或多个数组元素 */
/*
* this.array.push('d','e','f')
* console.dir(this.array)
* */
}
}
}).$mount("#app");