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

  • 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.rhkb.cn/news/218302.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于JSP+Servlet+Mysql的建设工程监管信息

基于JSPServletMysql的建设工程监管信息 一、系统介绍二、功能展示1.企业信息列表2.录入项目信息3.项目信息列表 四、其它1.其他系统实现五.获取源码 一、系统介绍 项目名称&#xff1a;基于JSPServlet的建设工程监管信息 项目架构&#xff1a;B/S架构 开发语言&#xff1a;…

ubuntu pycharm 死机,如何重启

1. 找出pycharm 进程的id 进入命令行&#xff1a; ps -ef 是查看当前运行的进程 值输入 ps -ef 会返回所有当前执行的进程&#xff0c;太多了&#xff0c;过滤一下&#xff0c;找到 pycharm : ps -ef | grep pycharm 2. 使用 kill -s 9 来杀死进程 如图所是&#xff0c;…

HTML基础标签

但实际上无论声明为中文还是英文都可以写&#xff0c;中文/英文 主要是浏览器在进行调用翻译功能的时候&#xff0c;会按照声明的语言来进行翻译。 标签语义&#xff1a; 标签的属性一般都是在第一个标签中定义该标签效果所拥有的属性。 即标签的作用是什么 <>标签功能…

美食大赛的题解

目录 原题描述&#xff1a; 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 样例输入&#xff1a; 样例输出&#xff1a; 数据规模&#xff1a; 题目大意&#xff1a; 主要思路&#xff1a; 注&#xff1a; 代码&#xff1a; 原题描述&#xff1a…

C# WPF上位机开发(crc校验)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 为了验证数据传输的过程中有没有发生翻转&#xff0c;我们在传输报文的同时一般还会添加一个crc校验。对于modbus协议也是一样&#xff0c;它在数据…

Unity中Shader URP最简Shader框架(整理总结篇)

文章目录 前言一、精简 ShaderGraph 所有冗余代码后的最简 URP Shader二、我们来对比一下 URP Shader 与 BuildInRP Shader 的对应关系 与 区别1、"RenderPipeline""UniversalPipeline"2、面片剔除、深度测试、深度写入、颜色混合 和 BRP 下一致3、必须引入…

maven工程中读取resources中的资源文件

maven工程的代码布局如下&#xff1a;在resources下面有一个资源文件test.properties&#xff0c;现在的目标要在Java代码中读取该资源文件中的内容。 test.properties资源文件的内容如下&#xff1a; Java代码如下&#xff1a; package com.thb;import java.io.BufferedR…

github 学习番外篇

我们可以按照仓库开始的提示提交仓库 不知道为什么 出现了 我用 git branch 查看了一下&#xff0c;竟然没发现分支 后来发现是只有commit以后才会显示这个分支 后来显示 这是因为本地和远程仓库不同步的原因 这时候我们就需要git pull 一下 发现两个仓库由于不关联不能git…

【算法】【动规】乘积为正数的最长子数组长度

跳转汇总链接 &#x1f449;&#x1f517;算法题汇总链接 1.1 乘积为正数的最长子数组长度 &#x1f517;题目链接 给你一个整数数组 nums &#xff0c;请你求出乘积为正数的最长子数组的长度。 一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。 请你返回乘积…

生产派工自动化:MES系统的关键作用

随着制造业的数字化转型和智能化发展&#xff0c;生产派工自动化成为了提高生产效率、降低成本&#xff0c;并实现优质产品生产的关键要素之一。制造执行系统&#xff08;MES&#xff09;在派工自动化中发挥着重要作用&#xff0c;通过实时数据采集和智能调度&#xff0c;优化生…

人工智能联盟的首件神兵利器——“Purple Llama” 项目,旨为保护工智能模型安全性

Meta公司&#xff08;Meta Platform Inc&#xff09;&#xff0c;原名Facebook&#xff0c;创立于2004年2月4日&#xff0c;市值5321.71亿美元。总部位于美国加利福尼亚州门洛帕克。 Meta 公司推出了名为“Purple Llama”的项目&#xff0c;旨在保护和加固其开源人工智能模型。…

详细了解stm32---按键

提示&#xff1a;永远支持知识文档免费开源&#xff0c;喜欢的朋友们&#xff0c;点个关注吧&#xff01;蟹蟹&#xff01; 目录 一、了解按键 二、stm32f103按键分析 三、按键应用 一、了解按键 同学们&#xff0c;又见面了o(*&#xffe3;▽&#xffe3;*)ブ&#xff0c;最…

高级前端开发工程师

岗位需求 熟练掌握前端主流框架Vue、React、Angular,至少熟练掌控Vue全家桶 文章目录 岗位需求前言一、Vue框架二、React框架三、Angular框架四、什么是Vue全家桶前言 -那就看你表哥的电脑里有没有硬盘 -我不敲键盘 一、Vue框架 Vue(读音为/vjuː/,类似于"view"…

Jenkins离线安装部署教程简记

前言 在上一篇文章基于Gitee实现Jenkins自动化部署SpringBoot项目中&#xff0c;我们了解了如何完成基于Jenkins实现自动化部署。 对于某些公司服务器来说&#xff0c;是不可以连接外网的&#xff0c;所以笔者专门整理了一篇文章总结一下&#xff0c;如何基于内网直接部署Jen…

gitlab下载,离线安装

目录 1.下载 2.安装 3.配置 4.启动 5.登录 参考&#xff1a; 1.下载 根据服务器操作系统版本&#xff0c;下载对应的RPM包。 gitlab官网&#xff1a; The DevSecOps Platform | GitLab rpm包官网下载地址: gitlab/gitlab-ce - Results in gitlab/gitlab-ce 国内镜像地…

Redis第1讲——入门简介

Java并发编程的总结和学习算是告一段落了&#xff0c;这段时间思来想去&#xff0c;还是决定把Redis再巩固和学习一下。毕竟Redis不论是在面试还是实际应用中都是极其重要的&#xff0c;在面试中诸如Redis的缓存问题、热key、大key、过期策略、持久化机制等&#xff1b;还有在实…

武林风云之linux组软raid0

小y可喜欢玩文明系列的游戏了&#xff0c;因为小y也一直喜欢造轮子&#xff0c;属于自己的轮子。 每次小y听到”要向雄鹰一样&#xff0c;定要遨游于天际。”感觉自己给自己打了一针强心剂&#xff0c;要求自己拼搏进取。 众所周知&#xff0c;文明是个原生的linux游戏&#xf…

助力工业产品质检,基于yolov5l集成CBAM注意力机制开发构建智能PCB电路板质检分析系统

AI助力工业质检智能生产制造已经有很多成功的实践应用了&#xff0c;在我们前面的系列博文中也有很多对应的实践&#xff0c;感兴趣的话可以自行移步阅读前面的博文即可&#xff0c;这里本文的核心目的就是想要基于改进的yolov5l来开发构建用于PCB电路板智能检测分析的模型&…

ssm+vue的高校智能培训管理系统分析与设计(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的高校智能培训管理系统分析与设计&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09…

OkHttp: 拦截器和事件监听器

文章目录 1. 拦截器1. 拦截器链2. 实际案例1. 注册为应用拦截器2. 注册为网络拦截器 3. 如何选择用哪种拦截器1. 应用拦截器2. 网络层拦截器3. 重写请求4. 重写响应 4. 可用性 2. 事件监听器1. 请求的生命周期2. EventListener使用案例3. EventListener.Factory4. 调用失败的请…