最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue 数组操作及源码分析

    正文概述 掘金(聒噪的观月叶蝉)   2021-03-20   525

    Vue 在操作数组时的问题

    Vue 中我们在操作数组时会遇到一些问题,明明数据已经被修改了,但是为什么页面没有刷新呢?对于这个问题官方文档中已经给出答案,检测变化的注意事项

    Vue 数组操作及源码分析

    Vue 数组操作及源码分析

    7 个方法的包装

    Vue 将 Array 的 7 个方法进行了包装,使用这些方法操作数组可以出发页面的刷新。

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

    7 个方法的使用

    • push() 方法用于向数组末尾添加元素,可添加一个或多个,返回值为数组 length

    • pop() 方法用于删除数组末尾元素,返回值为删除的元素

    • shift() 方法用于删除数组第一个元素,返回值为删除的元素

    • unshift() 方法用于像数组首部添加元素,可添加一个或多个,返回值为数组 length

    • splice() 方法用于删除、添加、替换元素,

      • arrayObject.splice(index,howmany,item1,.....,itemX) 第一个参数 index 为其实下标,第二个参数 howmany 为删除个数,第三个及之后参数为像数组添加的元素

      • 如果只传第一个元素,则删除下标 index 后的所有元素

      • 如果传两个参数,则删除起始下标 index 开始之后的 howmany 个元素

      • 如果传三个参数,则删除起始下标 index 开始之后的 howmany 个元素,添加 items 元素

    • sort() 方法用于对数组排序,不传参数则按照字符编码排序,也可传入一个有两个形参的回调函数,然后根据返回值排序,(此处将回调函数的参数命名为 a 和 b)

      • 若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
      • 若 a 等于 b,则返回 0。
      • 若 a 大于 b,则返回一个大于 0 的值。
    • reverse() 方法用于颠倒数组中元素的顺序

    7 个数组方法的实现原理

    这 7 个方法能够出发页面的刷新并不是因为他原本就能够触发,而是在 Vue 中对这 7 个方法做了特殊的逻辑处理,其实现方式就是对这 7 个方法进行了包装

    .\src\core\observer\array.js

    import { def } from '../util/index'
    
    // 存储 Array 的 prototype 对象
    const arrayProto = Array.prototype
    // 创建原型指向 arrayProto 对象的 arrayMethods 对象
    export const arrayMethods = Object.create(arrayProto)
    
    // 定义 7 个方法名称的数组
    const methodsToPatch = [
      'push',
      'pop',
      'shift',
      'unshift',
      'splice',
      'sort',
      'reverse'
    ]
    
    // 遍历 7 个方法名称的数组
    methodsToPatch.forEach(function (method) {
      // 存储 Array 原本的原型方法,放在这里取是因为只需要取一次,利用闭包以空间换时间
      const original = arrayProto[method]
      // def 就是对 defineProperty 的简单包装,将我们包装后的方法添加到 arrayMethods 对象中
      def(arrayMethods, method, function mutator (...args) {
        // 先调用 Array 原本的原型方法,进行方法原有的逻辑处理,然后再处理添加的逻辑
        const result = original.apply(this, args)
        // __ob__ 就是 Dep 对象
        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)
        // 通知 watcher 继而更新视图
        ob.dep.notify()
        // 返回原方法的返回值
        return result
      })
    })
    

    可以看出,这里暴露了原型为 Array.prototypearrayMethods 对象,在这个对象中我们对 Array 中这 7 个方法进行了包装。

    那我们包装完了,它又是如何修改我们的在 data 中的数组对象的呢,原因就是他在对数组对象进行响应式化时对其方法进行了修改,修改分为两种情况,

    1. 在我们的环境中对象拥有 __ob__ 属性时便直接将数组的原型指向了我们的 arrayMethods 对象,即在原型链中添加了一层,也就是我们的arrayMethods,当数组对象调用这 7 个方法时,在对象中找不到,便在原型对象中找,找到便直接调用了
    2. 如果没有 __ob__ 对象,则直接将这 7 个方法定义到我们的数组对象中去

    .\src\core\observer\index.js

    export class Observer {
      value: any;
      dep: Dep;
      vmCount: number;
    
      constructor (value: any) {
        this.value = value
        this.dep = new Dep()
        this.vmCount = 0
        def(value, '__ob__', this)
        if (Array.isArray(value)) {
          if (hasProto) {
            // 如果有 __ob__ 对象则更改原型链
            protoAugment(value, arrayMethods)
          } else {
            // 否则直接在数组对象中定义方法
            copyAugment(value, arrayMethods, arrayKeys)
          }
          this.observeArray(value)
        } else {
          this.walk(value)
        }
      }
    
      walk (obj: Object) {
        ...
      }
      
      observeArray (items: Array<any>) {
        for (let i = 0, l = items.length; i < l; i++) {
          observe(items[i])
        }
      }
    }
    
    // 修改原型链
    function protoAugment (target, src: Object) {
      target.__proto__ = src
    }
    
    // 将 7 个方法定义到数组对象中
    function copyAugment (target: Object, src: Object, keys: Array<string>) {
      for (let i = 0, l = keys.length; i < l; i++) {
        const key = keys[i]
        def(target, key, src[key])
      }
    }
    

    图中可以看出 books 原型链中添加了我们的 arrayMethods

    Vue 数组操作及源码分析

    使用 Vue.set 操作数组

    Vue 数组操作及源码分析

    从源码中可以看出,使用 set 方法操作数组其实就是调用了 splice 方法(我们修改之后的 splice 方法)

    .\src\core\observer\index.js

    export function set (target: Array<any> | Object, key: any, val: any): any {
      if (Array.isArray(target) && isValidArrayIndex(key)) {
        target.length = Math.max(target.length, key)
        target.splice(key, 1, val)
        return val
      }
    }
    

    Vue3 中的数组操作

    Vue3 的响应式原理是使用了 proxy ,不同于 Vue2 中使用的 defineProperty ,不存在上述问题,可以通过下标修改数组,也可以直接修改数组 length


    起源地下载网 » Vue 数组操作及源码分析

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元