初步認識微前端
微前端是什麼
現在的前端應用,功能、互動日益複雜,若只由一個團隊負責,隨著時間的推進,會越來越龐大,愈發難以維護。
微前端這個名詞,第一次提出是在2016年底。它將微服務(將單一應用程式劃分成一組小的服務,服務之間相同配合,為使用者提供最終價值)這個應用於服務端的技術擴充套件到前端領域。
微前端背後的想法是:將網站或 web 應用程式視為由獨立團隊負責的子應用(或模組、功能)的組合。
微前端核心是:拆、和。
拆
,即將一個應用拆成多個子應用。每個子應用由單獨的團隊負責,獨立開發、釋出和
,將多個子應用整合成完整的 web 應用。
微前端框架
微前端框架有:single-spa、qiankun。
由於 qiankun 基於 single-spa,所以我們先介紹 single-spa。
single-spa
Single-spa 是一個將多個單頁面應用聚合為一個整體應用的 JavaScript 微前端框架 —— 官網
實戰
single-spa 能將多個單頁面應用聚合成一個整體應用,所以我將建立一個 vue的應用、一個 react 的應用,再將這兩個應用整合成一個應用。
下面我們將使用 cli 建立三個專案,vue 子應用、react 子應用和父應用(或完整應用),實現一個微前端架構系統。
cli(create-single-spa)
vue 提供了 cli,用於快速開始 vue 的編寫;single-spa 也提供了自己的 cli,即 create-single-spa
全域性安裝 cli:
>npm i create-single-spa -g
added 387 packages, and audited 388 packages in 40s
50 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
下面只使用 cli 其中的一種語法來建立三個專案:即create-single-spa my-dir
。用法如下:
>create-single-spa single-spa-project
? Select type to generate
single-spa application / parcel
in-browser utility module (styleguide, api cache, etc)
> single-spa root config
選項說明:
single-spa application
子應用parcel
一個parcel可以大到一個應用,也可以小至一個元件in-browser utility module
瀏覽器內實用模組single-spa root config
single-spa 根配置。可以建立父應用,用於整合子應用。
使用 cli 建立父應用
使用 cli 建立父專案。選擇 single-spa root config
:
single-spa-test> create-single-spa single-spa-project
// single-spa root config
? Select type to generate single-spa root config
// 選擇 npm
? Which package manager do you want to use? npm
// 不使用 Typescript
? Will this project use Typescript? No
// 不需要 single-spa Layout 引擎
? Would you like to use single-spa Layout Engine No
// 組織輸入 pjl
? Organization name (can use letters, numbers, dash or underscore) pjl
...
Project setup complete!
Run 'npm start' to boot up your single-spa root config
注:後面兩個子應用的組織也輸入 pjl
,說明這三個專案都屬於 pjl。
配置檔案如下:
// single-spa-project/package.json
{
"name": "@pjl/root-config",
"scripts": {
"start": "webpack serve --port 9000 --env isLocal",
...
},
...
"dependencies": {
...
"single-spa": "^5.9.3"
}
}
通過 npm run start
啟動專案:
single-spa-project>npm run start
> start
> webpack serve --port 9000 --env isLocal
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:9000/
<i> [webpack-dev-server] On Your Network (IPv4): http://192.168.0.102:9000/
...
webpack 5.68.0 compiled successfully in 5151 ms
根據提示訪問專案(http://localhost:9000/
),瀏覽器輸出(中文註釋由筆者新增):
// 歡迎來到你的 single-spa 根配置。說明專案啟動成功,並能正常訪問
Welcome
to your single-spa root config! ?
// 這個頁面由一個示例應用 `single-spa application` 所渲染
This page is being rendered by an example single-spa application that is being imported by your root config.
Next steps
// 新增共享依賴。下文引入 react 子應用時會用到
1. Add shared dependencies
Locate the import map in src/index.ejs
Add an entry for modules that will be shared across your dependencies. For example, a React application generated with create-single-spa will need to add React and ReactDOM to the import map.
"react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js"
Refer to the corresponding single-spa framework helpers for more specific information.
// 建立你的下一個 single-spa 應用。
2. Create your next single-spa application
// 使用create-single-spa生成一個single-spa應用,按照提示操作,直到本地執行
Generate a single-spa application with create-single-spa and follow the prompts until it is running locally
// 返回到 root-config 並使用您的專案名稱更新 src/index.ejs 中的匯入對映
Return to the root-config and update the import map in src/index.ejs with your project's name
// 建議使用應用程式的 package.json 名稱欄位
It's recommended to use the application's package.json name field
// 開啟 src/root-config.js 去掉註冊這個應用的程式碼
Open src/root-config.js and remove the code for registering this application
Uncomment the registerApplication code and update it with your new application's name
// 在此之後,您應該不再看到這個歡迎頁面,而是應該看到您的新應用程式!
After this, you should no longer see this welcome page but should instead see your new application!
Learn more
Shared dependencies documentation on single-spa.js.org
SystemJS and Import Maps
Single-spa ecosystem
Contribute
Support single-spa by donating on OpenCollective!
Contribute to single-spa on GitHub!
Join the Slack group to engage in discussions and ask questions.
Tweet @Single_spa and show off the awesome work you've done!
於是我們知道:
- 專案啟動成功,並能正常訪問
- 這個頁面由一個示例應用
single-spa application
所渲染 - 下一步建立子應用,替換這個示例應用
使用 cli 建立 vue 子應用
使用 cli 建立 vue 子應用。選擇 single-spa application / parcel
:
single-spa-test> create-single-spa single-spa-vue
? Select type to generate single-spa application / parcel
// 框架選擇 vue
? Which framework do you want to use? vue
// 組織輸入 pjl。保持與父應用相同
? Organization name (can use letters, numbers, dash or underscore) pjl
Vue CLI v4.5.13
┌───────────────────────────────────────────┐
│ │
│ New version available 4.5.13 → 4.5.15 │
│ Run npm i -g @vue/cli to update! │
│ │
└───────────────────────────────────────────┘
// vue 的配置
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Router, Vuex, Linter
// vue 2.x
? Choose a version of Vue.js that you want to start the project with 2.x
// 路由模式是否使用 history
? Use history mode for router? (Requires proper server setup for index fallback in production) No
// eslint
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
// 配置是否儲存在專門的檔案中
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
// 預設是否儲存
? Save this as a preset for future projects? No
...
Project setup complete!
Steps to test your Vue single-spa application:
1. Run 'npm run serve'
2. Go to http://single-spa-playground.org/playground/instant-test?name=@pjl/single-spa-vue&url=%2F%2Flocalhost%3A8080%2Fjs%2Fapp.js&framework=vue to see it working!
No change to package.json was detected. No package manager install will be executed.
Tip:其中 vue 的配置
和我們之前學習的 vue-cli 是相同的。
vue 子應用(專案)是否能獨立跑起來?首先檢視 package.json 中的指令碼:
// single-spa-vue/package.json
{
"name": "@pjl/single-spa-vue",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"serve:standalone": "vue-cli-service serve --mode standalone"
},
"dependencies": {
"single-spa-vue": "^2.1.0",
...
},
"devDependencies": {
"vue-cli-plugin-single-spa": "~3.1.2",
...
}
}
執行指令碼 npm run serve
:
single-spa-test\single-spa-vue>npm run serve
> @pjl/single-spa-vue@0.1.0 serve
> vue-cli-service serve
...
App running at:
- Local: http://localhost:8080/
- Network: http://192.168.0.102:8080/
...
根據提示訪問專案(http://localhost:8080/
),瀏覽器輸出(中文註釋由筆者新增):
// 你的微前端不在這裡
Your Microfrontend is not here
// @pjl/single-spa-vue 微前端以“整合”模式執行
The @pjl/single-spa-vue microfrontend is running in "integrated" mode, since standalone-single-spa-webpack-plugin is disabled. This means that it does not work as a standalone application without changing configuration.
// 我如何開發這個微前端?
How do I develop this microfrontend?
// 要開發此微前端,請嘗試以下步驟:
To develop this microfrontend, try the following steps:
// 注:父應用整合 vue 子應用時,需要用到下面這個 url
Copy the following URL to your clipboard: http://localhost:8080/js/app.js
In a new browser tab, go to the your single-spa web app. This is where your "root config" is running. You do not have to run the root config locally if it is already running on a deployed environment - go to the deployed environment directly.
In the browser console, run localStorage.setItem('devtools', true); Refresh the page.
A yellowish rectangle should appear at the bottom right of your screen. Click on it. Find the name @pjl/single-spa-vue and click on it. If it is not present, click on Add New Module.
Paste the URL above into the input that appears. Refresh the page.
Congrats, your local code is now being used!
For further information about "integrated" mode, see the following links:
// 本地開發預覽
Local Development Overview
Import Map Overrides Documentation
If you prefer Standalone mode
// 要在“獨立”模式下執行這個微前端,可以執行 `npm run start:standalone`
To run this microfrontend in "standalone" mode, the standalone-single-spa-webpack-plugin must not be disabled. In some cases, this is done by running npm run start:standalone. Alternatively, you can add --env.standalone to your package.json start script if you are using webpack-config-single-spa.
If neither of those work for you, see more details about enabling standalone mode at Standalone Plugin Documentation.
於是我們知道:
- 啟動失敗,這個子應用以“整合”模式執行
- “獨立”模式下執行這個微前端,可以執行
npm run start:standalone
嘗試獨立執行失敗:
single-spa-vue> npm run start:standalone
npm ERR! missing script: start:standalone
npm ERR!
npm ERR! Did you mean this?
// 是這個嗎?
npm ERR! serve:standalone
...
提示是否是 serve:standalone
?package.json 的 script
配置中確實有這個配置,再次嘗試:
// 注:第一次失敗,之後啟動都成功了
single-spa-vue>npm run serve:standalone
> @pjl/single-spa-vue@0.1.0 serve:standalone
> vue-cli-service serve --mode standalone
INFO Starting development server...
...
App running at:
- Local: http://localhost:8080/
- Network: http://192.168.0.102:8080/
瀏覽器訪問 http://localhost:8080/
,vue 專案正常啟動。
使用 cli 建立 react 子應用
使用 cli 建立 react 子應用。選擇 single-spa application / parcel
:
single-spa-test> create-single-spa single-spa-react
? Which framework do you want to use? react
? Which package manager do you want to use? npm
? Will this project use Typescript? No
// 組織輸入 pjl
? Organization name (can use letters, numbers, dash or underscore) pjl
? Project name (can use letters, numbers, dash or underscore) react
...
Project setup complete!
Steps to test your React single-spa application:
1. Run 'npm start -- --port 8500'
2. Go to http://single-spa-playground.org/playground/instant-test?name=@pjl/react&url=8500 to see it working!
父應用如何引入子應用
上文(“使用 cli 建立父應用”),我們知道父應用中預設使用了一個子應用,所以我們只需瞭解其原理即可。
主要關注 src 下的兩個檔案:
index.ejs
,可以認為是一個 html 頁面,裡面使用的是 ejs,一個高效的嵌入式 JavaScript 模板引擎,語法為<%= EJS %>
。pjl-root-config.js
,根配置,用於啟動 single-spa 應用。
我們先初略看一下這兩個檔案:
<!-- single-spa-project/src/index.ejs(已刪除其中註釋) -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Root Config</title>
<script src="https://cdn.jsdelivr.net/npm/regenerator-runtime@0.13.7/runtime.min.js"></script>
<meta http-equiv="Content-Security-Policy"
content="default-src 'self' https: localhost:*; script-src 'unsafe-inline' 'unsafe-eval' https: localhost:*; connect-src https: localhost:* ws://localhost:*; style-src 'unsafe-inline' https:; object-src 'none';">
<!-- systemjs 是一個模組載入器 -->
<meta name="importmap-type" content="systemjs-importmap" />
<!-- 匯入對映。這裡表明依賴於 single-spa -->
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/system/single-spa.min.js"
}
}
</script>
<!-- 預載入 -->
<link rel="preload" href="https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/system/single-spa.min.js" as="script">
<!-- 本地啟動,會載入 pjl-root-config.js -->
<% if (isLocal) { %>
<script type="systemjs-importmap">
{
"imports": {
"@pjl/root-config": "//localhost:9000/pjl-root-config.js" // {2}
}
}
</script>
<% } %>
<script src="https://cdn.jsdelivr.net/npm/import-map-overrides@2.2.0/dist/import-map-overrides.js"></script>
<% if (isLocal) { %>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/system.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/extras/amd.js"></script>
<% } else { %>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/system.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/extras/amd.min.js"></script>
<% } %>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<main></main>
<script>
// 使用 System.import() 動態載入模組。入口。
System.import('@pjl/root-config'); // {1}
</script>
<import-map-overrides-full show-when-local-storage="devtools" dev-libs></import-map-overrides-full>
</body>
</html>
// single-spa-project/src/pjl-root-config.js
import { registerApplication, start } from "single-spa";
// 註冊應用。
registerApplication({
name: "@single-spa/welcome", // 應用名。隨便起
app: () =>
// 匯入模組
System.import(
"https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
),
// 當匹配 `/` 時,載入這個應用
activeWhen: ["/"],
});
// registerApplication({
// name: "@pjl/navbar",
// app: () => System.import("@pjl/navbar"),
// activeWhen: ["/"]
// });
// 啟動
// 這時應用才會被真正掛載。在start被呼叫之前,應用先被下載,但不會初始化/掛載/解除安裝
start({
urlRerouteOnly: true,
});
引入子應用的原理:
index.ejs
(或稱index.html)主要為了呼叫registerApplication()
方法,裡面使用了 systemjs(模組載入器)- 首先通過
systemjs-importmap
匯入對映(亦或匯入依賴) - 入口是
System.import('@pjl/root-config')
(行{1}),於是匹配到@pjl/root-config
對應的pjl-root-config.js
(行{2}) pjl-root-config.js
中註冊了一個應用,並啟動http://localhost:9000/
會匹配/
,則會載入註冊的應用(@single-spa/welcome
)
bootstrap、mount、unmount
vue 子應用和 react子應用都匯出了三個生命週期函式:bootstrap(引導)、mount(掛載)和unmount(解除安裝)。
// single-spa-vue/src/main.js
import singleSpaVue from 'single-spa-vue'
...
const vueLifecycles = singleSpaVue({
...
})
export const bootstrap = vueLifecycles.bootstrap
export const mount = vueLifecycles.mount
export const unmount = vueLifecycles.unmount
// single-spa-react/src/pjl-react.js
import singleSpaReact from "single-spa-react";
...
const lifecycles = singleSpaReact({
...
});
export const { bootstrap, mount, unmount } = lifecycles;
Tip:示例應用中也是有這三個生命週期函式的。
// 示例應用
https://unpkg.com/single-spa-welcome@2.3.0/dist/single-spa-welcome.js
bootstrap:()=>T,mount:()=>O,unmount:()=>R
將 vue 子應用合入父應用
- 首先啟動子應用,並取得子應用的 url
single-spa-vue>npm run serve
...
App running at:
- Local: http://localhost:8080/
- Network: http://192.168.0.102:8080/
...
瀏覽器訪問 url(http://localhost:8080/
),從中取得子應用的 url:
// 瀏覽器輸出:
...
Copy the following URL to your clipboard: http://localhost:8080/js/app.js
...
子應用的 url為 //localhost:8080/js/app.js
。
- 在
index.ejs
中匯入對映,並在根配置(pjl-root-config.js
)中將示例應用替換成 vue 子應用
// single-spa-project/src/index.ejs
...
<script type="systemjs-importmap">
{
"imports": {
"@pjl/root-config": "//localhost:9000/pjl-root-config.js",
// @pjl/single-spa-vue,子應用 package.json 中定義的 name 屬性的值
+ "@pjl/single-spa-vue": "//localhost:8080/js/app.js"
}
}
</script>
// single-spa-project/src/pjl-root-config.js
/*
registerApplication({
name: "@single-spa/welcome",
app: () =>
System.import(
"https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
),
activeWhen: ["/"],
});
*/
registerApplication({
// name 名字隨意
name: "@pjl/single-spa-vue123",
app: () => System.import('@pjl/single-spa-vue'),
activeWhen: ["/vue"],
})
至此,瀏覽器中訪問 http://localhost:9000/vue
就能看到 vue 子應用。
注:假如你沒有註釋
示例應用,當你訪問 http://localhost:9000/vue
,頁面顯示的仍會是示例應用的內容。這是因為 /vue
也會匹配 /
。可以將陣列形式改為回撥即可。
- activeWhen: ["/"],
+ activeWhen: (location) => location.pathname === '/',
父應用給子應用傳 props
可以使用 customProps
來給子應用傳遞 props
。比如下面我們傳一個 age
,請看示例:
- 父應用通過
customProps
給 vue 子應用傳遞age
屬性:
registerApplication({
name: "@pjl/single-spa-vue123",
...
// 自定義屬性可以是一個物件,也可以是一個返回Object的函式
customProps: {
age: 18,
},
})
子應用的 main.js
中接收 age
屬性:
// single-spa-vue/src/main.js
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
render(h) {
// 將屬性傳到 App
return h(App, {
props: {
// single-spa props are available on the "this" object.
+ age: this.age
}
})
},
router,
store
}
})
在 App.vue
中將 age
屬性輸出:
<script>
export default {
props: ['age'],
created(){
console.log('age', this.age)
}
}
</script>
注:在 About.vue
這麼寫不可以,因為 main.js
中是將 props
傳給 App.vue
。
將 react 子應用合入父應用
首先啟動子應用,並取得子應用的 url
single-spa-react> npm run start
> start
> webpack serve
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/
<i> [webpack-dev-server] On Your Network (IPv4): http://192.168.0.102:8080/
...
瀏覽器訪問 url(http://localhost:8080/
),從中取得子應用的 url:
// 瀏覽器輸出:
...
Copy the following URL to your clipboard: http://localhost:8080/pjl-react.js
...
子應用的 url為 //localhost:8080/pjl-react.js
。
- 在
index.ejs
中匯入對映,並在根配置(pjl-root-config.js
)中將示例應用替換成 react 子應用
// single-spa-project/src/index.ejs
...
<script type="systemjs-importmap">
{
"imports": {
"@pjl/root-config": "//localhost:9000/pjl-root-config.js",
// @pjl/react,子應用 package.json 中定義的 name 屬性的值
+ "@pjl/react": "//localhost:8080/pjl-react.js"
}
}
</script>
// single-spa-project/src/pjl-root-config.js
registerApplication({
name: "@pjl/react",
app: () => System.import('@pjl/react'),
activeWhen: ["/react"],
})
訪問 react(http://localhost:9000/react
),頁面空白,控制檯報錯:
Uncaught Error: application '@pjl/react' died in status LOADING_SOURCE_CODE: Unable to resolve bare specifier 'react' from http://localhost:8080/pjl-react.js (SystemJS Error#8 https://git.io/JvFET#8)
點入 http://localhost:8080/pjl-react.js
,發現 System
註冊了兩個模組:
System.register(["react","react-dom"], function(__WEBPACK_DY...
將上文(“使用 cli 建立父應用”)提到的“新增共享依賴”加入後,再次重新整理頁面即可看到 react 應用正常生效。
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/system/single-spa.min.js",
+ "react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js",
+ "react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js"
}
}
</script>
Tip:如果需要同時接入 vue 應用和react 應用,可以修改 vue 子應用的埠即可。比如增加 vue.config.js
:
// configureWebpack 是 vue-cli 提供的
// devServer 在 webpack 文件中
module.exports = {
devServer: {
port: 9000
},
}
qiankun
可能是你見過最完善的微前端解決方案 —— 官網
根據官網介紹,其特點是:簡單、樣式隔離、js 沙箱、生產可使用。
Tip:qiankun 的文件內容不多,其中的指南、API、常見問題都可以看一下。
實戰
下面我們將使用 cli 建立三個專案,vue 子應用、react 子應用和父應用(或完整應用),實現一個微前端架構系統。(與 single-spa 實戰類似)
使用 vue-cli 建立父應用
// 選中 babel、Router、vue2.x
qiankun-test> vue create qiankun-project
注:建議父應用和vue 子應用的 vue-router 模式都選用 history。筆者這裡選則了 hash,所以後面需要將路由模式改為 history。
使用 vue-cli 建立 vue 子應用
// 選中 babel、Router、Vuex、vue2.x
qiankun-test> vue create qiankun-vue
使用 cli 建立 react 子應用
Tip:沒有用過 react 也沒有問題,只需以下幾步即可跑起一個 react 專案。
// 第一次失敗
qiankun-test>npx create-react-app qiankun-react
// 全域性安裝 create-react-app
qiankun-test>npm install -g create-react-app
// 再次安裝成功
qiankun-test>npx create-react-app qiankun-react
qiankun-test>cd qiankun-react
// 啟動。檢視 package.json 即可
qiankun-react>npm start
注:第一次執行 npx create-react-app qiankun-react
提示需要安裝包(create-react-app),選擇 Y
卻也沒能安裝成功。之後全域性安裝 create-react-app
,再次執行則成功了。
qiankun-test> npx create-react-app qiankun-react
Need to install the following packages:
create-react-app
Ok to proceed? (y) y
You are running `create-react-app` 4.0.3, which is behind the latest release (5.0.0).
We no longer support global installation of Create React App.
Please remove any global installs with one of the following commands:
- npm uninstall -g create-react-app
- yarn global remove create-react-app
The latest instructions for creating a new app can be found here:
up to date, audited 1 package in 444ms
found 0 vulnerabilities
將 vue 子應用合入父應用
- 修改父應用
安裝 qiankun(參考:qiankun 主應用):
qiankun-project> npm i qiankun -S
added 4 packages, and audited 1251 packages in 12s
...
package.json 則會增加依賴:
"dependencies": {
"qiankun": "^2.6.3",
},
在父應用中註冊微應用:
// qiankun-project/src/main.js
...
// 以下都是新增
import { registerMicroApps, start } from 'qiankun';
const apps = [
{
name: 'qiankuan-vue123', // 名字隨意
entry: '//localhost:9000/', // 子應用的 url
container: '#vue', // 掛載到的元素 id。
activeRule: '/app-vue', // 當匹配 /app-vue 時觸發子應用
// 傳參給子應用
props: {
age: 18,
},
},
]
// 註冊應用
registerMicroApps(apps);
// 啟動
start();
增加子應用的掛載元素:
// qiankun-project/src/App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<!-- 路由到子應用,會渲染到下面定義的元素 #vue -->
+ <router-link to="/app-vue">app-vue</router-link>
</div>
<router-view />
<!-- 掛載到的元素 id。-->
+ <div id="vue"></div>
</div>
</template>
修改路由模式為 history
:
// qiankun-project\src\router\index.js
const router = new VueRouter({
+ mode: 'history',
routes
})
- 修改子應用
新建配置檔案,開啟 cors,配置微應用的打包工具:
// qiankuan-vue/vue.config.js
// 取得 package.json 中欄位 name 的值,即專案名稱
const packageName = require('./package.json').name;
module.exports = {
devServer: {
// 父應用已佔用 8080,子應用得使用一個不同的埠
port: 9000,
headers: {
// 若不開啟 cors,瀏覽器控制檯報錯如下
// Access to fetch at 'http://localhost:9000/' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
'Access-Control-Allow-Origin': '*',
},
},
// 配置微應用的打包工具
configureWebpack: {
output: {
// 輸出一個庫,為你的入口做匯出
library: `${packageName}-[name]`,
// 將你的 library 暴露為所有的模組定義下都可執行的方式。它將在 CommonJS, AMD 環境下執行,或將模組匯出到 global 下的變數。
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
}
}
}
匯出相應的生命週期鉤子:
// qiankuan-vue/src/main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
// 匯入 routes。
import { routes } from './router'
import VueRouter from 'vue-router';
// 使用 webpack 執行時 publicPath 配置
if (window.__POWERED_BY_QIANKUN__) {
// 註釋也正常
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
let router = null;
let instance = null;
function render(props = {}) {
const { container } = props;
router = new VueRouter({
// 應用的基路徑。例如,如果整個單頁應用服務在 /app/ 下,然後 base 就應該設為 "/app/"
base: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/',
mode: 'history',
routes,
});
instance = new Vue({
router,
store,
render: (h) => h(App),
// fix 常見問題:微應用的根 id 與其他 DOM 衝突。解決辦法是:修改根 id 的查詢範圍。
}).$mount(container ? container.querySelector('#app') : '#app');
}
// fix 常見問題:如何獨立執行微應用
// 如果不是 qiankun,則渲染
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log('vue app bootstraped');
}
/**
* 應用每次進入都會呼叫 mount 方法,通常我們在這裡觸發應用的渲染方法
*/
export async function mount(props) {
render(props);
}
/**
* 應用每次 切出/解除安裝 會呼叫的方法,通常在這裡我們會解除安裝微應用的應用例項
*/
export async function unmount(props) {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
router = null;
}
匯出 routes:
// qiankuan-vue/src/router\index.js
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
...
]
+ export { routes }
重啟父子應用即可:
qiankun-project> npm run serve
qiankuan-vue> npm run serve
就像這樣:
// 訪問
http://localhost:8080/app-vue/about
瀏覽器輸出:
Home | About | app-vue
Home | About(啟用)
This is an about page
Tip:react 子應用合入主應用,請自行完成。
總結
有人說 single-spa 不夠靈活,沒有樣式隔離,沒有 js 沙箱;
而 qiankun 建立在 single-spa 的基礎上,使用起來更加簡單,提供樣式隔離,也提供了 js 沙箱;
single-spa 的原理是什麼?
qiankun 的樣式隔離、js沙箱機制又是如何實現?
我們後面再聊。
祝:諸君新年身體健康、技術有成