Micro front-end introduction, we have seen a lot of, here is not much to introduce, directly start demo, quick start, even grandma will learn cough up! (Project code at the end of the article) ~
Initialize the project
- Initialize the primary application
Initialize the project (check vue Router and VUex during installation)
vue create main-app
Copy the code
Install UI frame Element-UI, micro front end frame Qiankun
npm i element-ui qiankun --save
Copy the code
- Initializer application
Initialize the project (check vue Router and VUex during installation)
vue create child-app
Copy the code
Install the UI framework Element-UI
npm i element-ui --save
Copy the code
Basic version
Write the main application code
- /src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'vue'.// Name of the child application
entry: '//localhost:10000'.// Subapplication entry
container: '#vue'.// The location of the child application in the main application
activeRule: '/vue'.// The routing rule activated by the subapplication}]); start();new Vue({
router,
store,
render: h= > h(App)
}).$mount('#app')
Copy the code
Code details:
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Copy the code
Introducing the element – the UI
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'vue'.// Name of the child application
entry: '//localhost:10000'.// Subapplication entry
container: '#vue'.// The location of the child application in the main application
activeRule: '/vue'.// The routing rule activated by the subapplication}]); start();Copy the code
There are two loading methods for microapplications, one is based on routing and the other is based on manual loading. We use the first method here.
Use registerMicroApps for configuration. registerMicroApps(apps, lifeCycles?) Two parameters are received. The first required parameter is the registration information for the micro application. BeforeLoad, beforeMount, afterMount, beforeUnmount, afterUnmount, afterUnmount, afterUnmount. In order to make the demo easier to understand, this parameter is left out and only the first parameter is used.
Use Start to open Qiankun
- /src/App.vue
<template>
<div>
<el-menu :router="true" mode="horizontal" default-active="/">
<el-menu-item index="/">Home</el-menu-item>
<el-menu-item index="/about">About</el-menu-item>
<el-menu-item index="/vue">Vue</el-menu-item>
</el-menu>
<router-view></router-view>
<div id="vue"></div>
</div>
</template>
Copy the code
Code details:
<el-menu :router="true" mode="horizontal" default-active="/">
<el-menu-item index="/">Home</el-menu-item>
<el-menu-item index="/about">About</el-menu-item>
<el-menu-item index="/vue">Vue</el-menu-item>
</el-menu>
Copy the code
The menu bar provided by Element-UI
<router-view></router-view>
Copy the code
Acts as a routing component container for the host application itself
<div id="vue"></div>
Copy the code
The child application container must be the same as the container registered by registerMicroApps in main.js.
- /src/views/Home.vue
<template>
<div class="home">
<h1>The main application of Home</h1>
</div>
</template>
Copy the code
- /src/views/About.vue
<template>
<div class="about">
<h1>The main application About</h1>
</div>
</template>
Copy the code
Write child application code
- /src/main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Vue.config.productionTip = false
let instance;
function render() {
// Load the vue instance
instance = new Vue({
router,
store,
render: h= > h(App)
}).$mount('#app')}// Run the microapplication independently
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
// When used by the main application
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
export async function bootstrap() {}
export async function mount(props) {
render(props)
}
export async function unmount(props) {
instance.$destroy()
instance = null
}
Copy the code
Code details:
let instance;
function render() {
// Load the vue instance
instance = new Vue({
router,
store,
render: h= > h(App)
}).$mount('#app')}Copy the code
There are two reasons for using a render function to wrap the process of the original instance Vue. One is that we need to render the child application when the main application needs to render the child application, and the other is that we need to support running the child application independently, so we need to make the rendering process controllable. Call the render function when you need to render
// Run the microapplication independently
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
Copy the code
The global variable window.__powered_by_QIANkun__ can be used to distinguish whether the sub-application is currently running in the context of the main application of Qiankun. If the global variable does not exist, it is shown that the sub-application should be running independently, so the render function is called
// When used by the main application
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
Copy the code
Our main application uses port 8080, and our child application uses port 10000. If this were not set, our requests to the child application route would start with http://localhost:8080 instead of the correct http://localhost:10000.
Runtime publicPath resolves the problem that scripts, styles, and images loaded dynamically by microapplications are not correctly addressed.
export async function bootstrap() {}
export async function mount() {
render()
}
export async function unmount() {
instance.$destroy()
}
Copy the code
According to the regulations, microapplications need to export the bootstrap, mount, and unmount lifecycle hooks in their own entry JS files for the main application to call at the appropriate time.
bootstrap
It is called only once when the microapplication is initialized and directly the next time the microapplication re-entersmount
Hook that will not trigger againbootstrap
. Usually we can do some initialization of global variables here, such as not inunmount
Phase destroyed application level cache, etc.- The application is called every time it enters
mount
Method, where we usually trigger the application’s render method - Every time the application
Cut/unload
This is where we normally unload the application instance of the microapplication
Therefore, we render the child application in mount and unmount the child application in unmount.
- /src/App.vue
<template>
<div class="child-app">
<el-menu :router="true" default-active="/" class="child-menu">
<el-menu-item index="/">Home</el-menu-item>
<el-menu-item index="about">About</el-menu-item>
</el-menu>
<router-view />
</div>
</template>
<style scoped>
.child-app {
width: 400px;
display: flex;
border: 1px solid black;
margin-top: 20px;
padding: 10px;
}
.child-menu {
margin-right: 20px;
}
</style>
Copy the code
- /src/view/Home.vue
<template>
<div class="home">
<h2>Son Home application</h2>
</div>
</template>
Copy the code
- /src/views/About.vue
<template>
<div class="about">
<h2>The child used the About</h2>
</div>
</template>
Copy the code
- /src/router/index.js
/ /... const router = new VueRouter({ mode: 'history', base: window.__POWERED_BY_QIANKUN__ ? '/vue' : process.env.BASE_URL, routes }) //...Copy the code
When running as a child application, the child application should have the same routing rules as the activeRule registered by registerMicroApps in main.js, so set base to ‘/vue’.
- /vue.config.js
module.exports = {
devServer: {
port: 10000.headers: {
'Access-Control-Allow-Origin': The '*' // Cross-domain response header when the master application gets the child application}},configureWebpack: {
output: {
library: `vue`.libraryTarget: 'umd',}}}Copy the code
Since the access is from port 8080 to port 10000, we need to configure cross-domain permission. In addition, we need to add more webPack configuration so that the main application can correctly identify some information exposed by the microapplication.
Run away
At this point, the master and child apps are ready to run!
- Test subapplication
Open http://localhost:10000
If you can also see the following effect, then the child application can run independently
- Test master application
Open http://localhost:8080
If you see something like this, then both the main app and its children are working perfectly
Communication version
From the above results, we have realized the basic use of micro-front-end and successfully embedded the sub-application into the main application. However, in our project, we often involve some scenes that require the communication between the main application and the sub-application. Since both of them are VUE projects, we use VUEX to realize the communication.
Write the main application
- /src/main.js
// ...
registerMicroApps([
{
name: 'vue'.// app name registered
entry: '//localhost:10000'.container: '#vue'.activeRule: '/vue'.// Add the following paragraph
props: {
initState: store.state
}
}
]);
// ...
Copy the code
Pass the state of the main application, using props, to the child application, which can receive it in the exported mount function
- /src/store/index.js
// ...
mutations: {
addNum(state, n) {
state.num += n
}
}
// ...
Copy the code
- /src/App.vue
<template>
<div>
<el-menu :router="true" mode="horizontal" default-active="/">
<el-menu-item index="/">Home</el-menu-item>
<el-menu-item index="/about">About</el-menu-item>
<el-menu-item index="/vue">Vue</el-menu-item>
</el-menu>
<h1>Num: {{$store.state.num}}</h1>
<button @click="addNum(10)">Add 10 to the main application</button>
<router-view></router-view>
<div id="vue"></div>
</div>
</template>
<script>
export default {
methods: {
addNum(n) {
this.$store.commit('addNum'.10)}}}</script>
Copy the code
Writing subapplications
- /src/main.js
// ...
function render(props = {}) {
if(! store.hasModule('global')) {
// State needs to be initialized when running the microapplication independently, otherwise an error will be reported
const initState = props.initState || {
num: 0
}
// Store the parent application's data in the child application with the namespace fixed as global
const globalModule = {
namespaced: true.state: initState,
mutations: {
addNum(state, n) {
state.num += n
}
},
};
// Register a dynamic module
store.registerModule('global', globalModule);
}
// Load the vue instance
instance = new Vue({
router,
store,
render: h= > h(App)
}).$mount('#app')}// ...
export async function mount(props) {
render(props)
}
// ...
Copy the code
mount
Receives data from the master application and passes it toRender function
Render function
Judge the subapplications firststore
Is it registered in thereglobal
Module, if not available, useprops
Received from the main applicationstate
And treat it asglobal
The modulestate
Data source, dynamic registration. After that, we useglobal
The module, in fact, is to use the data passed by the parent, so as to achieve data communication.
- /src/views/Home.vue
<template>
<div class="home">
<h2>Son Home application</h2>
<h2>Num: {{$store.state.global.num}}</h2>
<button @click="addNum(10)">Main application increased by 20</button>
</div>
</template>
<script>
export default {
methods: {
addNum() {
this.$store.commit('global/addNum'.20)}}}</script>
Copy the code
Run away
At this point, the master and child apps are ready to run!
- Test subapplication
Open http://localhost:10000
As you can see, the button in the child app is also clicking normally because we gave it an initial value in the Render function
const initState = props.initState || {
num: 0
}
Copy the code
- Test master application
Open http://localhost:8080
You can see that whether you click the button in the main app or the button in the sub-app, the data can be updated synchronously and responsively, which proves that our data communication has been successfully realized!
Project code
Github.com/xiezijie439… If it helps you, please give me a little star