XingYun blog
  • JS基础

    • 图解js原型链
    • JS Event Loop
    • 对象的底层数据结构
    • 让你的JavaScript代码简单又高效
    • 函数参数按值传递
    • 判断数据类型
    • 浮点数精度问题和解决办法
    • 常用方法snippet
    • 实现Promise
    • 防抖和节流
    • 巧用sort排序
  • CSS && HTML

    • CSS也需要性能优化
    • class命名规范
    • em、px、rem、vh、vw 区别
    • CSS揭秘阅读笔记
  • 浏览器

    • 浏览器是如何渲染页面的
    • 重排和重绘
    • BOM浏览器对象模型
    • DOM事件
    • 浏览器存储
  • 数据结构

    • JS实现链表
    • JS实现栈与栈应用
    • JS实现常见排序
    • 哈夫曼编码
    • MD5算法
  • vue原理浅析

    • Vue虚拟dom与Diff算法
    • 前端打包文件的缓存机制
    • vue数组为什么不是响应式
    • v-for为什么不能用index做key
  • 前端工程化

    • 浏览器是如何渲染页面的
    • 前端打包需要gzip压缩吗
    • 前端打包文件的缓存机制
    • webpack loader和plugin
  • 轮子&&组件库

    • 实现水波浪进度球
  • 文字转语音mp3文件
  • 文件上传前后端实现
  • moment.js给定时间获取自然月、周的时间轴
  • 实现文件上传功能
  • 批量下载照片
  • leaflet改变坐标原点
  • 网络

    • 有了MAC地址 为什么还需要IP地址
    • 为什么IP地址老是变
    • 我们为什么需要IPV6
    • TCP与UDP
  • 计算机组成原理

    • ASCII、Unicode、UTF-8和UTF-16
  • VSCode

    • VSCode图片预览插件 Image preview
    • rsync:linux间的高效传输工具

XingYun

冲!
  • JS基础

    • 图解js原型链
    • JS Event Loop
    • 对象的底层数据结构
    • 让你的JavaScript代码简单又高效
    • 函数参数按值传递
    • 判断数据类型
    • 浮点数精度问题和解决办法
    • 常用方法snippet
    • 实现Promise
    • 防抖和节流
    • 巧用sort排序
  • CSS && HTML

    • CSS也需要性能优化
    • class命名规范
    • em、px、rem、vh、vw 区别
    • CSS揭秘阅读笔记
  • 浏览器

    • 浏览器是如何渲染页面的
    • 重排和重绘
    • BOM浏览器对象模型
    • DOM事件
    • 浏览器存储
  • 数据结构

    • JS实现链表
    • JS实现栈与栈应用
    • JS实现常见排序
    • 哈夫曼编码
    • MD5算法
  • vue原理浅析

    • Vue虚拟dom与Diff算法
    • 前端打包文件的缓存机制
    • vue数组为什么不是响应式
    • v-for为什么不能用index做key
  • 前端工程化

    • 浏览器是如何渲染页面的
    • 前端打包需要gzip压缩吗
    • 前端打包文件的缓存机制
    • webpack loader和plugin
  • 轮子&&组件库

    • 实现水波浪进度球
  • 文字转语音mp3文件
  • 文件上传前后端实现
  • moment.js给定时间获取自然月、周的时间轴
  • 实现文件上传功能
  • 批量下载照片
  • leaflet改变坐标原点
  • 网络

    • 有了MAC地址 为什么还需要IP地址
    • 为什么IP地址老是变
    • 我们为什么需要IPV6
    • TCP与UDP
  • 计算机组成原理

    • ASCII、Unicode、UTF-8和UTF-16
  • VSCode

    • VSCode图片预览插件 Image preview
    • rsync:linux间的高效传输工具
  • 3个提升Vue性能的写法
  • 重读Vue文档
  • moment.js给定时间获取自然月、周的时间轴
  • Vue虚拟dom与Diff算法
  • 深入响应式原理
  • Echart样例
  • 我的Vue指令库
  • 滚动到底部加载更多
  • 实现一键换肤
  • 手写Vue数据劫持
  • vue-router核心原理与手写实现
  • computed和watch
  • vue数组为什么不是响应式
    • v-for为什么不能用index做key
    • webpack loader和plugin
    • keep-alive组件原理
    • vue插槽进化
    • Vue多层嵌套组件
    • vue生命周期hook
    • vue监听dom元素的resize事件
    • 前端打包需要gzip压缩吗
    • 实现水波浪进度球
    • Vue
    XingYun
    2021-11-29
    目录

    vue数组为什么不是响应式

    Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()

    用这些方法操作数组的时候是会触发响应式的

    下面讨论的是除这些方法外的数组修改

    # 一、Vue数组为什么不是响应式

    看官网文档响应式原理 (opens new window) Vue不能检测到以元素赋值方式的数组变动

    由于 JavaScript 的限制,Vue 不能检测以下数组的变动:

    1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
    2. 当你修改数组的长度时,例如:vm.items.length = newLength

    为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新

    // Vue.set
    Vue.set(vm.items, indexOfItem, newValue)
    
    1
    2

    为了解决第二类问题,你可以使用 splice:

    arr.splice(indexOfItem, 1, newValue)
    
    1

    # 二、为什么不把数组做成响应式

    Vue的双向绑定是通过 Object.defineProperty 给对象添加 getter 和 setter 方法实现的。

    var data = {
        name: 'Kikky',
        msg: 'hello'
    }
    
    // 枚举data里的属性并添加getter setter方法
    Object.keys(data).forEach(function(key) {
        Object.defineProperty(data, key, {
            get: function() {
                console.log('trigger subscription')
            },
            set: function() { // 属性变动触发通知
                console.log('trigger notify')
            }
        })
    })
    
    data.msg = 'hello world' // 输出 trigger notify
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    对象可以,那么数组是否也可以呢?试一下

    var array = ['a', 'b']
    
    // 枚举数组各项,试图设置各项的getter,setter,
    for (var i = 0, len = array.length; i < len ;i++) {
        // 数组的index就相当于对象的属性
        Object.defineProperty(array, i, {
            get: function() {
                console.log('trigger subscription')
            },
            set: function() { // 数组项变动触发通知
                console.log('trigger notify')
            }
        })
    }
    
    array[0] = 'x' // // 输出 trigger notify
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    事实证明,是可以通过 array[index] = newValue 这样的方式触发响应的。Vue自身选择的不对数组进行Object.defineProperty的属性劫持,并不是做不到

    那Vue为什么不这样做呢?

    1.试想一下,如果数组的长度不是2个,而是 1000 ,1000个,要是每个元素都这样设置一遍,是不是很笨拙,也很损耗性能?

    2.不仅如此,由于JavaScript的数组是可变的,可以通过 array[index] = value 随时添加数组项

    let arr = [1,2,3]
    arr[5] = 100
    console.log(arr)  // [ 1, 2, 3, <2 empty items>, 100 ]
    
    1
    2
    3

    如上这样的操作不仅增加了arr[5],还增加了两个空元素,这对响应式来说是灾难

    为什么对象可以?

    一般开发中,对象属性多的最多几十个,量级小,而且我们在创建Vue实例的时候,data中的属性是预先定义好了的。Object.defineProperty是针对已有项的设置,新加的项是不会被 Object.definePropert设置的,也就不会触发响应了。

    # 三、触发数组响应式的方法

    除了开头重写的js数组方法外

    使用Vue.$set(arr, index, value)

    Vue.$set(arr, index, value)
    vm.$set(arr, index, value)
    Vue.set(arr, index, value)
    
    1
    2
    3

    这三个方法的写法不同效果相同,都是调用了set函数,set函数中调用了defineReactive(ob.value, key, val),手动劫持arr[index]

    #vue
    上次更新: 2023/04/05, 09:41:10
    computed和watch
    v-for为什么不能用index做key

    ← computed和watch v-for为什么不能用index做key→

    最近更新
    01
    JavaScript-test
    07-20
    02
    二维码的原理
    07-20
    03
    利用ChatGPT优化代码
    07-20
    更多文章>
    Theme by Vdoing | Copyright © 2021-2023 XingYun | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式