网站建设情况存在问题,晋城建设局官方网站,东莞黄页顺企网,海洋cms响应性数据的前世 js的程序性: 一套固定的#xff0c;不会发生变化的执行流程
1 #xff09;没有响应的数据
// 定义商品对象
const product {price: 10,quantity: 2
}// 总价格
let total product.price * product.quantity
console.log(总价格#xff1a;${total}) //…响应性数据的前世 js的程序性: 一套固定的不会发生变化的执行流程
1 没有响应的数据
// 定义商品对象
const product {price: 10,quantity: 2
}// 总价格
let total product.price * product.quantity
console.log(总价格${total}) // 20// 修改商品的数量
product.quantity 5
console.log(总价格${total}) // 20这是一段非常普通的js程序当最后 product.quantity 发生改变的时候最终结果并没有发生变化这里当商品数量发生变化总价格也会发生变化是我们的期望由于js程序性的约束我们只能得到20我们想让程序变得更智能
2 进一步改造
// 定义商品对象
const product {price: 10,quantity: 2
}// 总价格
let total 0// 定义一个 effect 函数
const effect () {total product.price * product.quantity // 访问属性这里是 getter行为
}effect()
console.log(总价格${total}) // 20// 修改商品的数量
product.quantity 5 // 修改属性这里是 setter 行为effect() // 注意这里
console.log(总价格${total}) // 50这里封装了一个effect方法这个方法是重新计算 total 的方法当 product.quantity 数据发生改变的时候手动调用了一次 effect 方法以上的方式是每次手动触发 effect 方法进行一次 类似 getter 操作这样手动操作是比较麻烦的为此js中的API可以有效解决这个问题
响应式数据的今生 1 关于响应性数据
响应数据是指影响视图变化的数据
2 ) vue2核心响应式API Object.defineProperty() 方法
let quantity 2
const product {price: 10,quantity
}// 总价格
let total 0// 计算总价格函数
const effect () {total product.price * product.quantity
}effect()
console.log(总价格${total}) // 20// 响应式变化
Object.defineProperty(product, quantity, {set(newVal) {console.log(setter)quantity newValeffect()},get() {console.log(getter)return quantity // 这里的变量是暴露在最外面的不是很好}
})这样可以在指定对象上指定属性上的 getter 和 setter 行为以此来触发effect(更新程序)这样来说相对更智能了
3 Obeject.defineProperty() 在设计上的缺陷
存在一个致命缺陷vue官网/深入响应式原理/检测变化的注意事项 由于js的限制vue不能检测数组和对象变化
代码示例如下
templatedivulli v-for(val, key, index) in obj :keyindex{{ key }} --- {{ val }}/li/ulbutton clickaddObjKey为对象增加属性/buttondiv ---------------- /divulli v-for(item, index) in arr :keyindex{{ item }} --- {{ index }}/li/ulbutton clickaddArrItem为数组增加元素/button/div
/template
scriptexport default {name: App,data() {return {obj: {name: 张三,age: 30},arr: [张三, 李四]}},methods: {addObjKey() {this.obj.gender 男console.log(this.obj)},addArrItem() {this.arr[2] 王五console.log(this.arr)}}}
/script上面两个按钮点击后数据会更新但是页面视图不会更新当对象新增一个没有在data中声明的属性时新增的属性不是响应式的当为数组通过下标形式新增一个元素时新增的元素不是响应式的why? Object.defineProperty 只能监听指定对象指定属性的 getter 和 setterjs限制是指没有办法知道为某一个对象新增了某一个属性这类行为新增属性会失去响应性
4 Vue3中的 Proxy 文档https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy Proxy 对象用于创建一个对象的代理从而实现基本操作的拦截和自定义如属性查找、赋值、枚举、函数调用等。 语法 const p new Proxy(target, handler)target 表示proxy包装的目标对象可以是任何对象: 原生数组函数甚至另一个对象p 是 proxy的实例是代理对象handler 是一个对象可以在这个对象上指定getter和setter
代码改造示例如下
// 定义商品对象
const product {price: 10,quantity: 2
}// 生成代理对象, 注意事项使用时不能使用被代理对象(原对象)而应该使用代理对象
// proxy 代理的是整个对象而非某个对象的某个属性
const proxyProduct new Proxy(product, {set(target, key, newVal, receiver) {// console.log(setter)target[key] newVal// 这里触发 effect 重新计算effect()return true},get(target, key, receiver) {// console.log(getter)return target[key]}
})// 总价格
let total 0// 定义一个 effect 函数
const effect () {total proxyProduct.price * proxyProduct.quantity // 访问属性这里是 getter行为
}effect()
console.log(总价格${total}) // 20// 修改商品的数量, 注意这里是修改的代理对象的值而非被代理对象的值
proxyProduct.quantity 5 // 修改属性这里是 setter 行为effect()
console.log(总价格${total}) // 50通过修改代理对象的值来让被代理对象同步发生变化这里使用 proxy 完成了 和 Object.defineProperty一样的效果总结 proxy: Proxy 将一个对象 (被代理对象), 得到一个新的对象 (代理对象), 同时拥有被代理对象中所有的属性当想要修改对象的指定属性时我们使用 代理对象 进行修改代理对象的任何一个属性都可以触发 handler 的getter和setter Object.defineProperty 该API为指定对象的指定属性 设置 属性描述符当想要修改对象的指定属性时可以使用原对象进行修改通过属性描述符只有 被监听 的指定属性才可以触发 getter 和 setter 所以当 vue3 通过 Proxy 实现响应性核心 API 之后 vue 将不会再存在新增属性时失去响应性的问题
5 proxy的最佳合伙API: Reflect 拦截js对象操作 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect Reflect 是一个内置的对象它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler (en-US) 的方法相同。Reflect 不是一个函数对象因此它是不可构造的。 const obj { name: 张三 }
Reflect.get(obj, name) // 张三Reflect.get(target, propertyKey[, receiver]) 可以看到这个 API 有三个参数target 需要取值的目标对象propertyKey 需要获取的值的键值receiver 如果target对象中指定了getterreceiver则为getter调用时的this值
测试代码如下:
// p1 对象
const p1 {lastName: 张,firstName: 三,get fullName() {return this.lastName this.firstName}
}// p2 对象
const p2 {lastName: 李,firstName: 四,get fullName() {return this.lastName this.firstName}
}// 测试
console.log(p1.fullName) // 张三
console.log(Reflect.get(p1, fullName)) // 张三
console.log(Reflect.get(p1, fullName, p2)) // 李四 这里改变了getter的this指向this指向了 p2, 所以 getter中获取的是 p2的fullName属性console.log(p2.fullName) // 李四使用 proxy 和 Reflect 一起使用
// p1 对象
const p1 {lastName: 张,firstName: 三,get fullName() {return this.lastName this.firstName}
}const proxy new Proxy(p1, {get(target, key, receiver) {console.log(getter)return target[key]}
})console.log(proxy.fullName) // 这里进行一次getter操作会执行一次上述代码只会触发一次getter, 因为其中的this指向是target也就是原对象p1但是我们的理解如果做任意的取值都会触发一次getter, 也就是 访问fullName的时候会触发一次getter, 但是fullName里面也有两次getter这时候我们想要触发三次getter 如何修改呢
// p1 对象
const p1 {lastName: 张,firstName: 三,get fullName() {return this.lastName this.firstName}
}const proxy new Proxy(p1, {get(target, key, receiver) {console.log(getter, key)// return target[key]return Reflect.get(target, key, receiver) // 注意修改这里}
})console.log(proxy.fullName) // 这里进行一次getter操作会执行一次这时候 proxy.fullName 会触发 三次 getter的行为 先输出fullName 的getter再输出lastName 的getter最后输出firstName 的getter 某些场景下使用 return target[key] 会存在bug,请使用 return Reflect.get(target, key, receiver) 代替