vue中哪些数组的方法可以做到响应式

news2024/11/17 22:40:11
  • 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 会检测到数组的变化并及时更新视图,从而保证视图和状态的同步。

而直接通过索引来修改元素,则无法触发这种变化,因此也就无法实现响应式更新。

为了解决这个问题,可以通过以下两种方法来实现对数组元素的响应式更新:

  1. 使用 $set() 方法

$set() 方法可以向数组中添加新的元素,并且确保这个新元素也是响应式的。例如:

this.$set(this.items, index, newValue);
  1. 通过 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、 setsplice、$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中是如何解决数组和对象的响应式问题。

<html>

<head>

</head>

<body>
  <script>
    //1. 定义一个data对象来模拟组件中的数据
    var data = {
      name: 'xwd'sex: 1dog: {
        name: 'peter'age: 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(target,key,value) {
      Observer(value)
      Object.defineProperty(target,key,{
        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的处理

数组内部的变化,包括使用我们常用的数组函数,push,pop等。

都不能被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
<html>

<head>

</head>

<body> 
  <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/127714522
  • https://blog.csdn.net/qq_36290842/article/details/120941497

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1314051.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑自动关机怎么设置?

电脑自动关机怎么设置&#xff1f;如果你是一名上班族&#xff0c;工作忙起来很多事情都会忘记做&#xff0c;有时候忙到很晚后紧急下班&#xff0c;就会忘记给电脑关机&#xff0c;电脑如果经常不关机&#xff0c;那么电脑就会超负荷的运转&#xff0c;大家都知道电脑的寿命是…

云端赋能大湾区:华为云照亮数字化转型之路

编辑&#xff1a;阿冒 设计&#xff1a;沐由 在中国的经济版图上&#xff0c;大湾区是极其重要的增长引擎。这块富有活力和创新力的经济区域里&#xff0c;荟聚了大量的高新技术企业&#xff0c;以及一批创新孵化器和科研机构&#xff0c;产业升级和技术创新的氛围格外浓烈。 1…

OpenCV中的格式转换

目录 1. 前言2. 采集到播放数据流的走向3. OpenCV中的格式转换3.1 RGB3.2 YUV3.2.1 YUV420{P}3.2.2 YUV420SP 4 简单应用5. 关联文章 1. 前言 实际音视频开发过程芯片是做了硬件加速的&#xff0c;主要涉及到视频编解码。二次开发过程中需要对SDK做一定的封装使用才行。 写这…

2021年数维杯国际大学生数学建模A题新冠肺炎背景下港口资源优化配置策略求解全过程文档及程序

2021年数维杯国际大学生数学建模 A题 新冠肺炎背景下港口资源优化配置策略 原题再现&#xff1a; 2020年初&#xff0c;新型冠状病毒&#xff08;COVID-19&#xff09;在全球迅速蔓延。根据世界卫生组织2021年7月31日的报告&#xff0c;新冠病毒疫情对人类的影响可能比原先预…

nginx反向代理实践指南:访问Tomcat

目录 前言1 实现的效果2 访问流程分析3 安装tomcat并测试4 配置4.1 在Windows系统的hosts文件进行域名和IP对应关系的配置4.2 在NGINX进行请求转发的配置&#xff08;反向代理配置&#xff09; 5 最终测试结论 前言 从Windows系统访问Tomcat Web应用程序&#xff0c;设置和配置…

对BIOS进行简单快速的设置更改,就能启用安全引导来安装Windows 11

本文介绍如何在UEFI/BIOS中启用安全引导&#xff0c;以便继续安装Windows 11。 如何启用安全引导 启用安全引导最简单的方法是通过UEFI/BIOS进行。它通常被列为BIOS中的众多选项之一&#xff0c;因此你只需打开它即可启用它。 1、启动&#xff0c;或重新启动你的电脑或笔记本…

深入理解 hash 和 history:网页导航的基础(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

插头是什么

插头 电工电气百科 文章目录 插头前言一、插头是什么二、插头的类别三、插头的作用原理总结前言 插头的设计和结构会根据不同的国家和地区的标准和电源类型而有所不同。所以,在使用插头时,需要注意使用符合当地标准和规定的插头,以确保电气安全以及插入正确的电源插座 一、…

香槟过了保质期还能喝吗?

香槟是起泡酒的高级代表&#xff0c;是浪漫和喜庆的化身&#xff0c;它浑身上下都散发着无穷的魅力。那么&#xff0c;这么精贵的葡萄酒有没有保质期&#xff0c;会不会变质呢&#xff1f;云仓酒庄的品牌雷盛红酒分享当然会。一瓶酒的生命离不开它的保存期限&#xff0c;酒的质…

GPT-AI导航

1. https://ai-bot.cn/ https://ai-bot.cn/

股票交易信息实时大屏(Kafka+storm+Redis+DataV)

目录 引言 需求分析&#xff1a; 思路 数据源&#xff1a; 数据传输&#xff1a; 数据处理&#xff1a; 数据统计&#xff1a; 数据可视化&#xff1a; 数据提取&#xff1a; 技术栈 技术实现 前端界面搭建 布局: ​ 组件&#xff1a; 通信&#x…

Google Gemini Pro:AI模型的新里程碑,开放API访问;Octo: 一个开源通用的机器人策略

&#x1f989; AI新闻 &#x1f680; Google Gemini Pro&#xff1a;AI模型的新里程碑&#xff0c;开放API访问 摘要&#xff1a;Google宣布推出了名为Gemini的AI模型&#xff0c;旨在使AI更加有用。Gemini分为Ultra、Pro和Nano三个版本&#xff0c;并已开始在产品中使用。Ge…

node.js 启一个前端代理服务

文章目录 前言一、分析技术二、操作步骤2.1、下载依赖2.2、创建一个 serve.js 文件2.3、js 文件中写入以下代码 三、运行&#xff1a; node serve四、结果展示五、总结六、感谢 前言 有时候我们需要做一些基础的页面时&#xff0c;在研发过程中需要代理调用接口避免浏览器跨域…

Gartner发布2024年网络安全预测一:人工智能与网络安全将颠覆转化为机遇

Gartner 预测人工智能将以积极的方式持久地破坏网络安全&#xff0c;但也会造成许多短期的幻灭。安全和风险管理领导者需要接受 2023 年只是生成式 AI 的开始&#xff0c;并为其演变做好准备。 主要发现 生成式人工智能 (GenAI) 是一系列公认的颠覆性技术中的最新技术&#xff…

MaxCompute获取当前季度的第一天日期(odps sql)

工作中遇到获取当前季度的第一天&#xff0c;如下所示 SELECT CASE WHEN QUARTER(GETDATE()) 1 THEN DATETRUNC(GETDATE(),yyyy) WHEN QUARTER(GETDATE()) 2 THEN DATEADD(DATETRUNC(GETDATE(),yyyy),3,mm) WHEN QUARTER(GETDATE()) 3 THEN DATEADD(DATETRUNC(GETDATE(),…

学习Django从零开始之三

搭建虚拟python环境 搭建开发环境有多种方式&#xff0c;其中包括本地直接安装Python的可执行文件&#xff0c;使用virtualenv&#xff0c;以及使用Anaconda和Miniconda等工具。这些工具在创建Python虚拟环境方面各有特点。具体不同之处感兴趣的同学可以自行查阅相关资料。 简…

2023,真人漫改走上IP高地

你能接受自己的纸片人老公/老婆变成了真人吗&#xff1f; 无论大家能不能接受&#xff0c;真人漫改都已经成为了影视行业的新趋势&#xff0c;而阅文集团收购腾讯动漫的举措&#xff0c;无疑是为漫改剧添了一把火。 在阅文宣布以6亿人民币的价格收购腾讯动漫旗下的相关业务以…

spring 笔记八 SpringMVC异常处理和SpringMVC拦截器

文章目录 SpringMVC拦截器拦截器&#xff08;interceptor&#xff09;的作用拦截器和过滤器区别拦截器是快速入门拦截器方法说明 SpringMVC拦截器拦截器&#xff08;interceptor&#xff09;的作用拦截器和过滤器区别拦截器是快速入门拦截器方法说明 SpringMVC异常处理异常处理…

DevEco Studio 项目鸿蒙(HarmonyOS)资源引用(自定统和系统)

DevEco Studio 项目鸿蒙&#xff08;HarmonyOS&#xff09;资源引用&#xff08;自定统和系统&#xff09; 一、操作环境 操作系统: Windows 10 专业版 IDE:DevEco Studio 3.1 SDK:HarmonyOS 3.1 二、资源访问 HarmonyOS应用资源分为两类&#xff0c;一类是应用资源&…

从最近爆火的ChatGPT,我看到了电商的下一个形态

爆火的ChatGPT似乎让每个行业有了改造的可能性&#xff0c;电商行业也不例外。 在讨论了很多流量红利消失的话题后&#xff0c;我们看到互联网电商行业不再性感&#xff0c;从淘宝天猫&#xff0c;京东&#xff0c;到拼多多&#xff0c;再到抖音&#xff0c;快手&#xff0c;电…