Home Reference Source

model-adapter

NPM version Build Status Coverage Status Known Vulnerabilities changelog license

npm-image

模型适配器: 后端数据与前端数据的桥梁

初衷

Vue 或者其他视图层框架中, 如果直接使用如下插值表达式, 当嵌套对象(通常是后端返回的数据)中的某一层级为空时就会报错 TypeError: Cannot read property 'xxx' of undefined, 造成整个组件都无法渲染.

{{a.aa.aaa}}

为了解决这种问题, 让前端的视图层能够容错增强代码的健壮性, 我们可能要写出如糖葫芦一般的防御性代码, 例如这样 {{a && a.aa && a.aa.aaa}}, 要是再多嵌套几层, 简直不忍直视啊.

舒服一些的处理方式是通过 object path get 之类的库事先处理好数据, 形成前端的视图层模型, 尽量避免嵌套数据, 再到视图层中使用, 例如

// 在视图中使用: {{aaa}}
var vm = {
    aaa: _.get('a.aa.aaa')
};

核心思路

建立一个新的模型, 通过适配器(Adapter)映射(path get 机制)源数据(模型)上的属性

例如

新模型(target)            源数据模型(source)
{                        {
    a: 'a',        <─        a: 1,
   nb: 'b',        <─        b: '2',
  ccc: 'c.cc.ccc'  <─┐       c: {
                     │           cc: {
                     └─              ccc: 'ccc'
                                 }
                             }
}                        }

示例

嵌套数据: 打平数据结构, 映射 path 来访问

import ModelAdapter from 'model-adapter';

// 这里示例由后端接口返回的数据
var ajaxData = {
    name: 'Sun',
    age: 18,
    extData: {
        country: {
            name: 'China'
        }
    }
};

var model = new ModelAdapter({          // name, age 属性默认一对一映射
    countryName: 'extData.country.name' // 嵌套属性映射到源数据属性的 path 路径
}, ajaxData);

console.log(model.name);        // 'Sun'
console.log(model.age);         // 18
console.log(model.countryName); // 'China'

空数据: 设置默认值

import ModelAdapter from 'model-adapter';

var ajaxData = {
    name: null,
    age: 18
};

var model = new ModelAdapter({
    name: { // null 的属性值
        defaultValue: 'Guest'
    },
    sex: { // undefined 的属性值
        defaultValue: 'man'
    }
}, ajaxData);

console.log(model.name); // 'Guest'
console.log(model.age);  // 18
console.log(model.sex);  // 'man'

格式化数据: 变形和还原

import ModelAdapter from 'model-adapter';

var ajaxData = {
    date: 1565001521464
};

var model = new ModelAdapter({
    date: {
        transformer: function(value, source) { // 变形器负责格式化数据
            return new Date(value).toISOString();
        },
        restorer: function(value, model) {     // 还原器负责还原回去
            return new Date(value).getTime();
        }
    }
}, ajaxData);

var restored = model.$restore();

console.log(model.date);    // '2019-08-05T10:38:41.464Z'
console.log(restored.date); // 1565001521464

数组: 在 transformer 中适配数组元素的模型

import ModelAdapter from 'model-adapter';

var ajaxData = {
    users: [{
        name: 'Sun',
        age: 18,
        extData: {
            country: {
                name: 'China'
            }
        }
    }, {
        name: 'Shine',
        age: 19,
        extData: {
            country: {
                name: 'China'
            }
        }
    }]
};

var model = new ModelAdapter({
    users: {
        transformer: function(value) {
            return value.map(function(item) {
                return new ModelAdapter({
                    countryName: 'extData.country.name'
                }, item);
            });
        }
    }
}, ajaxData);

console.log(model.users[0].name);        // 'Sun'
console.log(model.users[0].age);         // 18
console.log(model.users[0].countryName); // 'China'

验证数据: 验证器(仅输出日志提示)

import ModelAdapter from 'model-adapter';

var ajaxData = {
    age: '18'
};

var model = new ModelAdapter({
    age: {
        validator: 'number'
    }
}, ajaxData);

console.log(model.age); // '18'

先声明模型再适配数据

import ModelAdapter from 'model-adapter';

// 声明模型
var model = new ModelAdapter({
    countryName: 'extData.country.name'
});

var ajaxData = {
    name: 'Sun',
    age: 18,
    extData: {
        country: {
            name: 'China'
        }
    }
};
// 适配数据
model.$adapt(ajaxData);

console.log(model.name);        // 'Sun'
console.log(model.age);         // 18
console.log(model.countryName); // 'China'

声明模型类(推荐关闭 copy 机制)

import ModelAdapter from 'model-adapter';

class User extends ModelAdapter {
    constructor(source) {
        super({
            name: 'name',
            countryName: 'extData.country.name'
        }, source, false);
    }
}

var ajaxData = {
    name: 'Sun',
    age: 18,
    extData: {
        country: {
            name: 'China'
        }
    }
};

var user = new User(ajaxData);

console.log(user);             // <User>
console.log(user.name);        // 'Sun'
console.log(user.countryName); // 'China'

与其他框架集成

API 概览

参考