第 8 部分 - 工厂

Mirage 包含一个工厂层,帮助简化用真实、关联数据填充 Mirage 服务器的过程。

目前,如果我们想创建一个提醒,我们需要在我们的 seeds() 钩子中执行类似以下操作

seeds(server) {
  server.create("reminder", { text: "Walk the dog" });
}

始终为创建的每个模型指定所有属性可能会在代码中添加很多样板代码,尤其是在测试期间。当需要关系才能使数据有效时,情况会变得更加复杂。

工厂是编码数据约束的理想位置,使您能够更轻松地快速创建有效的数据图。让我们看看它们是如何工作的。

对于此应用程序,提醒始终具有 text 属性。让我们定义一个提醒工厂来编码此属性

import {
  createServer,
  Model,
  hasMany,
  belongsTo,
  RestSerializer,
  Factory,
} from "miragejs"

export default function () {
  createServer({
    models: {
      list: Model.extend({
        reminders: hasMany(),
      }),

      reminder: Model.extend({
        list: belongsTo(),
      }),
    },

    factories: {
      reminder: Factory.extend({
        text: "Reminder text",
      }),
    },

    // ...rest of server
  })
}

首先,我们导入 Factory,然后使用服务器的新 factories 键定义一个提醒工厂。我们可以使用 Factory.extend(config) 传递一个配置对象,其中配置对象的键对应于我们模型上的属性。

现在,我们可以更新我们的 seeds(),只需调用 server.create('reminder'),而无需传递其他任何内容

seeds(server) {
  server.create("reminder");
  server.create("reminder");
  server.create("reminder");
}

使用此种子数据,应用程序现在看起来像这样

Failing request

有效,但不太现实!我们可以使用函数属性使其更加动态

factories: {
  reminder: Factory.extend({
    text(i) {
      return `Reminder ${i}`
    }
  }),
},

现在每个提醒都有自己的唯一文本

Dyanamic attr

faker.js 这样的库在函数属性中也很好用,可以创建更高保真度的數據。

结合 server.createList,我们现在可以使用我们的工厂定义轻松创建许多提醒

seeds(server) {
  server.createList("reminder", 100);
}

这很快就会给我们一个大型数据集

Dyanamic list

但是,如果我们想覆盖在工厂中定义的特定属性怎么办?我们始终可以通过将属性传递到 server.create() 中来实现。这些属性将覆盖我们基本工厂中定义的任何内容。

seeds(server) {
  // Create a specific reminder
  server.create('reminder', { text: 'Walk the dog' })

  // Create 5 more generic reminders
  server.createList("reminder", 5);
}

以下是结果

Mixing static and dynamic attrs

让我们转向我们的列表模型。我们将从为它定义一个工厂开始

factories: {
  list: Factory.extend({
    name(i) {
      return `List ${i}`;
    },
  }),

  reminder: Factory.extend({
    text(i) {
      return `Reminder ${i}`;
    },
  }),
},

现在,如果我们想创建通用的列表和提醒,我们不需要指定任何属性

seeds(server) {
  server.create("list", {
    reminders: server.createList("reminder", 5),
  });
}

如您所见,在创建时将提醒传递到列表中,我们仍然可以创建有效的关系数据图

Lists

如果我们想更轻松地创建一个包含多个提醒的列表怎么办?我们可以使用列表工厂的 afterCreate 钩子,将新创建的列表传递到我们创建的任何新提醒中

factories: {
  list: Factory.extend({
    name(i) {
      return `List ${i}`;
    },

    afterCreate(list, server) {
      server.createList('reminder', 5, { list })
    }
  }),

  reminder: Factory.extend({
    text(i) {
      return `Reminder ${i}`;
    },
  }),
},

现在,只需使用 server.create('list'),我们就可以像以前一样拥有一个包含 5 个提醒模型的有效列表

seeds(server) {
  server.create("list")
}

但是,如果我们想恢复一些更精心策划的、更现实的数据,无论是因为我们在测试特定内容,还是因为我们希望在开发过程中获得更少通用的数据怎么办?

将此填充逻辑复制到

seeds(server) {
  server.create("list", {
    name: "Home",
    reminders: [server.create("reminder", { text: "Do taxes" })],
  });

  server.create("list")
}

如果我们使用此逻辑,我们会看到我们的 afterCreate 钩子被两个列表触发,现在我们的“主页”列表有 5 个随机生成的提醒,以及我们特定的“缴税”提醒

After create

我们可以更新我们的 afterCreate 逻辑,首先检查新创建的列表是否已经包含传递到其中的提醒,如果不存在,则只创建默认提醒

factories: {
  list: Factory.extend({
    name(i) {
      return `List ${i}`;
    },

    afterCreate(list, server) {
      if (!list.reminders.length) {
        server.createList('reminder', 5, { list })
      }
    }
  }),

  reminder: Factory.extend({
    text(i) {
      return `Reminder ${i}`;
    },
  }),
},

现在,相同的填充逻辑使我们能够轻松创建一个仅包含我们传递的“缴税”提醒的精心策划的“主页”列表,而我们对 server.create('list') 的普通调用会快速为我们搭建一个包含 5 个提醒的通用列表

Improved after create

作为最后一个重构步骤,有一个更好的方法可以只在需要时创建与我们的列表关联的通用提醒:工厂特征。

特征使我们能够将属性和 afterCreate 逻辑分组到一个名称下,因此只有在使用该名称时才会调用它们。让我们在列表工厂中添加一个 withReminders 特征。

首先,我们将导入 trait

import {
  createServer,
  Model,
  hasMany,
  belongsTo,
  RestSerializer,
  Factory,
  trait,
} from "miragejs"

然后,我们将 afterCreate 逻辑移动到列表工厂的 withReminders 特征中

factories: {
  list: Factory.extend({
    name(i) {
      return `List ${i}`;
    },

    withReminders: trait({
      afterCreate(list, server) {
        server.createList('reminder', 5, { list })
      }
    })
  }),

  reminder: Factory.extend({
    text(i) {
      return `Reminder ${i}`;
    },
  }),
},

请注意,我们删除了 list.reminders.length 检查,因为此逻辑现在只有在明确调用特征时才会调用。

如果您保存并检查应用程序,您会发现我们的第二个通用列表不再有任何关联的提醒。让我们更新我们的 seeds 钩子,为该列表调用新特征

seeds(server) {
  server.create("list", {
    name: "Home",
    reminders: [server.create("reminder", { text: "Do taxes" })],
  });

  server.create("list", "withReminders")
}

现在,我们的 UI 又回到了最初的样子!但我们通过使列表工厂不那么令人惊讶地提高了它 - 基本情况只创建一个列表模型。

正如我们在下一节中将看到的,良好的工厂定义对于编写良好的测试非常重要。

工厂具有 更多功能,提供灵活的方式来创建数据场景,因此您可以执行诸如快速将开发环境从匿名用户切换到已认证用户,或在测试中设置所需的数据等操作。

现在,让我们将填充逻辑恢复到我们精心策划的数据集

seeds(server) {
  server.create("reminder", { text: "Walk the dog" });
  server.create("reminder", { text: "Take out the trash" });
  server.create("reminder", { text: "Work out" });

  server.create("list", {
    name: "Home",
    reminders: [server.create("reminder", { text: "Do taxes" })],
  });

  server.create("list", {
    name: "Work",
    reminders: [server.create("reminder", { text: "Visit bank" })],
  });
}

充分利用工厂是充分发挥 Mirage 数据层所有功能的最佳方式之一。

要点

  • 工厂就像蓝图,帮助您轻松创建关系数据的图表。
  • 您可以使用静态值或函数作为属性。
  • 您可以在调用 server.create() 时覆盖工厂默认值。
  • 使用 afterCreate 钩子来执行额外的逻辑,例如自动为模型创建相关数据。
  • 使用 Traits 来分组相关的属性和 afterCreate 逻辑,并保持您的工厂可组合。