2021SC@SDUSC amis代码分析(11)

本文主要分析了amis中的resolveVariable函数,探讨了如何根据路径获取数据,同时介绍了prettyBytes、escapeHtml、formatDuration和makeSorter等实用函数,展现了在ReactJS中处理数据和操作的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2021SC@SDUSC

概述

本周学习amis中怎么泛化地书写将数据从各个对象中取出来的代码以及几个其他值得学习的功能函数。

resolveVariable(path,data)

对于给定的一个路径path,可以分成两类:

// 1
a.b.c | a.b[1].c
// 2
ns: varname

当为第一种的时候,给的是一个属性路径,这需要我们从data中获取各个级别对应的属性值,层层查找,直到最后一级即为目标值。
当为第二种的时候,是一个键值对的形式,ns 是数据存储的对象,varname 是同第一种的属性路径。包括windowlocalStoragesessionStorageCookit以及以下自定义的位置。其实,第一种方式相当于省略了ns = data的条件。

代码分析:

// 获取 data 对象在 Path 路径的变量值
function objectGet(data: any, path: string) {
  if (typeof data[path] !== 'undefined') {
    return data[path]; // 只有一级路径时
  }
  // 将 path 切分成各级路径
  let parts = keyToPath(path.replace(/^{|}$/g, ''));
  return parts.reduce((data, path) => {
    // 迭代的取每一级的路径中的属性值
    if ((isObject(data) || Array.isArray(data)) && path in data) {
      return (data as {[propName: string]: any})[path];
    }
    // 只要有一级没有,总为 undefined
    return undefined;
  }, data);
}

function parseJson(str: string, defaultValue?: any) {
  try {
    return JSON.parse(str);
  } catch (e) {
    return defaultValue;
  }
}

// 在 Cookie 中获取对应的键值 name
function getCookie(name: string) {
  // Cookit 总是以分号分割,结尾不加分号
  const value = `; ${document.cookie}`; // 手动补充一个分号。注意空格!
  const parts = value.split(`; ${name}=`); // 寻找目标属性
  if (parts.length === 2) { // 说明 split 切割成功
    return parts.pop()!.split(';').shift(); // 后面的一个 `;` 之前的部分 // 没有分号同样满足
  }
  return undefined;
}

// 获取 data 在 path 路径下对应的值
export const resolveVariable = (path?: string, data: any = {}): any => {
  if (!path || !data || typeof path !== 'string') {
    return undefined;
  }
  // 从这里就可以推断出 path 的格式
  // ns : varname
  // 其中 ns 是数据存储的位置,varname 是最后的变量名
  let [ns, varname] = path.split(':');
  // 不含有 varname 时,说明不为上述格式
  // path 结构为 a.b.c , 数据存储在 data 中的
  if (!varname && ns) {
    varname = ns;
    ns = '';
  }
  if (ns === 'window') {
    data = window; // window 中
  } else if (ns === 'ls' || ns === 'ss') {
    // ls -> localStorage
    // ss -> sessionStorage
    let parts = keyToPath(varname.replace(/^{|}$/g, '')); // 切割路径
    const key = parts.shift()!; // 第一个为在本地储存的键值
    const raw = // 获取对应值,此时为 string 类型
    // 本地存储不能存储大对象,存储的是 JSON.stringify 后的对象字符串
      ns === 'ss' ? sessionStorage.getItem(key) : localStorage.getItem(key);
    // string 类型,不为 Null
    if (typeof raw === 'string') {
      const data = parseJson(raw, raw); // 重新还原成对象
      if (isObject(data) && parts.length) {
        // 从该对象的中获取 剩下的 parts.join('.') 下的值
        return objectGet(data, parts.join('.'));
      }
      return data;
    }
    return undefined;
    // cookit 中
  } else if (ns === 'cookie') {
    const key = varname.replace(/^{|}$/g, '').trim();
    return getCookie(key);
  }
  // 以下是自定义的另一些标识符
  if (varname === '$$') {
    return data;
  } else if (varname[0] === '$') {
    varname = path.substring(1);
  } else if (varname === '&') {
    return data;
  }
  return objectGet(data, varname);
};

对于我自己来说,对于不同的对象的操作可能会使用不同的类来描述,并且在对应的类中当然还有其他操作。而amis开发人员则是将函数用功能组织起来,一个函数完成多个不同对象的方面的功能,这也是一种较好的方式,看个人习惯即可。而我更喜欢方向对象的开发模式。

prettyBytes(num)

格式化数据大小,将统一用 B做单位的数据大小标识标准化。

// 数据单位
const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

// 格式化数据大小
export const prettyBytes = (num: number) => {
  // isFinite // 判断数值是否为有限的数值 !== inf
  if (!Number.isFinite(num)) {
    throw new TypeError(`Expected a finite number, got ${typeof num}: ${num}`);
  }
  // 存储符号
  const neg = num < 0;

  if (neg) {
    num = -num;
  }
  // 数据小于最小单位 1B
  if (num < 1) {
    return (neg ? '-' : '') + num + ' B';
  }
  const exponent = Math.min(
    Math.floor(Math.log(num) / Math.log(1000)), // 数据含有多少个 10^3,每一个标志着单位的一个等级
    UNITS.length - 1 // 最大单位等级
  );
  // toPrecision 将数据格式化为指定长度,不够就添加,过长就用科学计数法
  // 将 num 化成对应单位的数值
  const numStr = Number((num / Math.pow(1000, exponent)).toPrecision(3));
  const unit = UNITS[exponent]; // 对应单位

  return (neg ? '-' : '') + numStr + ' ' + unit;
};

escapeHtml

将 HTML 字符串中对应符号转成网页中的形式。要知道的是,在网页中某些符号比如空格,并不是敲敲空格就可以的,而是将其转化成一种特定的格式。

const entityMap: {
  [propName: string]: string;
} = { // 网页中对应符号的 HTML 中的形式
  '&': '&amp;',
  '<': '&lt;',
  '>': '&gt;',
  '"': '&quot;',
  "'": '&#39;',
  '/': '&#x2F;'
};
// 将 HTML 字符串中对应符号转成网页中的形式
export const escapeHtml = (str: string) =>
  String(str).replace(/[&<>"'\/]/g, function (s) {
    return entityMap[s];
  });

formatDuration

// 将时间段时间戳格式化成标准时间形式
export function formatDuration(value: number): string {
  const unit = ['秒', '分', '时', '天', '月', '季', '年']; // 单位
  const steps = [1, 60, 3600, 86400, 2592000, 7776000, 31104000]; // 各单位对应的秒数
  let len = steps.length;
  const parts = []; // 用于存储每个单位量级对应的时间字符串
  while (len--) {
    // 当时第 len 个单位时,value 超出了1单位数值,就可以将该单位的数值提出来
    if (steps[len] && value >= steps[len]) {
      parts.push(Math.floor(value / steps[len]) + unit[len]);
      value %= steps[len]; // 剩下的秒数
    } else if (len === 0 && value) {
      parts.push((value.toFixed ? value.toFixed(2) : '0') + unit[0]);// 最后部分包含小数在内,都为 B
    }
  }
  return parts.join('');
}

makeSorter

自定义产生排序方法的函数工厂

function makeSorter(
  key: string, // 用于排序的属性
  method?: 'alpha' | 'numerical', // 字典序 | 数值大小
  order?: 'desc' | 'asc' // 降序 | 升序
) {
  // 返回的函数部分 a,b 为其中的两个值
  return function (a: any, b: any) {
    if (!a || !b) { // 含有空值时
      return 0; // 不做处理,即仍然按原顺序
    }

    const va = resolveVariable(key, a); // 获取 a 的 key 路径下的值
    const vb = resolveVariable(key, b);
    let result = 0;
    if (method === 'numerical') { // 数值大小排序
      result = (parseFloat(va) || 0) - (parseFloat(vb) || 0);
    } else { // 字典序排序
      result = String(va).localeCompare(String(vb));
    }
    // -1 为降序
    //  1 为升序
    return result * (order === 'desc' ? -1 : 1);
  };
}

工厂模式,顾名思义,是一种用来批量生产特定函数的工厂函数,它的返回值是一个可以独立运行的函数。根据工厂参数的不同,可以创造出不同功能的函数。比如上面这个函数,生产的函数就可以用于ort排序中,实现不同的排序方法。

let arr = [2,1,3,11,21,321,32];
sort(arr,makeSorter('','alpha','desc'))
// 按降序字典序排列

总结

本周我学习到了如何处理多种对象的同功能函数的内聚(有点软件工程的味道),以及工厂函数的实现。

amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可以节省页面开发工作量,极大提升开发前端页面的效率。 目前在百度广泛用于内部平台的前端开发,已有 100+ 部门使用,创建了 3w+ 页面。 特点: 1、不需要懂前端:在百度内部,大部分 amis 用户之前从来没写过前端页面,也不会 JavaScript,却能做出专业且复杂的后台界面,这是所有其他前端 UI 库都无法做到的; 2、不受前端技术更新的影响:百度内部最老的 amis 页面是 4 年多前创建的,至今还在使用,而当年的 Angular/Vue/React 版本现在都废弃了,当年流行的 Gulp 也被 Webpack 取代了,如果这些页面不是用 amis,现在的维护成本会很高; 3、享受 amis 的不断升级:amis 一直在提升细节交互体验,比如表格首行冻结、下拉框大数据下不卡顿等,之前的 JSON 配置完全不需要修改; 4、可以完全使用可视化页面编辑器 来制作页面:一般前端可视化编辑器只能用来做静态原型,而 amis 可视化编辑器做出的页面是可以直接上线的。 5、提供完整的界面解决方案:其它 UI 框架必须使用 JavaScript 来组装业务逻辑,而 amis 只需 JSON 配置就能完成完整功能开发,包括数据获取、表单提交及验证等功能,做出来的页面不需要经过二次开发就能直接上线; 6、内置 100+ 种 UI 组件:包括其它 UI 框架都不会提供的富文本编辑器、条件组合等,能满足各种页面组件展现的需求,而且对于特殊的展现形式还可以通过 自定义组件 来扩充; 7、容器支持无限级嵌套:可以通过组合来满足各种布局需求; 8、经历了长时间的实战考验:amis 在百度内部得到了广泛使用,在 4 年多的时间里创建了 3 万+ 页面,从内容审核到机器管理,从数据分析到模型训练,amis 满足了各种各样的页面需求,最复杂的页面有超过 1 万行 JSON 配置。   amis前端低代码框架 更新日志: v1.1.7 Feature Wrapper 组件 style 支持动态获取 数据映射支持 cookie 获取 内置 filter 新增 map 方法 Rating 组件支持清空 Tabs 的 activeKey 支持变量 Excel 导出支持自定义文件名 数据映射的 key 可以支持 . 或者 [] 来指定路径 Tree-Selector 支持懒加载 升级 ECharts 到 5.1.1 升级 Monaco-Editor 到 0.24.0 Enhancement 升级 mst 到 3 的最新版本 开发使用 concurrently 避免新开一个窗口 data-link 优化 Wizard 组件新增 startStep 配置项 按钮 tooltip 整理,支持 disabledTip Each 组件空状态时文字居左,同时将空数组状态也认为是空状态 去掉 Tab line 模式下顶部的 padding Uuid 有值时不设置,没值自动设置 TextArea 组件最小行数限制 & 静态展示超出等 Form 远端校验显示报错时,可以再次提交 Nav 的 mapTree 需要 depthFirst Checkboxes 分组样式优化 DateTime-Range下拉增加 popoverClassName 属性,可以自定义弹框的 className; 父级有缩放比时弹框宽度计算问题修复; Date 快捷键支持上月底 autoFill 支持多选 CRUD 的 toolbar 默认不再将最后一个组件放右边 接口兼容多种 json 返回格式 CRUD filterable 判断是否选中不要那么严格 Button-Group disabled 统一使用透明度的方式来实现,不然无法区分选中状态是哪个 调整日期按钮位置顺序 和 Dialog 统一 Bugfix 修复 Audio should not call load method at first render 修复 文档多余描述 修复 CRUD filter Combo模式不能清空查询条件 修复 初始状态 autoFill 不同步的问题 修复 文档样例错误 修复 Audio 组件 src 属性不符合预期的行为 修复 表单联合校验问题 修复 PopOver 宽度计算问题 修复 图片表单项 disabled 影响放大功能的问题 修复 Transfer selectTitle resultTitle 不生效的问题 修复 Tree 组件问题 修复 Fiule 组件错误提示样式问题 修复 Select 组件自定义菜单模式下无法全选问题 修复 Number 最大最小值校验问题 修复 sdk 中 dialog 里的编辑器弹窗被遮挡问题 修复
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值