column:
- [Micro Frontend] What is a Micro Frontend
- 【Micro Frontend】qiankun
- [Micro front end] qiankun + vite + vue3
1. Overall structure
Under the qiankun system, a micro-frontend project includes a main application and multiple sub-applications. Essentially, each project (main application) can be developed and run independently.
1.1 Project structure during development
A total of three projects, one main application, two sub-applications, directory structure:
.
├── app-01
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── public
│ ├── src
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── app-02
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── public
│ ├── src
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── main-app
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── public
│ ├── src
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
└── readme.md
1.2 Deployment project structure
There are many deployment methods to choose from. The method used here is to deploy the three projects on the same server and the same port. The directory structure is:
.
├── index.html
├── static
│ ├── index-011eeef2.css
│ └── index-0ab867b1.js
├── sub
│ ├── app-01
│ │ ├── index.html
│ │ ├── static
│ │ │ ├── index-0244ff29.js
│ │ │ └── index-83c9dd61.css
│ │ └── vite.svg
│ └── app-02
│ ├── index.html
│ ├── static
│ │ └── index-cb440182.js
│ └── vite.svg
└── vite.svg
2. Development
During development, the listening ports corresponding to the three applications:
application | port |
---|---|
main-app | 80 |
app-01 | 8081 |
app-02 | 8082 |
After the project is started, it can be accessed in the browser:
- http://localhost:80 The overall operation effect
- http://localhost:8081 app-01 running alone
- http://localhost:8082 app-02 running alone
2.1 Main application
主应用
Used to register sub-applications and control switching between sub-applications.
A. Register sub-applications
Register the sub-application in the main.ts of the newly created vue3 project:
// 开发模式时,entry的值为子应用的开发演示环境的地址
if ("development" === import.meta.env.MODE) {
registerMicroApps([
{
name: "app_01",
entry: "//localhost:8081/",
container: "#container",
activeRule: "/app_01",
},
{
name: "app_02",
entry: "//localhost:8082/",
container: "#container",
activeRule: "/app_02",
},
]);
} else {
// 生产环境时,entry的路径为app在部署时的真实路径
registerMicroApps([
{
name: "app_01",
entry: "./sub/app-01",
container: "#container",
activeRule: "/app_01",
},
{
name: "app_02",
entry: "./sub/app-02",
container: "#container",
activeRule: "/app_02",
},
]);
}
setDefaultMountApp("/app_01");
// 启动 qiankun
start();
When registering sub-applications, there are two modes, development mode and deployment mode, and the corresponding entry
values are different.
B. Sub-application routing
<a @click="toApp('/app_01')">app 01</a>
function toApp(path: string) {
history.pushState({
}, "", path);
}
It should be noted that the href of the a tag cannot be used here, and a 404 error will be reported, and history.pushState
the control route must be used.
Because href
the attribute will cause the browser to refresh, the resource cannot be obtained.
2.2 Sub-applications
A. Install dependencies
pnpm add vite-plugin-qiankun
vite.config.js
Configuration file modification
export default defineConfig({
// 打包时,这里填充的为绝对路径,对应的是部署路径
base: "/sub/app-01",
plugins: [
vue(),
qiankun("app-01", {
useDevMode: true,
}),
],
});
C. Entrance Retrofit
There are two ways to start:
- start alone
- Start in the main application
qiankun requires sub-applications to export three interfaces:
- bootstrap
- mount
- unmount
import {
renderWithQiankun,
qiankunWindow,
} from "vite-plugin-qiankun/dist/helper";
import {
App as VueApp, createApp } from "vue";
import router from "./router";
import App from "./App.vue";
let app: VueApp<Element>;
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
createApp(App).use(router).mount("#app");
} else {
renderWithQiankun({
mount(props) {
console.log("--app 01 mount");
app = createApp(App);
app.use(router);
app.mount(
(props.container
? props.container.querySelector("#app")
: document.getElementById("app")) as Element
);
},
bootstrap() {
console.log("--app 01 bootstrap");
},
update() {
console.log("--app 01 update");
},
unmount() {
console.log("--app 01 unmount");
app?.unmount();
},
});
}
3. Deployment
After the three projects are packaged in sequence, main-app
create a new sub folder in the packaged output, and move the packaged output of the sub-application to the sub folder. structure:
.
├── index.html
├── static
│ ├── index-011eeef2.css
│ └── index-0ab867b1.js
├── sub
│ ├── app-01
│ │ ├── index.html
│ │ ├── static
│ │ │ ├── index-0244ff29.js
│ │ │ └── index-83c9dd61.css
│ │ └── vite.svg
│ └── app-02
│ ├── index.html
│ ├── static
│ │ └── index-cb440182.js
│ └── vite.svg
└── vite.svg
Start a static web service locally to access the page, for example, use serve
the command to start the service:
serve . -p 5500
Access in browser: http://localhost:5500
Four, pits
001. When the main application registers the App, activeRule
there are two modes
hash mode
const getActiveRule = (hash) => (location) => location.hash.startsWith(hash);
registerMicroApps([
{
name: "app-hash",
entry: "http://localhost:8080",
container: "#container",
activeRule: getActiveRule("#/app-hash"),
// 这里也可以直接写 activeRule: '#/app-hash',但是如果主应用是 history 模式或者主应用部署在非根目录,这样写不会生效。
},
]);
history mode
registerMicroApps([
{
name: "app",
entry: "http://localhost:8080",
container: "#container",
activeRule: "/app",
},
]);
002. history
How to control the switching of sub-applications when the main application is in use
In history mode, the main application will monitor location.pathname
the changes to switch the loading and unloading of sub-applications.
In the main application, when using a sticky note to switch applications:
<!-- 开发环境时,没有问题 -->
<!-- 部署环境时,会报错:/app_01 404的错误 -->
<a href="/app_01">app 01</a>
The reason for 404, during static deployment: the a tag will trigger the refresh of the browser. After the refresh, the browser initiates a request to the background /app_01, and the background does not have this physical path
Improvement scheme, use history.pushState
the interface:
<a @click="toApp('/app_01')">app 01</a>
function toApp(path: string) {
history.pushState({
}, "", path);
}
The method used history.pushState
will not trigger the browser's refresh behavior. When the browser pathname
changes, qiankun
it will sense the route change and load the corresponding page.
At this point, if F5
the refresh operation is not actively performed, everything is normal. But F5
after using it, an error 404 will still be reported. At this time, the background routing needs to cooperate. Take Nginx as an example:
server {
listen 8080;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /child/vue-history {
root html;
index index.html index.htm;
try_files $uri $uri/ /child/vue-history/index.html;
}
}
6. Source code
Source address: https://github.com/swlws/qiankun-vite-vue3