vue3.0后的学习proxy笔记(觉得和reflect一起对比学习效果更佳)

      最近看相关报道,vue3.0 开发计划 传送门 https://juejin.im/post/5bb719b9f265da0ab915dbdd

其中,监测机制:

一句话介绍:更加全面、精准、高效;更具可调试性的响应跟踪;以及可用来创建响应式对象的 API。

3.0 将带来一个基于 Proxy 的 observer 实现,它可以提供覆盖语言 (JavaScript——译注) 全范围的响应式能力,消除了当前 Vue 2 系列中基于 Object.defineProperty 所存在的一些局限,如:

  • 对属性的添加、删除动作的监测
  • 对数组基于下标的修改、对于 .length 修改的监测
  • 对 Map、Set、WeakMap 和 WeakSet 的支持

另外这个新的 observer 还有以下特性:

  • 公开的用于创建 observable (即响应式对象——译注) 的 API。这为小型到中型的应用提供了一种轻量级的、极其简单的跨组件状态管理解决方案。(译注:在这之前我们可以通过另外 new Vue({data : {...}}) 来创建这里所谓的 observable;另外,其实 vuex 内部也是用这种方式来实现的)
  • 默认为惰性监测(Lazy Observation)。在 2.x 版本中,任何响应式数据,不管它的大小如何,都会在启动的时候被监测。如果你的数据量很大的话,在应用启动的时候,这就可能造成可观的性能消耗。而在 3.x 版本中,只有应用的初始可见部分所用到的数据会被监测,更不用说这种监测方案本身其实也是更加快的。
  • 更精准的变动通知。举个例子:在 2.x 系列中,通过 Vue.set 强制添加一个新的属性,将导致所有依赖于这个对象的 watch 函数都会被执行一次;而在 3.x 中,只有依赖于这个具体属性的 watch 函数会被通知到。
  • 不可变监测对象(Immutable observable):我们可以创建一个对象的“不可变”版本,以此来阻止对他的修改——包括他的嵌套属性,除非系统内部临时解除了这个限制。这种机制可以用来冻结传递到组件属性上的对象和处在 mutation 范围外的 Vuex 状态树。
  • 更良好的可调试能力:通过使用新增的 renderTrackedrenderTriggered 钩子,我们可以精确地追踪到一个组件发生重渲染的触发时机和完成时机,及其原因

那么 proxy 到底是什么 该怎么使用呢???

target是指代理的原对象,它是你需要拦截访问的原始对象,它总是作为Proxy构造器的第一个方法,也可以传递到每个trap中。

handler是一个包含你要进行拦截和处理的对象,也就是你拦截了原始对象以后想干什么?主要的代理内容在这里,是作为Proxy构造器的第二个方法参数传统,它是实现Proxy API。

trap用来规定对于指定什么方法进行拦截处理,如果你想拦截get方法的调用,那么你要定义一个get trap。

let  obj = { name:"bruce",age:"25"}

let handler = {
//get运算符有两个参数 - 对象本身和被访问的属性。
    get:function(target,prop){
       console.log('target:'+target+"prop:"+prop);
    },
//set操作符有三个参数 - 对象本身,被访问的属性和为该属性设置的值。
    set:function(target,prop,value){
       if(typeof(value) == 'string'){ //用来作 类型检验
    
       }
       console.log('target:'+target+"prop:"+prop+"value:"+value);
    }
}

let proxy = new Proxy(obj,handler)

proxy.name //打印 target:[object Object]prop:name

proxy.name = 333 //打印 target:[object Object]prop:namevalue:333

    proxy与设计模式

     在面向对象的编程中,代理模式的合理使用能够很好的体现下面两条原则:

  • 单一职责原则: 面向对象设计中鼓励将不同的职责分布到细粒度的对象中,Proxy 在原对象的基础上进行了功能的衍生而又不影响原对象,符合松耦合高内聚的设计理念。

  • 开放-封闭原则:代理可以随时从程序中去掉,而不用对其他部分的代码进行修改,在实际场景中,随着版本的迭代可能会有多种原因不再需要代理,那么就可以容易的将代理对象换成原对象的调用

    Proxy实现前端中3种代理模式的使用场景,分别是:缓存代理验证代理实现私有属性

1.0.0 缓存代理

//计算斐波那契数列  40以上很慢
const getFib = (number) => {
  if (number <= 2) {
    return 1;
  } else {
    return getFib(number - 1) + getFib(number - 2);
  }
}

//创建缓存代理的工厂函数

const getCacheProxy = (fn, cache = new Map()) => {
  return new Proxy(fn, {
    apply(target, context, args) {
      const argsString = args.join(' ');
       console.log(args)
      if (cache.has(argsString)) {
        console.log(argsString)
        // 如果有缓存,直接返回缓存数据
        console.log(`输出${args}的缓存结果: ${cache.get(argsString)}`);
        return cache.get(argsString);
      }
      const result = fn(...args);
      cache.set(argsString, result);
      return result;
    }
  })
}

//使用
const getFibProxy = getCacheProxy(getFib)
getFibProxy(40); // 102334155
getFibProxy(40); // 输出40的缓存结果: 102334155


1.0.1 验证代理

// 表单对象

const userForm = {
  account: '',
  password: '',
}

// 验证方法
const validators = {
  account(value) {
    // account 只允许为中文
    const re = /^[\u4e00-\u9fa5]+$/;

    return {
      valid: re.test(value),
      error: '"account" is only allowed to be Chinese'
    }
  },

  password(value) {
    // password 的长度应该大于6个字符
    return {
      valid: value.length >= 6,
      error: '"password "should more than 6 character'
    }
  }
}


// 校验器
const getValidateProxy = (target, validators) => {

  return new Proxy(target, {

    _validators: validators,

    set(target, prop, value) {

      if (value === '') {

        console.error(`"${prop}" is not allowed to be empty`);

        return target[prop] = false;

      }

      const validResult = this._validators[prop](value);

      if(validResult.valid) {

        return Reflect.set(target, prop, value);

      } else {

        console.error(`${validResult.error}`);

        return target[prop] = false;

      }

    }

  })

}
// 使用
const userFormProxy = getValidateProxy(userForm, validators);
userFormProxy.account = '123'; // "account" is only allowed to be Chinese
userFormProxy.password = 'he'; // "password "should more than 6 character
扫描二维码关注公众号,回复: 4542628 查看本文章

1.0.2 实现私有属性 (设置访问限制)

function getPrivateProps(obj, filterFunc) {

  return new Proxy(obj, {
    get(obj, prop) {
      if (!filterFunc(prop)) {
        let value = Reflect.get(obj, prop);
        // 如果是方法, 将this指向修改原对象
        if (typeof value === 'function') {
          value = value.bind(obj);
        }
        return value;
      }
    },

    set(obj, prop, value) {

      if (filterFunc(prop)) {
        throw new TypeError(`Can't set property "${prop}"`);
      }
      return Reflect.set(obj, prop, value);
    },

    has(obj, prop) {
      return filterFunc(prop) ? false : Reflect.has(obj, prop);
    },

    ownKeys(obj) {
      return Reflect.ownKeys(obj).filter(prop => !filterFunc(prop));
    },

    getOwnPropertyDescriptor(obj, prop) {
      return filterFunc(prop) ? undefined : Reflect.getOwnPropertyDescriptor(obj, prop);
    }

  });

}
//因为私有属性 一般以 _开头 这里就简单过滤
function propFilter(prop) {
  return prop.indexOf('_') === 0;
}

参考资料:https://www.imooc.com/article/47896

http://es6.ruanyifeng.com/#docs/proxy 

https://www.cnblogs.com/diligenceday/p/5474126.html      

猜你喜欢

转载自blog.csdn.net/Bruce__taotao/article/details/84888974