第 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) 傳入一個 config 物件,其中 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') 的一般呼叫會快速為我們建立一個包含五個「提醒」的通用列表

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() 的地方覆寫 Factory 預設值。
  • 使用 afterCreate hook 來執行額外的邏輯,例如自動為模型建立相關資料。
  • 使用 Traits 來群組相關的屬性和 afterCreate 邏輯,並保持您的 factory 可組合。