前端对请求数据做预处理/格式化的方式。

问题描述

在与后台交互的过程中遇到的关于处理数据格式的问题。

问题出现的环境背景及自己尝试过哪些方法

比如:后台传过来一个list,格式如下

list: [
  {
    id: 10086,
    firstName: '小',
    lastName: '明',
    birth: 1546072820279,
    age: 11,
    sex: 0,
    // 其他属性
    ...
  },
  ...
]

然后,我想对数据做预处理,原因有下面两点:

  1. 渲染在页面上之前,需要先对data中的某些项做数据处理。
  2. 防止后端同学修改了字段,如id -> userId,如果不统一在model层做处理,那后期维护就很麻烦,需要在页面中一个个找id,然后换成userId

我的想法是单独创建一个model.js用于统一处理这些数据预处理。

方法1: 类

// model.js
class Person{
  constructor(props){
    // 将原始data的所有的属性做挂载   Question1: 此处印象中应有更方便的写法用于挂载所有属性 Object.create(props)?
    Object.keys(props).forEach(prop => {
      this[prop] = props[prop];
    });
    // 将需要处理的数据进行处理/重写
    this.name = props.firstName + props.lastName;
    this.birthDate = new Date(props.birth);
    // 假设0为男 1为女
    this.sex = props.sex === 0 ? '男' : '女'
  }
  // 后期如需扩展,可以写一些原型方法
  // 如通过id请求手机号
  getPhone(){
    fetch(xxx, { id: this.id }).then(...)
  }
}

// 最终我们就可以直接在页面/业务组件中直接渲染format之后的list
export const formatList = list => list.map(item => new Person(item));

方法2: 代理

// model.js
const personHandler = {
  get(target, property){
    switch (property) {
      case 'name':
        return target.firstName + target.lastName;
      case 'birthDate':
        return new Date(target.birth);
      case 'sex':
        return target.sex === 0 ? '男' : '女';
      // 其他属性直接返回,相比于方法1,省去了遍历挂载的过程。 
      // Question 2 但是这里如何给target添加原型方法呢?总不能直接target.getPhone = function(){...}吧? 那岂不是每个target都挂载了一个方法,没有必要。 
      default: return target[property];
    }
  }
};

export const formatList = list => list.map(item => new Proxy(item, personHandler));

结论

对于原因1: 我们将这些数据的预处理(与业务逻辑无关),从业务组件/逻辑中抽离出来,让业务组件/逻辑更加纯粹地处理业务。
对于原因2: 如果后端同学说将id改为userId,我只需要在class 或 proxy中加一行代码就搞定了
方法1中: this.id = props.userId ,方法2中:case 'id': return target.userId

问题: 注释中的 question1 和 2.
question3: 方法1和方法2哪个更适合处理这种场景呢。 求大佬们帮忙对比分析下。

回顾
目前在处理业务需求中这种场景的时候,会在tools.js里增加一个对应的formatXXXData(XXX) { // 将XXX map 处理,然后return处理后的值}的函数。 感觉这种做法不太直观,所以在考虑是否应该增加一个model.js文件,用上面类似于 class Person的形式来处理这些数据格式化和某些业务逻辑(比如getPhone())的问题。
但是这样会不会增加内存开销呢?毕竟需要将后端返回的dataList全部实例化, 设置了许多属性和方法。。

回答:

  1. 1和2应用场景不是一样的吗?目的就是先挖好坑等后台这萝卜来跳。
  2. 从逻辑上讲,萝卜和坑并不是一个东西,Proxy毕竟只是代理,如果你还有什么继承之类的需求,其实并没有那么好用。对于1你觉得的问题是在哪里?如果1没有什么问题,那就选1吧。

    case 'getPhone':
        return () => console.log('getPhone function')

回答:

觉得对js的使用,能不用类就不要用类,感觉太”重”了,想要独立的状态?一个新的字面量对象就解决,想要方法复用?用this,如果确实需要,方法1比较适合,question1那里不要使用Object.create(props),毕竟是独立属性,放到原型上很怪。

不使用类的方法

// 编写处理方法
function name(item){
  return item.firstName+item.lastName;
}
function birthDate(item){
  return new Date(item.birth);
}
function sex(item){
  return item.sex === 0 ? '男' : '女';
}
function getPhone(){
  return _fetchPhone
}
function _fetchPhone(){
  return fetch('www.xxx.com/?s='+this.id )
}
function extraProp(item){
  return item.xxx ? 'yyy' : 'xxx'
}
function extraMethod(){
  return _doubleAge
}
function _doubleAge(){
  return this.age * 2
}
// 配置对象
let handle={
  name,
  birthDate,
  sex,
  getPhone,
  extraProp,
  extraMethod
}

// 工厂函数,维护无须深入这里
function factory(data,handle){
  return data.map(item=>{
    let obj=Object.assign({},item)
    for(let k in handle){
      obj[k]=handle[k](item)
    }
    return obj
  })
}
// 调用
let res=factory(list,handle)

回答:

可以参考这里:前端架构之路(2) – 本地化接口模拟、前后端并行开发

暂无评论

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注