• 技术文章 >web前端 >Vue.js

    聊聊Vue中$set是如何实现的?

    青灯夜游青灯夜游2022-12-15 21:26:32转载205

    在日常开发中,$set的也是一个非常实用的API,因为Vue2实现响应式的核心是利用了ES5的Object.defineProperty,当我们通过直接修改数组下标更改数组或者给对象添加新的属性,这个时候Object.defineproperty是监听不到数据的变化的,这时候大家就会用上$set,让修改的操作也实现响应,我们知其然更要知其所以然,接下来看一下Vue中的$set是如何实现的。【相关推荐:vuejs视频教程web前端开发

    应用场景

     let dataArr = ["item1"];
     let dataObject = {
          name: "ccs"
        };
    
        dataArr[2] = "item2";
        dataObject.age = 22;
        响应失败,页面没有显示更新新增的数据
    
        this.$set(this.dataArr,2,'item2')
        this.$set(this.dataObject,'age',22)
        响应成功,页面显示更新新增的数据

    set实现

    接下来我们看一下$set在Vue中的定义

    function set(target: Array<any> | Object, key: any, val: any): any {
      if (
        process.env.NODE_ENV !== "production" &&
        (isUndef(target) || isPrimitive(target))
      ) {
        warn(
          `Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`
        );
      }
      if (Array.isArray(target) && isValidArrayIndex(key)) {
        target.length = Math.max(target.length, key);
        target.splice(key, 1, val);
        return val;
      }
      if (key in target && !(key in Object.prototype)) {
        target[key] = val;
        return val;
      }
      const ob = (target: any).__ob__;
      if (target._isVue || (ob && ob.vmCount)) {
        process.env.NODE_ENV !== "production" &&
          warn(
            "Avoid adding reactive properties to a Vue instance or its root $data " +
              "at runtime - declare it upfront in the data option."
          );
        return val;
      }
      if (!ob) {
        target[key] = val;
        return val;
      }
      defineReactive(ob.value, key, val);
      ob.dep.notify();
      return val;
    }

    在源码中首先判断set的目标是否是undefined基本类型如果是undefined基本类型就报错,
    因为用户不应该往undefined和基本类型中set东西
    然后又判断了目标是否是数组与key是不是合法的index,合法的index是指值为大于等于0的整数,
    如果两个条件都成立就对目标数组调用splice方法插入或者修改数组
    这里的splice不是普通的splice是王维诗里的splice,是被vue代理重写过的splice

    数组实现响应

    $set实现数组修改响应的方式是代理重写的数组的一部分方法,接下来我们看一下具体实现

    const arrayProto = Array.prototype
    export const arrayMethods = Object.create(arrayProto)
    
    const methodsToPatch = [
      'push',
      'pop',
      'shift',
      'unshift',
      'splice',
      'sort',
      'reverse'
    ]
    function def(obj, key, val, enumerable) {
        Object.defineProperty(obj, key, {
            value: val,
            enumerable: !!enumerable,
            writable: true,
            configurable: true
        });
    }
    methodsToPatch.forEach(function (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)
        ob.dep.notify()
        return result
      })
    })

    vue中代理重写的不只是splice,有push、pop、shift、unshift、splice、sort、reverse这七个方法, 首先执行了const result = original.apply(this, args)执行原本数组的方法并获取它的值,接下来判断如果是往数组中添加值就将新添加的值也实现响应式,
    最后一步拿到这个数组的_ob_对象_ob_里的dep进行派发更新。
    想深入了解vue的响应式可以查阅往期文章
    面试官问你Vue2的响应式原理,你怎么答? - 掘金 (juejin.cn)

    对象实现响应

    $set中下半部分的逻辑就是用来处理对象响应的,我们接着往下看

      if (key in target && !(key in Object.prototype)) {
        target[key] = val;
        return val;
      }
      const ob = (target: any).__ob__;
      if (!ob) {
        target[key] = val;
        return val;
      }
      defineReactive(ob.value, key, val);
      ob.dep.notify();
      return val;

    首先判断了属性如果在目标对象中直接return结束逻辑,
    因为vue只有添加目标对象中原本没有的属性时才会失去响应
    例如 let obj={} obj.name='ccs'
    vue在初始化的时候会将data里的所有属性都变成响应式,如果的值是对象或者数组则会new一个Observer实例储存在__ob__,想深入了解vue的响应式可以查阅往期文章
    面试官问你Vue2的响应式原理,你怎么答? - 掘金 (juejin.cn)
    拿到这个对象的_ob_进行判断,如果不存在就说明是未经过vue初始化的普通对象而不是响应式对象否则就手动通过defineReactive为属性添加get方法与set方法实现响应
    然后手动调用dep里的notify()发布更新。

    总结

    vue中$set方法对数组和对象的处理本质上的一样的,对新增的值添加响应然后手动触发派发更新。

    (学习视频分享:vuejs入门教程编程基础视频

    以上就是聊聊Vue中$set是如何实现的?的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    专题推荐:Vue.js
    上一篇:聊聊对vue内置组件keep-alive的理解 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • vue中v-bind有什么用• vue的导航钩子有哪几个• vscode写vue没有高亮语法怎么办• vue的导航链接组件是什么• vue中组件化和模块化有什么区别
    1/1

    PHP中文网