从Vue源码来看数组操作

简介: 从Vue源码来看数组操作

文章目录


文章风格

基于研发者心理talk is cheap, show me the code原则,文章可能会涉及大量代码。

学习目标

相信很多人写Vue的时候,如果碰到了这样的情况(本意想要视图更新,但是视图却没有更新的时候),往往会不管三七二十一,直接一波$forceupdate完事,但是再读源码的时候,你就会发现为什么视图没有自动更新了。

学习过程

不用forceUpdate

比如说下面一段代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src='../dist/vue.js'></script>
</head>
<body>
    <div id = 'demo'>
        <p v-for="item in arr" :key='item'>{{item}}</p>
    </div>
    <script>
        const app = new Vue({
            el:'#demo',
            data:{
                arr: [1,2,3,4]
            },
            mounted(){
                /*
                需要强迫更新
                */
                setTimeout(()=>{
                    this.arr[2] = 99;
                    console.log(this.arr)
                   // this.$forceUpdate()
                 },1000)
            }
        })
    </script>
</body>
</html>

如果你把代码粘贴下来自己跑一次的话,你会发现,在打开this.$forceUpdate()之前,虽然在控制台,会打印出变化之后的数组,但是视图却没有更新。如图

那这到底是为什么呢?

  • 如果你读过源码的话,你应该会发现会,在vue2的源码中(src\core\observer\array.js),数组的数据响应式,是这样的。
/*
 * not type checking this file because flow doesn't play well with
 * dynamically accessing methods on Array prototype
 */
import { def } from '../util/index'
// 获取数组原型,备份
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
/**
 * Intercept mutating methods and emit events
 */
// 覆盖七个方法
methodsToPatch.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    // 执行原来的动作
    const result = original.apply(this, args)
    const ob = this.__ob__
    // 如果是插入的操作,还需要额外的响应化处理
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    // 怀疑插入的是一个新数组,对他进行响应式
    if (inserted) ob.observeArray(inserted)
    // notify change  通知watcher更新
    ob.dep.notify()
    return result
  })
})

我们发现,对数组的响应式处理,其实是通过覆盖会改变原数组的值的七个方法进行覆盖,添加新的操作。

简单地来说,就是只要对数组进行进行

'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'

操作就会导致视图更新,或者直接更新整个数组,因为在vue2里面,属性名跟属性值是分离的(个人理解), 为什么这么说呢? 因为在defineReactive里面主要是通过Object.defineProperty的getter以及setter进行属性拦截的,如果你改变数组的其中一个值arr[i],并不会影响到arr对他的值([1,2,3,4])的引用,类似于C系语言的指针,没有发生改变,内存地址不没有发生变化,进一步的寻址不受影响。

简而言之,就是arr[i]的改变,不会触发set,所以视图没有更新(如果你就是想要他更新的话,就得调用forceUpdate进行强制重新渲染,但是,这会影响性能,是毋庸置疑的)。

  • 除此之外,你可能会想到的办法是对原数组就行拷贝,然后重新赋值引用。比如:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src='../dist/vue.js'></script>
</head>
<body>
    <div id = 'demo'>
        <p v-for="item in arr" :key='item'>{{item}}</p>
    </div>
    <script>
        const app = new Vue({
            el:'#demo',
            data:{
                arr: [1,2,3,4]
            },
            mounted(){
                /*
                需要强迫更新
                */
                setTimeout(()=>{
                    let temp = this.arr;
                    temp[2] = 99
                    this.arr = temp
                    console.log(this.arr)
                },1000)
    </script>
</body>
</html>

很遗憾地告诉你,结果是这样的:

因为js对数组就行=是进行的浅拷贝。

调用forceUpdate

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src='../dist/vue.js'></script>
</head>
<body>
    <div id = 'demo'>
        <p v-for="item in arr" :key='item'>{{item}}</p>
    </div>
    <script>
        const app = new Vue({
            el:'#demo',
            data:{
                arr: [1,2,3,4]
            },
            mounted(){
                /*
                需要强迫更新
                */
                setTimeout(()=>{
                    this.arr[2] = 99;
                    console.log(this.arr)
                    this.$forceUpdate()
                },1000)
            }
        })
    </script>
</body>
</html>

一些会自动触发视图更新的办法

  • 七个方法之一
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src='../dist/vue.js'></script>
</head>
<body>
    <div id = 'demo'>
        <p v-for="item in arr" :key='item'>{{item}}</p>
    </div>
    <script>
        const app = new Vue({
            el:'#demo',
            data:{
                arr: [1,2,3,4]
            },
            mounted(){
                setTimeout(()=>{
                    this.arr.push(100)
                },1000)
        })
    </script>
</body>
</html>

  • 深拷贝数组
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src='../dist/vue.js'></script>
</head>
<body>
    <div id = 'demo'>
        <p v-for="item in arr" :key='item'>{{item}}</p>
    </div>
    <script>
        const app = new Vue({
            el:'#demo',
            data:{
                arr: [1,2,3,4]
            },
            mounted(){
                setTimeout(()=>{
                    // 深拷贝
                    let temp = [...this.arr]
                    temp[2] = 9999
                    this.arr = temp
                    console.log(this.arr)
                },3000)
            }
        })
    </script>
</body>
</html>


学习总结

如果在开发过程中,遇到了数组操作

  1. 更新的是数组中间的值,使用深拷贝,或许性能最好
  2. 如果是尾插或者头插一个新的值,那就push,shift
  3. 如果是去掉第一个或者最后一个就用unshift,pop
相关文章
|
1月前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
25天前
|
机器学习/深度学习 人工智能 自然语言处理
Sketch2Lineart:AI绘画工具,自动将手绘草图转换成清晰的线条画
Sketch2Lineart是一款基于人工智能的绘画工具,能够自动将手绘草图转换成清晰的线条画。该工具支持多种功能,如草图转线稿、自动描述生成、细节调整和风格定制等,适用于艺术创作、产品设计、教育培训等多个领域。
142 60
Sketch2Lineart:AI绘画工具,自动将手绘草图转换成清晰的线条画
|
1天前
|
人工智能 自然语言处理 语音技术
AI 工具导航网站分享
一个致力于成为您在 AI 世界探索旅程中的可靠伙伴,为您搭建起一座通往便捷与高效的桥梁的网站。
AI 工具导航网站分享
|
7天前
|
人工智能 自然语言处理 搜索推荐
你用过最好用的AI工具是什么?
2023年AI革命风起云涌,众多产品令人振奋。我体验了OpenAI的ChatGPT、微软New Bing、Anthropic Claude等多款AI产品。特别推荐微软New Bing,它集成了强大的搜索引擎和语言模型,回答问题精准,提供最新信息,还能免费进行创意绘图。此外,New Bing还集成了Copilot网页助手,帮助用户高效提取和总结内容。
62 27
你用过最好用的AI工具是什么?
|
1天前
|
人工智能 自然语言处理 JavaScript
Univer:开源全栈 AI 办公工具,支持 Word、Excel、PPT 等文档处理和多人实时协作
Univer 是一款开源的 AI 办公工具,支持 Word、Excel 等文档处理的全栈解决方案。它具有强大的功能、高度的可扩展性和跨平台兼容性,适用于个人和企业用户,能够显著提高工作效率。
29 7
Univer:开源全栈 AI 办公工具,支持 Word、Excel、PPT 等文档处理和多人实时协作
|
6天前
|
机器学习/深度学习 人工智能 算法
X-AnyLabeling:开源的 AI 图像标注工具,支持多种标注样式,适于目标检测、图像分割等不同场景
X-AnyLabeling是一款集成了多种深度学习算法的图像标注工具,支持图像和视频的多样化标注样式,适用于多种AI训练场景。本文将详细介绍X-AnyLabeling的功能、技术原理以及如何运行该工具。
45 2
X-AnyLabeling:开源的 AI 图像标注工具,支持多种标注样式,适于目标检测、图像分割等不同场景
|
14天前
|
编解码 人工智能 监控
VISION XL:支持四倍超分辨率的 AI 视频修复处理工具,提供去除模糊、修复缺失等功能
VISION XL是一款基于潜在扩散模型的高效视频修复和超分辨率工具,能够修复视频缺失部分、去除模糊,并支持四倍超分辨率。该工具优化了处理效率,适合快速处理视频的应用场景。
61 6
VISION XL:支持四倍超分辨率的 AI 视频修复处理工具,提供去除模糊、修复缺失等功能
|
21天前
|
存储 人工智能 数据库
Codel:AI代理工具,支持在终端、浏览器、编辑器执行复杂任务和项目
Codel是一款全自主AI代理工具,支持在终端、浏览器和编辑器中执行复杂任务和项目。它运行在沙盒化的Docker环境中,具备自主操作能力,内置浏览器和文本编辑器,所有操作记录存储于PostgreSQL数据库。Codel能够自动完成复杂任务,如创建项目结构、进行网络搜索等,适用于自动化编程、研究与开发、教育与培训以及数据科学与分析等多个领域。
59 11
Codel:AI代理工具,支持在终端、浏览器、编辑器执行复杂任务和项目
|
6天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
23天前
|
机器学习/深度学习 人工智能 自然语言处理
Voice-Pro:开源AI音频处理工具,集成转录、翻译、TTS等一站式服务
Voice-Pro是一款开源的多功能音频处理工具,集成了语音转文字、文本转语音、实时翻译、YouTube视频下载和人声分离等多种功能。它支持超过100种语言,适用于教育、娱乐和商业等多个领域,为用户提供一站式的音频处理解决方案,极大地提高工作效率和音频处理的便捷性。
97 10
Voice-Pro:开源AI音频处理工具,集成转录、翻译、TTS等一站式服务