如何使用 Vike 和 Vite 構建可擴展的微前端
微前端允許您將大型 Web 應用程序拆分為更小、可管理的部分。這種方法的靈感來自微服務架構,它允許不同的團隊成員使用他們喜歡的框架和構建工具獨立開發和擴展應用程序的不同部分。
Vite 是微前端的絕佳選擇,因為它的構建時間短且設置簡單。最近,情況得到了進一步的改善,因為您現在可以使用 Vike(以前稱為 vite-plugin-ssr)構建 SSR(服務器端渲染)應用程序,同時仍然可以從微前端架構中受益。
在本教程中,我們將探討如何使用 Vike 和 vite-plugin-federation。我們將對 Vike 進行簡短的介紹,介紹它的一些功能,這些功能使構建 SSR 變得有趣,并討論如何利用它來構建可擴展的微前端。
什么是Vike?
Vike(不要與其父構建工具 Vite 混淆)是 vite-plugin-ssr 的新名稱。Vike 最初是一個插件,旨在為 Vite 添加服務器端渲染 (SSR) 功能,但后來它已經發展成為一個更全面的框架,用于構建完整的 Web 應用程序。
在這樣的場景,Vike 利用 Vite 的捆綁和開發服務器來設置一個服務器,以動態呈現應用程序的頁面。當用戶請求頁面時,Vike 會在運行時在服務器上生成 HTML,這使其成為具有頻繁更改的動態內容的應用程序的理想選擇。
除了作為 SSR 框架之外,Vike 還可以用作靜態站點生成器 (SSG),與 SSR 相比,它將在構建時為您的網站預先生成 HTML 和資產文件,以便當用戶請求頁面時,服務器只需提供這些預先構建的 HTML 文件。
主要特點
除了 SSR 和 SSG 功能外,Vike 的其他一些主要功能包括:
- 快速 —Vike 在設計上速度很快,因為它利用了代碼分割、鏈接預取和快速冷啟動等技術
- 零配置 — Vike 也幾乎不需要配置,只讓您在重要的地方進行控制;其他一切都開箱即用
- 集成 — Vike 是一個完整的框架,可以輕松地與數據獲取、身份驗證、服務器管理、CSS 框架和國際化工具集成
- 支持多種框架 — Vike 非常靈活,可與 React、Vue、Svelte 等框架或您喜歡的任何其他框架配合使用
Vike 入門
npm create vike@latest
# OR
yarn create vike
運行此命令將提示您選擇首選的 JavaScript 框架,并根據所選框架創建新項目。
Vike 還支持 Bun 運行時,這意味著您可以使用以下命令啟動新項目:
bun create vike
Bati 用于高級設置
Vike 團隊開發了一個腳手架工具 Bati,它允許您通過選擇首選的 CSS 庫(如 Tailwind 或 DaisyUI)、身份驗證方法、數據庫、數據獲取庫等來自定義項目設置 - 所有這些都在安裝過程中完成。
例如,要使用 React 和 Tailwind 創建新的 Vike 應用程序,您可以運行:
npm create bati -- --react --tailwindcss
此命令將構建所選安裝選項所需的文件和目錄。接下來,我們需要通過運行來安裝所有必需的包:
npm install
最后,從以下位置開始您的應用程序:
npm run dev
您的應用程序現在應該在瀏覽器中運行,您應該會看到類似于下圖的輸出:
圖片
使用 Vike 和 vite-plugin-federation 進行微前端
Vike 不能僅用于構建微前端;它必須與模塊聯合插件集成,該插件允許您在不同的應用程序之間共享組件和狀態。在 Vite 生態系統中,一個流行的選項是 vite-plugin-federation。
要使用 vite-plugin-federation,我們需要設置至少兩個項目,其中一個將充當主機,另一個將作為遠程項目,我們希望從中訪問某些組件或共享應用程序狀態。
在我們的例子中,一個 Vike 項目將是主機,而一個獨立的 vite+react/vue/svelte 項目將充當遠程項目。下圖也更好地說明了這一點:
圖片
如上圖所示,我們將有一個獨立的組件(例如,卡片組件),在裸露的 Vite 應用程序中創建,在本例中為 Vite + Vue。然后,我們將使用 vite-plugin-federation 在另一個獨立的 Vike-react 應用程序中導入并重用此組件。
讓我們編寫一些代碼來查看實際的實現!
設置遠程 Vite + Vue 應用
首先,為您的微前端項目創建一個新文件夾并移動到其中:
mkdir micro-vike && cd micro-vike
接下來,運行以下命令來創建遠程 Vite + vue 應用程序,我們將從中導出一個卡片組件,供其他獨立應用程序重用:
npm create vite@latest vue-card -- --template vue
要繼續,請移動到新的項目目錄并運行以下命令,將 vite-plugin-federation安裝為 dev 依賴項:
cd vue-card
npm install @originjs/vite-plugin-federation --save-dev
當你在 dev 模式下運行 Vite 應用程序時,它會檢查并嘗試在默認的 5173 端口中運行它;如果此端口不可用,它將在另一個隨機端口中啟動應用程序。但是,對于我們的實現,我們需要知道運行遠程應用程序的端口,因為我們將訂閱它。
因此,我們必須強制我們的應用程序在自定義端口中啟動。為此,請打開您的項目package.json并更新 dev 腳本以匹配以下腳本:
. . .
"scripts": {
"dev": "vite --port 5001 --strictPort",
}
. . .
通過此新更新,我們的應用程序被迫在端口 5001 中運行。
接下來,我們來設計要導出的 card 組件。創建新 Card。vue 系列文件,并將以下代碼粘貼到其中:
<template>
<div class="container">
<h1 class="title">{{ title }}</h1>
<p class="description">{{ description }}</p>
</div>
</template>
<script setup>
const props = defineProps({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
});
</script>
<style scoped>
.container {
padding: 16px;
background-color: #f9f9f9;
border-radius: 8px;
}
.title {
font-size: 24px;
color: #333;
margin-bottom: 8px;
}
.description {
font-size: 16px;
color: #666;
}
</style>
上面的代碼定義了一個基本的 Card 組件,該組件具有作用域樣式,并且該 card 接受 title 和 description 屬性。在繼續之前,你可以在你的 Vue 應用程序中導入這個組件來測試并確保一切正常:
<!-- src/App.vue -->
<template>
<div>
<h1>Remote Vite + Vue app</h1>
<Card
title="Hello World"
descriptinotallow="This is a description passed as a prop."
/>
</div>
</template>
<script setup>
import Card from "./components/Card.vue";
</script>
您應該會看到類似于下面的輸出。
圖片
要使 Card (卡片) 組件遠程可用,請打開默認 vite。config。JS文件并使用以下代碼更新其內容:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import federation from "@originjs/vite-plugin-federation";
export default defineConfig({
plugins: [
vue(),
federation({
name: "remote_app",
filename: "remoteEntry.js",
exposes: {
"./Card": "./src/components/Card.vue",
},
shared: ["vue"],
}),
],
build: {
modulePreload: false,
target: "esnext",
minify: false,
cssCodeSplit: false,
},
});
在這里,我們定義了一個名為 remote_app 的模塊聯邦,其他應用程序可以使用它來引用我們的 Vite + Vue 應用程序。此外,文件名:remoteEntry.js的option 指定生成的主入口文件的名稱;此文件包含其他應用程序加載我們公開的組件所需的所有信息。
請務必驗證此文件是否是在構建項目后生成的。通常,它應該在 http://localhost:5001/assets/remoteEntry.js 上提供。但是,在極少數情況下,它可以在 http://localhost:5001/dist/assets/remoteEntry.js 上獲得。
此外,在上面的配置中,我們使用了 exposes 選項來公開我們的 Card 組件,以便其他應用程序可以使用 remote_app/Card 導入它。此外,因為我們使用的是模塊聯合,所以我們需要更新構建過程以禁用 modulePreload,從而控制模塊的動態加載方式;我們還將 target 設置為 esnext,以確保我們使用模塊聯合正常工作所需的最新 JavaScript 功能。
最后,通過運行以下命令構建并啟動遠程應用程序:
npm run build
npm run dev
生成 remoteEntry 需要 build 命令。JS文件,并且 npm run dev 命令允許通過 localhost 訪問它。
設置Vike-react host應用程序
現在,讓我們深入了解如何創建將利用我們剛剛公開的Card 組件的主機應用程序。回到您的主項目根目錄 micro-vike — 并通過運行以下命令使用 Bati 搭建新的 Vike-react 基架:
npm create bati -- --react host
上面的命令在名為 host 的新目錄中設置項目,到目前為止,我們應該有一個類似于下面的結構:
micro-vike/
├── vue-card/
│ └── . . .
└── host/
└── . . .
接下來,通過運行以下命令在主機項目中安裝 vite-plugin-federation:
更新 vite.config.js文件以匹配以下內容:
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import vike from "vike/plugin";
import federation from "@originjs/vite-plugin-federation";
export default defineConfig({
plugins: [
vike(),
react(),
federation({
name: "app",
remotes: {
remoteApp: "http://localhost:5001/dist/assets/remoteEntry.js",
},
shared: ["react", "react-dom"],
}),
],
build: {
modulePreload: false,
target: "esnext",
minify: false,
cssCodeSplit: false,
},
});
在這里,我們進行了與遠程應用程序類似的更改,此外,我們還更新了主機應用程序以連接到我們之前定義的 remoteEntry URL。
通過所有這些更改,我們現在可以在 Vike-react 項目中加載我們之前使用 Vue 創建的 Card 組件,如下所示:
// pages/index/+Page.jsx
import React from "react";
import Card from "remoteApp/Card";
export default function Page() {
return (
<>
<h1>Vike + React</h1>
<Card
title="Hello World"
description="This is a description passed as a prop."
/>
</>
);
}
使用以下命令啟動應用程序:
npm run dev
當我們運行 Vike 應用程序時,您應該會看到類似于下面的輸出:
圖片
就是這樣!我們已經成功地將一個獨立的 Vite + vue 項目的 Vue 組件加載到另一個 Vike-react 項目中!
Monorepo 考慮
值得一提的是,您還可以集成 Nx 和 Turborepo 等 Monorepo 框架,以將您的微前端整合到一個存儲庫中。這樣,您可以進一步簡化代碼共享、依賴項管理和配置,并減少維護工作。
但是,這是否是正確的選擇取決于您的項目要求。如果您有興趣開始使用 Nx,可以參考此采用指南:https://blog.logrocket.com/nx-adoption-guide。
結論
在本教程中,我們探討了 Vike、它的主要功能、如何開始使用它,以及如何將其與 vite-plugin-federation 結合使用以構建可擴展的微前端應用程序。您還可以在此 GitHub 存儲庫中找到本教程中使用的所有代碼。
原文來源:https://blog.logrocket.com/build-scalable-micro-frontends-vike-vite/
原文作者:Elijah Asaolu
本文翻譯:一川