Skip to content

数据引用

基本概念

在前面模板语法中我们已经学习到了如何在模板中使用引用数据

ts
{
  字段: {
    refModel: '模型名称' | '模型对象';
    // 生成数量
    count?: number;
    // 递归深度
    deep?: number;
  },
  // 或者,此时count和deep属性默认为1
  字段:'模型名称' | '模型对象'
}

本节将继续介绍引用数据的高级用法

默认配置

在模板中引用数据时,我们可以指定引用数据生成的数量和递归深度,这样在数据生产时就会按照相应的数量和深度来生成数据。

生成数量

ts
const addressModel = defineModel('address', {
  country: 'location.country',
  city: 'location.city',
});
// 用户模型
const userModel = defineModel('user', {
  firstName: 'person.firstName',
  secondName: 'person.lastName',
  age: ['number.int', { min: 18, max: 65 }],
  address: { refModel: 'address', count: 2 },
});
const userDatas = fakeData(userModel);
console.dir(userDatas, { depth: Infinity });

上面的案例中我们指定了address字段的count属性为2,因此在生成userDatas时,address字段会生成两个数据。生成的数据如下:

ts
{
  firstName: 'Anibal',
  secondName: 'Douglas-Crooks',
  age: 62,
  address: [
    { country: 'Hungary', city: 'Emoryton' },
    { country: 'Venezuela', city: 'New Marcelfurt' }
  ]
}

递归深度

如果存在自引用,比如生成树状数据,我们就需要指定递归深度了,因为如果不限制递归深度会导致栈溢出

ts
const addressModel = defineModel('address', {
  country: 'location.country',
  city: 'location.city',
  children: {
    refModel: 'address',
    count: 2,
    deep: 1,
  },
});
// 用户模型
const userModel = defineModel('user', {
  firstName: 'person.firstName',
  secondName: 'person.lastName',
  age: ['number.int', { min: 18, max: 65 }],
  address: { refModel: 'address', count: 2 },
  children: {
    refModel: 'user',
    deep: 2,
  },
});
const userDatas = fakeData(userModel);
console.dir(userDatas, { depth: Infinity });

上面的案例中addressuser都存在自引用,我们指定了addressdeep属性为1userdeep属性为2,因此在生成userDatas时,addressuser字段会生成相应的深度。并且,我们可以同时指定生成的数量,所以生成的数据如下:

json
{
  "firstName": "Lydia",
  "secondName": "Adams",
  "age": 41,
  "address": [
    // 第一个address数据
    {
      "country": "Democratic Republic of the Congo",
      "city": "Elisefurt",
      // 递归1层
      "children": [
        {
          "country": "Nauru",
          "city": "Randalltown"
        },
        {
          "country": "Afghanistan",
          "city": "Commerce City"
        }
      ]
    },
    // 第二个address数据
    {
      "country": "Ecuador",
      "city": "Hamillworth",
      "children": [
        {
          "country": "Reunion",
          "city": "West Greysonland"
        },
        {
          "country": "Italy",
          "city": "Hempstead"
        }
      ]
    }
  ],
  // 第一层children
  "children": {
    "firstName": "Lisette",
    "secondName": "Gutmann",
    "age": 29,
    "address": [
      {
        "country": "Syrian Arab Republic",
        "city": "Leuschkefield",
        "children": [
          {
            "country": "Austria",
            "city": "Hammesstad"
          },
          {
            "country": "Barbados",
            "city": "West Elodyfort"
          }
        ]
      },
      {
        "country": "Colombia",
        "city": "Fort Anastasia",
        "children": [
          {
            "country": "Czechia",
            "city": "Hyattfort"
          },
          {
            "country": "Zambia",
            "city": "West Stefanieborough"
          }
        ]
      }
    ],
    // 第二层children
    "children": {
      "firstName": "Rex",
      "secondName": "Farrell",
      "age": 22,
      "address": [
        {
          "country": "El Salvador",
          "city": "Montyshire",
          "children": [
            {
              "country": "Bangladesh",
              "city": "Port Prince"
            },
            {
              "country": "Svalbard & Jan Mayen Islands",
              "city": "South Siennacester"
            }
          ]
        },
        {
          "country": "Panama",
          "city": "Monterey Park",
          "children": [
            {
              "country": "Vietnam",
              "city": "South Scotworth"
            },
            {
              "country": "Mozambique",
              "city": "Matildeside"
            }
          ]
        }
      ]
    }
  }
}

运行时配置

但是有些时候我们并不想使用模板中定义的默认配置,我们可以在fakeData的第二个参数中直接配置引用数据的生成规则,这个配置的优先级大于默认的模板配置,其配置方式如下:

ts
type RefModelRule = {
  /**
   * 生成数量
   */
  [COUNT]?: number;
  /**
   * 引用自身时的递归深度
   */
  [DEEP]?: number;

  /**
   * 结构递归
   */
  [key: string | symbol]: number | RefModelRule | [number, number];
};

[COUNT][DEEP]

为了避免属性冲突我们需要使用DataFaker提供的两个symbol属性,COUNTDEEP,他们分别代表生成数量和递归深度的配置,其他的属性则代表结构递归的配置。

ts
const addressModel = defineModel('address', {
  country: 'location.country',
  city: 'location.city',
  children: {
    refModel: 'address',
    count: 2,
    deep: 1,
  },
});
// 用户模型
const userModel = defineModel('user', {
  firstName: 'person.firstName',
  secondName: 'person.lastName',
  age: ['number.int', { min: 18, max: 65 }],
  address: { refModel: 'address', count: 2 },
  children: {
    refModel: 'user',
    deep: 2,
    count: 2,
  },
});
const userDatas = fakeData(userModel, {
  refRules: {
    // address引用数据生成一个,然后其address.childern自引用数据生成一个
    address: {
      [COUNT]: 1,
      children: {
        [COUNT]: 1,
      },
    },
    // 自引用递归深度为1,且只生成一个,address引用属性同上
    children: {
      [DEEP]: 1,
      [COUNT]: 1,
    },
  },
});
console.dir(userDatas, { depth: Infinity });

上面的案例中

  1. address 引用数据生成一个,然后其 address.childern 自引用数据生成一个
  2. 自引用递归深度为 1,且只生成一个,address 引用属性同上

所以生成的数据如下:

json
{
  "firstName": "Jeremy",
  "secondName": "Hettinger",
  "age": 50,
  // 生成一个
  "address": {
    "country": "Cayman Islands",
    "city": "Orlandoview",
    // 递归深度1
    "children": { "country": "Dominica", "city": "Boynton Beach" }
  },
  // 递归深度为1,且生成一个
  "children": {
    "firstName": "Sigrid",
    "secondName": "Kassulke",
    "age": 62,
    "address": {
      "country": "Turkmenistan",
      "city": "Anthonyborough",
      "children": { "country": "French Guiana", "city": "Schaumburg" }
    }
  }
}

配置优先级

从上面的案例就可以看出,配置的优先级是:运行时配置 > 模板