最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue3 响应式原理简单实现

    正文概述 掘金(HJM515)   2020-12-14   699

    Vue3 使用 Proxy 对象重写响应式系统。相比 Vue2有以下不同:

    • 多层属性嵌套,在访问属性过程中才会处理下一级属性(响应式性能提升)
    • 默认监听动态添加的属性
    • 默认监听属性的删除操作
    • 默认监听数组索引和length属性
    • 可以作为单独的模块使用

    响应式系统关键API:

    • reactive: 响应式处理对象或数组
    • ref:响应式处理基本类型数据
    • toRefs:将proxy代理的对象的所有属性值,都变成响应式数据

    reactive

    • 参数只能是对象或数组 (简单类似使用ref)
    • 修改属性,响应式。重新赋值,不是响应式,需要再次使用 reactive 处理
    • 得到的响应式对象不能解构(解构用toRefs)
    /**
     * 响应式处理对象或数组
     * 1. 判断参数,如果不是对象或数组直接返回。
     * 2. proxy 代理 target。
     *    get 收集依赖,如果属性值是对象,需要递归处理。返回处理后的属性值。
     *    set 属性值变化,触发更新。如果新属性值是对象,需递归处理。返回boolean。
     *    deleteProperty 对象属性存在,成功删除后,触发更新。返回boolean。
     * @param { object, array } target
     * @return { proxy }
     */
    
    const isObject = (val) => val !== null && typeof val === "object";
    const isArray = (val) =>
      Object.prototype.toString.call(val) === "[object Array]";
    const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
    
    const convert = (target) => (isObject(target) ? reactive(target) : target);
    
    export function reactive(target) {
      if (!isObject(target)) {
        return target;
      }
    
      const handler = {
        get(target, key, receiver) {
          // 收集依赖
          track(target, key);
          const result = Reflect.get(target, key, receiver);
          return convert(result);
        },
        set(target, key, value, receiver) {
          const oldValue = Reflect.get(target, key, receiver);
          if (value === oldValue) return true;
    
          const result = Reflect.set(target, key, convert(value), receiver);
          // 触发更新
          trigger(target, key);
          return result;
        },
        deleteProperty(target, key, receiver) {
          const hasKey = hasOwn(target, key);
          const result = Reflect.deleteProperty(target, key, receiver);
          if (hasKey && result) {
            // 触发更新
            trigger(target, key);
            console.log("delete", key);
          }
          return result;
        },
      };
    
      return new Proxy(target, handler);
    }
    
    /**
     * watchEffect 底层调用的方法
     * 在callback() 访问响应式对象属性,收集依赖
     * @param { function } callback
     */
    let activeEffect = null;
    export function effect(callback) {
      activeEffect = callback;
      callback();
      activeEffect = null;
    }
    
    /**
     * 收集依赖
     * @param {object, array} target
     * @param {string} key
     */
    let targetMap = new WeakMap();
    export function track(target, key) {
      if (!activeEffect) return;
      let depsMap = targetMap.get(target);
      if (!depsMap) {
        depsMap = new Map();
        targetMap.set(target, depsMap);
      }
      let dep = depsMap.get(key);
      if (!dep) {
        dep = new Set();
        depsMap.set(key, dep);
      }
      dep.add(activeEffect);
    }
    
    /**
     * 触发更新
     * 先根据target、key找到回调集合,遍历执行。
     * @param {object, array} target
     * @param {string} key
     */
    export function trigger(target, key) {
      const depsMap = targetMap.get(target);
      if (!depsMap) return;
      const dep = depsMap.get(key);
      if (!dep) return;
      dep.forEach((cb) => {
        cb();
      });
    }
    

    ref

    • 参数为基础类型,内部创建具有value属性的对象,value属性具有getter和setter。
    • 参数为对象,就相当于reactive(obj) 返回代理对象
    • 返回的对象,重新赋值成对象,也是响应式的。
    /**
     * 对原始数据类型进行响应式处理
     * 1. 如果是带有__v_isRef的对象,直接返回
     * 2. 使用convert对raw进行递归的reactive处理,得到value
     * 3. 返回的对象,__v_isRef = true,value属性 获取会收集依赖,设置会触发更新。
     * @param {string, boolean, number} raw
     * @return {object}
     */
    export function ref(raw) {
      if (isObject(raw) && row.__v_isRef) return;
    
      let value = convert(raw);
    
      const r = {
        __v_isRef: true,
        get value() {
          track(r, "value");
          return value;
        },
        set value(newValue) {
          if (newValue !== value) {
            value = convert(newValue);
            trigger(r, "value");
          }
        },
      };
    
      return r;
    }
    

    toRefs

    • 参数为 proxy代理的对象
    • 将proxy代理的对象的所有属性值,都变成响应式数据。可解构。
    /**
     * 将响应式对象所有属性,变成ref
     * @param {*} proxy
     */
    export function toRefs(proxy) {
      const result = isArray(proxy) ? [] : {};
      for (let key in proxy) {
        result[key] = toRef(proxy, key);
      }
      return result;
    }
    
    /**
     * 将 响应式对象 指定的属性,变成ref
     * 1. 返回的对象解构与ref返回的类似
     * 2. 无需收集依赖和触发更新,是因为 proxy 本来是响应式的
     * @param {proxy} proxy proxy实例
     * @param {string} key
     */
    export function toRef(proxy, key) {
      return {
        __v_isRef: true,
        get value() {
          return proxy[key];
        },
        set value(newValue) {
          proxy[key] = newValue;
        },
      };
    }
    

    起源地下载网 » Vue3 响应式原理简单实现

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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