likes
comments
collection
share

JS Proxy的应用

作者站长头像
站长
· 阅读数 64

熟悉vue3的人都知道,它的数据响应式是用[JS Proxy](ES6 入门教程)实现的,proxy可以拦截诸如get、set、apply等很多操作,让我们可以通过“钩子”的方式,在这些操作时,做一些我们自己的动作。正好这段时间我们有一个项目应用到了它。

全局码表的使用

这个项目中定义了一些系统共用的常量,我们称为状态码,如下:

const CODE = {
  goods: {
    category: [
      {code: 3088, label:'面膜'},
      {code: 3089, label:'牙膏'},
    ]
  },
  order: {
    status: [
      {code: 10000, label: '待支付'},
      {code: 10100, label: '待采购'},
      {code: 10200, label: '待出库'},
    ]
  }
}

刚开始时,它只有几个状态,随系统就直接下发了。可后来这些状态码越来越多,并且出现了“动态”状态码,也就是不同用户打开系统时,实时计算他的状态码,由异步接口下发。

组件的抽离

因为这个状态表在系统的各处都在使用,我们想到的第一个方案是抽离了出一个公共模块,应用层import 使用就行了。

import ajax from './util/ajax';

let codePromise;
export default async function getDynamicCode{
  if(codePromise) return codePromise;
  codePromise = new Promise(async (resolve)=>{
    const codeMap = await ajax.get('/getDynamicCode');
    resolve(codeMap);
  });
  return codePromise;
}

这样只要在业务逻辑里引用它就可以使用了,全局共享一份,不需要重复申请。

import getDynamicCode from './getDynamicCode';

async function showOrderStatus(statusCode) {
  const CODE = await getDynamicCode();
  const ret = CODE.order.status.filter(item=>item.code==statusCode);
  return ret.length>0 ? ret[0].label : '-';
}

问题的产生

然后,随着业务的发展,大家对状态表的使用频率越来越高,问题也越来越多了。归纳了一下,有以下几个问题:

  1. 因为是动态码表,不同的人看到的码表不一样,有些code只对特殊角色开放,其它角色拿不到,使用的时候,因为码表没有值,就会报错。比如有些人是不发订单状态表的,操作它就会报错
CODE.order.status.filter(item=>item.code==statusCode);

为了解决它,就得每次使用前判断一下

let statusDesc = '';
if(CODE.order && CODE.order.status){
const ret = CODE.order.status.filter(item=>item.code==statusCode);
  statusDesc = ret.length>0 ? ret[0].label : '-';
}
return statusDesc;

因为要到处写这个兼容逻辑就很麻烦。 2. 因为要配合组件使用,经常要把码表格式化成一定结构,传给组件,最常用的是选择列表和字典结构。

// options给下接选择组件使用
const options = CODE.order.status.map(({code:value, label})=>({value, label}));
// dict给表格组件使用
const dict = Object.fromEntries(CODE.order.status.map(({code, label})=>[code, label]));

Proxy的应用

为了解决以上两个问题,我们升级了getDynamicCode组件,使用到了Proxy技术。大致流程如下:

  1. 用Proxy包装CODE,它在“读”的操作加钩子,每次读取前,判断一下要读取的字段是不是存在;
  2. 如果“存在”目标属性,把对应的属性值使用Proxy封装后返回;“不存在”目标属性,把空数组用Proxy封装后返回
  3. 如果属性值是“options”或者”dict”,直接把当前对象按照Options和dict要求的结构格式化后返回。

最终代码如下:

import ajax from './util/ajax';
const proxySetting = {
  get(target, propKey) {
    const isUndefined = typeof target[propKey] === "undefined";
    if (!isUndefined) {
      return new Proxy(target[propKey], proxySetting);
    }
    const isArray = Array.isArray(target);

    if (propKey === "options") {
      if (isArray)
        return target.map(({ code: value, desc: label }) => ({ value, label }));
      else return new Proxy([], proxySetting);
    }
    if (propKey === "dict") {
      if (isArray)
        return Object.fromEntries(target.map(({ code, desc }) => [code, desc]));
      else return new Proxy({}, proxySetting);
    }
    return new Proxy([], proxySetting);
  },
};

let codePromise;
export default async function getDynamicCode{
  if(codePromise) return codePromise;
  codePromise = new Promise(async (resolve)=>{
    const codeMap = await ajax.get('/getDynamicCode');
    const codeProxy = new Proxy(codeMap, proxySetting);
    resolve(codeProxy);
  });
  return codePromise;
}

这样就算读取了一个不存的属性,也不会报错,而是直接返回空数组。

const testCode = Code.category.bottle.glass;

虽然码表中没有它,但可以自由访问,不会触发js报错; 同时,可以直接访问options和dict属性,不用但心它们是否存在。

const columnsConfig = [
    {title: '订单状态', dataIndex:'status', valueEnum: CODE.order.status.dict},
]
转载自:https://juejin.cn/post/7216621821961093176
评论
请登录