北京网站改版有哪些好处,做soho 怎么建立网站,wordpress 网站标题设置,网站制作公司 深圳Vue2 中为什么直接通过数组的索引修改元素是不会触发视图更新 vue2 为什么不直接监听数组 Vue2 对于数组提供了一些变异方法 重写数组方法源码分析 定义拦截器将拦截器挂载到数组上面收集依赖 扩展#xff1a;理解Vue2如何解决数组和对象的响应式问题 对复杂对象的处理 复杂对…Vue2 中为什么直接通过数组的索引修改元素是不会触发视图更新 vue2 为什么不直接监听数组 Vue2 对于数组提供了一些变异方法 重写数组方法源码分析 定义拦截器将拦截器挂载到数组上面收集依赖 扩展理解Vue2如何解决数组和对象的响应式问题 对复杂对象的处理 复杂对象中对象属性的属性的变化给数据的属性set新对象 对Array的处理 以原来的Array原型为模板创建新模板对象重写新模板的push pop 等数组变异函数
的属性的变化](#%E5%A4%8D%E6%9D%82%E5%AF%B9%E8%B1%A1%E4%B8%AD%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7%E7%9A%84%E5%B1%9E%E6%80%A7%E7%9A%84%E5%8F%98%E5%8C%96) - [给数据的属性set新对象](#%E7%BB%99%E6%95%B0%E6%8D%AE%E7%9A%84%E5%B1%9E%E6%80%A7set%E6%96%B0%E5%AF%B9%E8%B1%A1)
- [对Array的处理](#%E5%AF%B9array%E7%9A%84%E5%A4%84%E7%90%86)- [以原来的Array原型为模板创建新模板对象](#%E4%BB%A5%E5%8E%9F%E6%9D%A5%E7%9A%84array%E5%8E%9F%E5%9E%8B%E4%B8%BA%E6%A8%A1%E6%9D%BF%E5%88%9B%E5%BB%BA%E6%96%B0%E6%A8%A1%E6%9D%BF%E5%AF%B9%E8%B1%A1)- [重写新模板的push pop 等数组变异函数](#%E9%87%8D%E5%86%99%E6%96%B0%E6%A8%A1%E6%9D%BF%E7%9A%84push-pop-%E7%AD%89%E6%95%B0%E7%BB%84%E5%8F%98%E5%BC%82%E5%87%BD%E6%95%B0)Vue2 中为什么直接通过数组的索引修改元素是不会触发视图更新
在 Vue2 中如果 直接通过数组的索引修改元素是不会触发视图更新的 原因是 Vue2 使用了“劫持”的技术来实现数据的响应式。
Vue2 中对于对象的响应式是通过 Object.defineProperty() 方法来实现的这个方法可以将对象属性转换成 getter 和 setter 的形式从而实现对属性的“劫持”。
但是对于数组来说Array.prototype 中的方法并不会触发这样的 getter 和 setter因此 Vue 无法监听到这些变化也就不能及时地更新视图。 当数组的元素发生变化时Vue.js无法触发视图的更新因为数组的属性例如长度是只读的。
为了解决这个问题Vue2 对于数组提供了一些变异方法如 push()、pop()、splice() 等这些方法具有响应式。当使用这些方法操作数组时Vue2 会检测到数组的变化并及时更新视图从而保证视图和状态的同步。
而直接通过索引来修改元素则无法触发这种变化因此也就无法实现响应式更新。
为了解决这个问题可以通过以下两种方法来实现对数组元素的响应式更新
使用 $set() 方法
$set() 方法可以向数组中添加新的元素并且确保这个新元素也是响应式的。例如
this.$set(this.items, index, newValue);通过 splice() 方法修改数组元素
splice() 方法可以删除数组中的元素并且可以在删除元素的位置插入新元素。因为这个方法会改变原始数组因此 Vue2 可以检测到数组的变化从而响应式地更新相关的视图。例如
this.items.splice(index, 1, newValue);vue2 为什么不直接监听数组
Vue2 不直接监听数组的原因是基于性能和一致性的考虑。
在 Vue2 中通过 Object.defineProperty 来劫持对象属性的 getter 和 setter并在 setter 中实现对数据变化的追踪和通知。这种方式在处理对象属性时非常高效可以精确追踪数据的变化并进行响应式更新。
然而对于数组而言它是一个特殊的对象类型。数组的操作方法例如 push、pop、splice 等会改变数组的内容但不会触发数组本身的 setter。因此Vue2 无法直接侦听数组的变化。
为了解决这个问题Vue2 提供了一组特殊的数组方法例如 s e t 、 set、 set、splice、$push 等通过这些方法修改数组Vue2 能够检测到数组的变化并进行响应式更新。这些特殊的数组方法相当于对原生数组方法做了一层代理或封装。
不直接监听数组的设计是为了在性能上取得平衡因为直接监听数组的每个元素可能导致性能下降。而通过特殊的数组方法进行包装只在需要的时候触发更新能够更好地控制性能。
虽然 Vue2 无法直接监听数组但仍然可以通过手动调用 $set 或使用深度监听的方式来实现对数组的监听。此外Vue3 中通过 Proxy 对象实现了对数组的监听能力更加灵活和高效。
Vue2 对于数组提供了一些变异方法
1、删除数组最后一位元素pop()2、向数组最后面添加元素push() 。注意可以添加多个元素比如 letters.push( ‘a’ , ‘b’ )3、删除数组第一位元素shift()4、向数组最前面添加元素unshift()。注意可以添加多个元素比如 letters.unshift( ‘a’ , ‘b’ )5、删除或插入或替换数组元素splice() 比如删除元素splice(2) 是 从第二位开始删除后面所有元素 比如删除元素splice(2,3) 是 从第二位开始删除3个元素 比如插入元素splice(2,0,‘j’,‘k’) 是 在第二位开始插入元素 ‘j’,‘k’ 比如替换元素splice(2,3,‘m’,‘n’,‘p’) 是 在第二位开始替换3个元素为’m’,‘n’,‘p’6、数组排序sort()7、数组内容反转reverse()
重写数组方法源码分析
实现的思路大体上就是说是使用了拦截器覆盖了Array.prototype上的方法在执行原型上的方法之外去做数据的响应式。
将数组的原型存到对象arrayMethods中找到Array上能够改变数组自身的7个方法 push, pop, shift,unshift, splice, sort, reverse将这7个方法进行响应式处理处理完成后用它们把arrayMethods中对应的方法覆盖掉将需要进行响应式处理的数组arr的__proto__指向arrayMethods如果浏览器不支持访问__proto__则直接将响应式处理后的7个方法添加到数组arr上如果要将一个数组完全实现响应式需要遍历该数组将数组中的数组使用该方法进行响应式处理将对象使用walk方法进行响应式处理
更多详细内容请微信搜索“前端爱好者“ 戳我 查看 。
定义拦截器
// 获取Array的原型
const arrayProto Array.prototype;
// 创建一个新对象该新对象的原型指向Array的原型。
export const arrayMethods Object.create(arrayProto);
[push,pop,shift,unshift,splice,sort,reverse
]
.forEach(mentod {// 缓存原始方法const original arrayProto[method];// 对新原型对象上的方法做数据绑定Object.defineProperty(arrayMethods method {value: function mutator(...args) {// 返回原始方法return original.apply(this, args); },enumerable: false,writable: true,configurable: true})
})将拦截器挂载到数组上面
import { arrayMethods } from ./array // 处理好的Array原型对象
// __proto__是否可用
const hasProto __proto__ in {};
// 所有属性名不论是否可枚举与Object.keys的区别
const arrayKeys Object.getOwnPropertyNames(arrayMethods);export class Observe {// 将value转为响应式constructor (value) {this.value value;if (Array.isArray(value)) {const augment hasProto ? protoAugment : copyAugment;augment(value, arrayMethods, arrayKeys);} else {this.walk(value); // Object的响应式处理在其他文章中}}
}/**
* __proto__可用的处理方式
* 将target对象的原型对象替换为src
*/
function protoAugment(target, src, keys) {target.__proto__ src;
}/**
* __proto__不可用的处理方式
* 将src上面的所有属性都copy到target
*/
function copyAugment (target, src, keys) {for (let i 0, len keys.length; i len; i ) {const key keys[i];def(target, key, src[key]);}
}// Object.defineProperty()的封装
function def (obj, key, val, enumerable) {Object.defineProperty(obj, key, {value: val,enumerable: !!enumerable,writable: true,configurable: true})
}收集依赖
收集依赖
function defineReactive(data, key, val) {let childOb observe(val);let dep new Dep(); // 存储依赖Object.defineProperty(data, key, {enumerable: true,configurable: true,get: function () {dep.depend();if (childOb) childOb.dep.depend(); // 收集return val;},set: function (newVal) {if (val newVal) return;dep.notify();val newVal;}})
}// 返回val的响应式对象
function observe(val, asRootData) {if (!isObject(value)) return;let ob;// 避免重复侦测if (hasOwn(value, __ob__) value.__ob__ instanceof observer) {ob value.__ob__;} else {ob new Observe(value)}return ob;
}扩展理解Vue2如何解决数组和对象的响应式问题
Vue2是通过用Object…defineProperty来设置数据的getter和setter实现对数据和以及视图改变的监听的。
对于数组和对象这种引用类型来说getter和setter无法检测到它们内部的变化。
那么Vue2是则么来解决这个问题的呢
通过一个简单的例子来理解Vue2中是如何解决数组和对象的响应式问题。
htmlhead/headbodyscript//1. 定义一个data对象来模拟组件中的数据var data {name: xwdsex: 1dog: {name: peterage: 5}hobby: [pingpang basktetball]}//2. 对Data做 reactive化Observer(data)function Observer(data) {// 模拟组件初始化对Data reactive化if (typeof data ! object || data null) {return data}for (let item in data) {//将数据响应式化defineReactive(data item data[item])}return data}function defineReactive(target key value) {Object.defineProperty(target key {enumerable: falseconfigurable: falseget() {//用打印控制台模拟视图发生渲染console.log(视图渲染使用了数据)return value;}set(newValue) {if (newValue ! value) {value newValue;//用打印控制台模拟数据变更视图更新console.log(更新视图)}}})}/script
/body对复杂对象的处理
对复杂对象对象属性的变更主要有以下几种情况
复杂对象中对象属性的属性的变化
Vue2的处理方式是在响应化的时候深度遍历Data对象的属性直到对所有的基本类型的属性都添加上getter和setter。 function defineReactive(targetkeyvalue) {Observer(value)Object.defineProperty(targetkey{enumerable: falseconfigurable: falseget() {console.log(视图渲染使用了数据)return value;}set(newValue) {if (newValue ! value) {value newValue;console.log(更新视图)}}})}给数据的属性set新对象 set新对象的时候会显示更新视图但是新添加对象的value并不会加上响应式。
Vue2的处理方式是在set的时候对该属性重新进行defineReactive操作给属性加上getter和setter。
set(newValue) {Observer(value)if (newValue ! value) {value newValue;console.log(更新视图)}
}Note: 因为Vue2对数据响应式的添加是在一开始初始化以及set属性的时候所以在使用过程中当发生data的属性的增加或者删除。
Vue不能添加响应式。如果要对运行过程中添加的属性做响应式必须使用Vue.delete方法或者Vue.Set。
对Array的处理
数组内部的变化包括使用我们常用的数组函数pushpop等。
都不能被setter函数检测到只有当整个数组被换掉才会被检测到。 Vue2为了解决这个问题采用的方式是:
提供一组新的数组变异函数。
换掉Array的原型用新的变异函数在自定义的变异函数里做更新视图的操作。
以原来的Array原型为模板创建新模板对象
const oldArrayProto Array.prototype;const newArrProto Object.create(oldArrayProto);重写新模板的push pop 等数组变异函数 if (Array.isArray(data)) {data.__proto__ newArrProto}Note: Vue 不能检测以下数组的变动
当你利用索引直接设置一个数组项时例如vm.items[indexOfItem] newValue当你修改数组的长度时例如vm.items.length newLength
htmlhead/headbody script//1. 定义一个data对象来模拟组件中的数据var data {name: xwd,sex: 1,dog: {name: peter,type: dog},hobby: [pingpang, basktetball],}const oldArrayProto Array.prototype;const newArrProto Object.create(oldArrayProto);[push, pop].forEach(methodName {newArrProto[methodName] function () {console.log(更新视图)oldArrayProto[methodName].call(this, ...arguments)}});Observer(data)function Observer(data) {if (typeof data ! object || data null) {return data}if (Array.isArray(data)) {data.__proto__ newArrProto}for (let item in data) {//将数据响应式化defineReactive(data, item, data[item])}return data }function defineReactive(target, key, value) {Observer(value)Object.defineProperty(target, key, {enumerable: false,configurable: false,get() {console.log(视图渲染使用了数据)return value;},set(newValue) {Observer(value)if (newValue ! value) {value newValue;console.log(更新视图)}}})} /script
/body
/html仍存在的问题
用 Object.defineProperty这种方法去监听数据和视图的改变当遇到复杂对象的时候需要对所有的对象进行深度遍历来给属性设置上getter和setter函数这会导致首屏加载速度很慢。
针对这个问题 Vue3 将响应式的实现由Object.defineProperty换成了Proxy实现在数据要用的时候再添加响应式提高了首屏加载速度。
参考文档
https://blog.csdn.net/wlijun_0808/article/details/127714522https://blog.csdn.net/qq_36290842/article/details/120941497