Practice of micro front end-vue3.0

Muyeyong 2021-02-23 16:12:54
practice micro end-vue3.0 end vue


Microfront -Vue3.0 practice

What is a micro front end

Micro front end is to split different functions into multiple sub applications according to different dimensions . Load these subapplications through the main application , The core lies in dismantling , Closing after dismantling .

Why use micro front end

  1. Ignore technology stack differences
  2. Subapplications can be packaged and deployed separately
  3. Compatible with old code

How to land

Single-Spa Realization

Single-SPA It's a front-end microservice JavaScript Front end solutions ( There is no processing style isolation 、js Isolation ) It realizes route hijacking and application loading .

Previous picture , This is based on Single-Spa To achieve the effect of :

  1. Create two vue engineering ( I'm through vue cli Created )
vue create parent-app // Create a parent app
vue create child-app // Create subapplication
Only... Is selected for the two project customization bable and vueRouter
 Copy code 
  1. Transform sub applications

     Subapplications need to expose bootstrap mount unmount Method
     Copy code 
    • Subapplication installation single-spa-vuenpm insatll sigle-spa-vue, stay main.js Introduced inside single-spa-vue,singleSpa It will be repackaged Vue, The returned object contains three methods that the subapplication needs to export .

    • newly build vue.config.js file ,output The setting is to package the output as umd Format , Referenced by parent app ,deveServer Set the port number of the boot

  2. Transform the parent application

    • Parent app installation single-spanpm install single-spa, The parent app needs to register and mount the child app .

      introduce single-spa,registerApplication It's a registered sub app , The first parameter is the name of the subapplication , The second parameter is a method , It is mainly the result of loading sub application package , Pay attention to the order of loading. Load the common module first , In the load app.js, Otherwise, the following error will appear . The return value contains three methods exported by the subapplication .

      The third parameter is the condition that triggers the loader application : When the route matches to the beginning subapp Just load the sub application . Of course, you can pass in custom parameters , Specific reference This web site .

      start The method is to load the registered child application .

      • App.vue You need to provide a mount point for the subapplication ,id Follow the application appOptions Of el Attributes correspond to .

    You can basically run here , But there are still some problems : The route of sub application can't be clicked and The picture in the sub route doesn't show .

  3. Detail correction

    The above problem is that the routing jump for the sub application is not based on its own jump and the path of the request resource is wrong ( The picture doesn't show )

    Add... To the routing configuration of the subapplication base Options , The solution is the problem of routing jump

    main.js Add the following judgment to the list , If referenced by the main application , The resource request path is mainly configured

    That's based on Single-Spa The micro front-end application has been realized

Single-Spa defects

be based on single-spa There are several problems in the implementation of micro front end , Style isolation and js Isolation . In the animation above , When loading subapplications , The style of the parent application is affected . Share global objects (window) It causes the pollution of the global object , Next, we will solve these two problems .

Pattern isolation

  • BEM(Block Element Modifier) Contract project prefix ( An agreement is just an agreement )
  • CSS-Modules Generate non conflicting selector names when packing
  • Shadow DOM The real sense of isolation
  • Css-in-js

All of the above methods can solve the problem that styles are not isolated , With Shadow DOM For example :

Upper figure :

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> Pattern isolation </title>
</head>
<body>
<div>
<p> I am a P label </p>
<div id="shadow"></div>
</div>
<script>
let shadowDOM = document.getElementById('shadow').attachShadow({mode:'closed'})
let pEle = document.createElement('p')
let shadowStyles = document.createElement('style')
let commonStyles = document.createElement('style')
shadowStyles.textContent = 'p{color:red}'
commonStyles.textContent = 'p{color:green}'
pEle.innerText = ' I am a shadow P'
shadowDOM.appendChild(pEle)
shadowDOM.appendChild(shadowStyles)
document.body.appendChild(commonStyles)
</script>
</body>
</html>
 Copy code 

Shadow API yes dom The object has ,shadow The elements inside are inaccessible to the outside world

Js Isolation

The sandbox : Snapshot sandbox ( Single application sandbox , Shallow copy ) Agent sandbox ( Use sandbox more ,es6 proxy)

The purpose of sandbox is to achieve logical isolation , The logic change of one application will not affect other applications

Single application

Only one application instance can use shallow copy to record the snapshot

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>js Sandbox isolation </title>
</head>
<body>
<script>
class SnapshotSandbox{
constructor(){
this.proxy = window
this.modifyProps = {} // Record the change
this.snapshot = {}
this.active()
}
active(){
for(const prop in window){
this.snapshot[prop] = window[prop]
}
Object.keys(this.modifyProps).forEach(p=>{
window[p] = this.modifyProps[p]
})
}
inactive(){
for(const prop in window){
if(this.snapshot[prop] !== window[prop]){
this.modifyProps[prop] = window[prop]
window[prop] = this.snapshot[prop] // Make sure it doesn't affect
}
}
}
}
let sandbox = new SnapshotSandbox()
function start(window){
window.a = 1,window.b = 2
console.log(window.a,window.b)
sandbox.inactive()
console.log(window.a,window.b)
sandbox.active()
console.log(window.a,window.b)
}
start(sandbox.proxy)
</script>
</body>
</html>
 Copy code 

Multiple applications

If there are multiple application instances , Can use es6 proxy Realization js Isolation

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> Multiple applications JS Isolation </title>
</head>
<body>
<script>
class ProxySandbox{
constructor(){
const rawWindow = window
const fakeWindow = {}
const proxy = new Proxy(fakeWindow,{
set(target, p ,value){
target[p] = value
return true
},
get(target, p){
return target[p] || rawWindow[p]
}
})
this.proxy = proxy
}
}
let sandbox1 = new ProxySandbox()
let sandbox2 = new ProxySandbox()
window.a = 1
function start(window,value){
window.a = value
console.log(window.a)
}
start(sandbox1.proxy,'hello sandbox1')
start(sandbox2.proxy,'hello sandbox2')
</script>
</body>
</html>
 Copy code 

Bingo......

QianKun Realization

qiankun be based on Single-SPA, Provide out of the box API(single-spa + sandbox + import-html-entry) Do it , Technology stack independent , Easy access ( image iframe).

The image above is based on qinakun2.0^ + vue3.0^ + antd-vue + mockJs + TS And so on , A main application + Two sub applications ( My code and life snapshot ), Of course, two sub applications can also run independently . Mainly from the following aspects to share :

  • Lord 、 The construction of sub application
  • Communication between applications
  • mock data
  • According to the environment (local、develop、 product) Different developments of mock data
  • Access control

Building the main 、 Subapplication

​ structure

Build the main application

main.ts

import { createApp, h } from 'vue'
import { registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start } from 'qiankun'
import { message } from 'ant-design-vue'
import actions from './shared/actions'
import loadBaseUiComponent from './plugins/antd'
import loadOneUiComponent from './library/ui/install'
import OneUi from './library/ui'
import App from './App.vue'
import router from './router'
import store from './store'
let app: any = null
const msg = {
data: [],
uiComponent: OneUi,
actions,
}
function render({ appContent, loading }: { appContent?: any; loading?: any } = {}): void {
if (!app) {
app = createApp({
data() {
return {
content: appContent,
loading,
}
},
render() {
return h(App, {
props: {
content: this.content,
loading: this.loading,
},
})
},
})
} else {
app.content = appContent
app.loading = loading
}
app.use(router).use(store)
loadBaseUiComponent(app)
loadOneUiComponent(app)
app.mount('#contain')
}
render()
function getActiveRule(routerPrefix: string) {
return (location: { pathname: string }) => location.pathname.startsWith(routerPrefix)
}
registerMicroApps( // Register subapplication 
[
{
name: 'sub-app-code',
entry: '//localhost:8895',
container: '#sub-app-view',
activeRule: getActiveRule('/code'), // The conditions of activator application 
props: msg,
},
{
name: 'sub-app-life',
entry: '//localhost:8099',
container: '#sub-app-view',
activeRule: getActiveRule('/life'),
props: msg,
},
],
{
beforeLoad: [
(currentApp) => {
console.log('before load', currentApp)
return Promise.resolve()
},
], // Callback before mount 
beforeMount: [
(currentApp) => {
console.log('before mount', currentApp)
return Promise.resolve()
},
], // Callback after mount 
afterUnmount: [
(currentApp) => {
console.log('after unload', currentApp)
return Promise.resolve()
},
],
},
)
setDefaultMountApp('/code')
runAfterFirstMounted(() => {})
start()
 Copy code 

App.vue

<div class="sub-app-view" id="sub-app-view" style="height: 100%"></div> // Provide mount points
 Copy code 

be relative to single-spa Build the main application in the implementation ,qiankun It can dynamically obtain the results of sub application packaging

Build sub applications

main.ts

Three King Kong bootstrap mount unmount

/* eslint-disable no-underscore-dangle */
import { createApp } from 'vue'
import loadBaseUiComponent from './plugins/antd'
import loadOneUiComponent from './library/ui/install'
import './utils/micro/public-path'
import App from './App.vue'
import actions from './shared/actions'
import router from './router'
import store from './store'
let instance: any = null
export async function bootstrap(props = {}) {
console.log(props)
}
export async function mount(props: any) {
console.log(props)
instance = createApp(App)
.use(router)
.use(store)
loadBaseUiComponent(instance)
loadOneUiComponent(instance)
instance.mount('#sub-app-code')
}
export async function unmount() {
instance.$destroy?.()
instance = null
}
// eslint-disable-next-line no-unused-expressions
window.__POWERED_BY_QIANKUN__ || mount({})
 Copy code 

vue.config.js

And single-spa The difference is that you need to set up cross domain

 devServer: {
// host: '0.0.0.0',
hot: true,
disableHostCheck: true,
port,
overlay: {
warnings: false,
errors: true
},
headers: {
'Access-Control-Allow-Origin': '*' // Cross domain setup 
}
},
configureWebpack: {
devServer: {
open: true,
},
output: {
// Package sub applications as umd Library format 
library: 'sub-app-code',
libraryTarget: 'umd'
},
},
 Copy code 

router The configuration also depends on whether it is in qiankun Dynamic settings in the environment

const router = createRouter({
history: createWebHistory(__qiankun__ ? '/code' : '/'), // todo /code It needs to be distributed by the main application 
routes,
})
 Copy code 

single-spa Build sub applications for __webpack_public_path__ Set it up , stay qiankun You have to set it up in , Pull away to utils/micro/public-path.js Next

/* eslint-disable camelcase */
/* eslint-disable no-underscore-dangle */
// eslint-disable-next-line no-underscore-dangle
if (window.__POWERED_BY_QIANKUN__) { // window.__POWERED_BY_QIANKUN__ Whether it is qiankun Signs of the environment 
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
 Copy code 

This one is based on qiankun The micro front-end application is completed

Communication between applications

I use the official actions signal communication ,qiankun The interior provides initGlobalState Method is used to register MicroAppStateActions Examples are used for communication , This example has three methods , Namely :

  • setGlobalState: Set up globalState - When setting a new value , It will be carried out internally Shallow examination , If you find globalState A notification is triggered when a change occurs , Notice to all The observer function .
  • onGlobalStateChange: register The observer function - Respond to globalState change , stay globalState Trigger when a change occurs The observer function .
  • offGlobalStateChange: Cancel The observer function - The instance no longer responds globalState change .

The effect is globalState After change , A prompt box will appear

  1. establish actions example ( Operation in the main application )

    shared/actions.ts

    import { initGlobalState, MicroAppStateActions } from 'qiankun'
    const initialState = {}
    const actions: MicroAppStateActions = initGlobalState(initialState)
    export default actions
     Copy code 
  2. Observe globalState change

    main.ts

    function render({ appContent, loading }: { appContent?: any; loading?: any } = {}): void {
    ...
    actions.onGlobalStateChange((state, preState) => {
    console.log('new', state, 'old', preState)
    message.success(` New news reminds :${state.message}`) // When globalState A prompt box will appear for the change 
    })
    ...
    }
     Copy code 
  3. Send out actions

    At present, only the main application uses actions, You can't communicate alone solo, We need to actions Distribute to subapplications , It's also very simple when you need to pass it to the subapplication props Just pass one more parameter

    main.ts

    const msg = {
    data: [],
    uiComponent: OneUi,
    actions, //actions example 
    }
    registerMicroApps(
    [
    {
    name: 'sub-app-code',
    entry: '//localhost:8895',
    container: '#sub-app-view',
    activeRule: getActiveRule('/code'),
    props: msg, // Down to the subapplication 
    },
    {
    name: 'sub-app-life',
    entry: '//localhost:8099',
    container: '#sub-app-view',
    activeRule: getActiveRule('/life'),
    props: msg,
    },
    ],
    ...
    )
     Copy code 

    It should be noted that , In the sub application, we need to build actions class , Receive from the main application actions

    class Actions {
    actions = {
    onGlobalStateChange: (args: any) => args,
    setGlobalState: (args: any) => args,
    }
    /**
    * Set up actions
    */
    // eslint-disable-next-line no-unused-vars
    setActions(actions: { onGlobalStateChange: (args: any) => any; setGlobalState: (args: any) => any }) {
    this.actions = actions
    }
    onGlobalStateChange(args: any) {
    return this.actions.onGlobalStateChange({ ...args })
    }
    setGlobalState(args: any) {
    return this.actions.setGlobalState({ ...args })
    }
    }
    const actions = new Actions()
    export default actions
     Copy code 

    Sub application of main.ts mount Method call

    export async function mount(props: any) {
    if (props.actions) {
    actions.setActions(props.actions)
    actions.setGlobalState({ message: 'BBB' })
    }
    ...
    }
     Copy code 

    Bingo...

    mock Data and requests are intercepted according to different environments

    It uses mockjs. Create these three files in the root directory to set the flags of different environments

    for example : .env.dev-local

    VUE_APP_CURRENTMODE = 'dev-local'
    VUE_APP_ENV = ' Local development environment '
    VUE_APP_MOCK = true
     Copy code 

    Of course, it needs to be set up npm script, For example, I want to run the local environment command : npm run dev-local, It reads .env.dev-local Add the values to the global environment variables .

     "dev-local": "vue-cli-service serve --mode dev-local",
    "dev-remote": "vue-cli-service serve --mode dev-remote",
     Copy code 

    Then it can be opened dynamically according to the environment you want mock 了 , Here's how I do it , stay main.ts It calls useMock()

    function useMock() {
    console.log('process.env', process.env)
    if (process.env.VUE_APP_MOCK) {
    // eslint-disable-next-line global-require
    require('../mock')
    }
    }
     Copy code 

    Access control

    Request routing permission can be unified in the main application , And then it's distributed to the subapplication , You can control the permissions .

    I didn't write

 summary : Subapplications can be built separately , Load dynamically at run time . Lord 、 The subapplication is completely decoupled , Technology stack independent , It's protocol access ( Subapplication must export bootstrap、mount、unmount Method )
 Copy code 

ending

Why not use iframe

Users will lose their current state when they refresh the page

Source code : qiankun edition single-spa edition More articles

版权声明
本文为[Muyeyong]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/02/20210223155148666L.html

  1. RxHttp - 轻量级、可扩展、易使用、完美兼容MVVM、MVC架构的网络封装类库
  2. Rxhttp - lightweight, extensible, easy to use, perfectly compatible with MVVM, MVC architecture network encapsulation class library
  3. Rxhttp - lightweight, extensible, easy to use, perfectly compatible with MVVM, MVC architecture network encapsulation class library
  4. 前端面试常考题:JS垃圾回收机制
  5. Frequently asked questions in front end interview: JS garbage collection mechanism
  6. Rxhttp - lightweight, extensible, easy to use, perfectly compatible with MVVM, MVC architecture network encapsulation class library
  7. Java之HTTP网络编程(一):TCP/SSL网页下载
  8. HTTP network programming in Java (1): TCP / SSL web page download
  9. Java之HTTP网络编程(一):TCP/SSL网页下载
  10. HTTP network programming in Java (1): TCP / SSL web page download
  11. 使用vite搭建vue项目
  12. 在组件中展示pdf文件:vue-pdf (由于未找到方法滑动加载下一页,只能点击加载下一页pdf文件)
  13. 在 vue 中通过 express 连接数据库
  14. Using vite to build Vue project
  15. Display PDF file in component: Vue pdf
  16. Connecting database through express in Vue
  17. 2021届秋招哈啰出行前端面经(一面)
  18. vue使用sdk进行七牛云上传
  19. Javascript性能优化【内联缓存】 V8引擎特性
  20. Small true wireless smart headset evaluation: put intelligence into the ear
  21. The front end experience of the 2021 autumn recruitment
  22. Vue uses SDK to upload Qi Niu cloud
  23. 深入理解 Web 协议 (三):HTTP 2
  24. dhtmlxGantt如何重新排序任务
  25. JavaScript performance optimization [inline cache] V8 engine features
  26. 深入理解 Web 协议 (三):HTTP 2
  27. Deep understanding of Web protocol (3): http 2
  28. 深入理解 Web 协议 (三):HTTP 2
  29. How dhtmlxgantt reorders tasks
  30. 深入理解 Web 协议 (三):HTTP 2
  31. JavaScriptBOM操作
  32. JavaScriptBOM操作
  33. Deep understanding of Web protocol (3): http 2
  34. Deep understanding of Web protocol (3): http 2
  35. dhtmlxGantt甘特图重新排序任视频教程
  36. vue实现七牛云上传图片功能
  37. vue.js环境配置步骤及npm run dev报错解决方案
  38. Deep understanding of Web protocol (3): http 2
  39. JavaScript BOM operation
  40. JavaScript BOM operation
  41. Dhtmlxgantt reordering video tutorial
  42. Vue to achieve seven cattle cloud upload image function
  43. vue.js Environment configuration steps and NPM run dev error reporting solution
  44. 什么是HTTPS以及如何实施HTTPS?
  45. vue使用sdk进行七牛上传
  46. JavaScript 邮箱验证 - 正则验证
  47. Codeless development platform cloud watch sets off a "new revolution" in enterprise Digitalization
  48. Codeless development of cloud watch for enterprise digital solution
  49. What is HTTPS and how to implement it?
  50. Vue uses SDK to upload seven cows
  51. JavaScript mailbox verification - regular verification
  52. JavaScriptBOM操作
  53. JavaScript BOM operation
  54. How to create a new NPM package and publish it to the NPM community
  55. vue --tinymce 解决上传图片的方法
  56. Vue development
  57. A simple solution for vite to batch introduce SVG symbol
  58. Building 3D rendering engine from scratch with JS (2)
  59. Vue -- tinymece solution to upload pictures
  60. Common configuration of nginx