vlambda博客
学习文章列表

前端微服务-qiankun结合Vue3的使用

微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。 qiankun 是一个基于 single-spa  的微前端 实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。

安装qiankun

$ yarn add qiankun # 或者 npm i qiankun -S

创建基座项目,并在基座项目中引入qiankun

这里我们基座项目使用的是vue项目,版本为[email protected],具体创建项目教程请参考创建vue,项目目录结构如下:

|-- qiankun |-- .DS_Store |-- .browserslistrc |-- .eslintrc.js |-- .gitignore |-- README.md |-- babel.config.js |-- package-lock.json |-- package.json |-- public | |-- favicon.ico | |-- index.html |-- src |-- .DS_Store |-- App.vue |-- main.js |-- assets | |-- logo.png |-- components | |-- HelloWorld.vue |-- router | |-- index.js |-- views |-- About.vue |-- Home.vue

创建两个子应用,分别为Vue项目和React项目

vue目录结构:

 |-- .DS_Store |-- .browserslistrc |-- .eslintrc.js |-- .gitignore |-- README.md |-- babel.config.js |-- package-lock.json |-- package.json |-- vue.config.js |-- public | |-- favicon.ico | |-- index.html |-- src |-- App.vue |-- main.js |-- assets | |-- logo.png |-- components | |-- HelloWorld.vue |-- router | |-- index.js |-- views |-- About.vue |-- Home.vue

react目录结构,react项目创建请参考创建react

 |-- .DS_Store |-- .eslintrc.js |-- .gitignore |-- README.md |-- config-overrides.js |-- package-lock.json |-- package.json |-- yarn.lock |-- public | |-- favicon.ico | |-- index.html | |-- logo192.png | |-- logo512.png | |-- manifest.json | |-- robots.txt |-- src |-- App.css |-- App.js |-- App.test.js |-- index.css |-- index.js |-- logo.svg |-- reportWebVitals.js |-- setupTests.js
  • 在基座项目中引入qiankun,修改main.js文件


    import { registerMicroApps, start } from "qiankun";const apps = [ { name: 'vueApp', entry: '//localhost:8081', // 默认会加载这个html,解析里面的js,动态执行(子应用必须支持跨域) container: '#vue', activeRule: '/vue', props: { a: 1 } }, { name: 'reactApp', entry: "//localhost:3000", container: "#react", activeRule: '/react' }]
    registerMicroApps(apps);start({ // sandbox: { experimentalStyleIsolation: true } //沙箱默认开启})createApp(App).use(ElementPlus).use(router).mount('#appBase')

    registerMicroApps的相关入参请参考入参,其中:

    • container是针对微应用挂载的节点,

    • activeRule微应用的激活规则,当配置为字符串时会直接跟 url 中的路径部分做前缀匹配,匹配成功表明当前应用会被激活。

      所以我们需要在基座项目中修改App.vue,在该项目中增加微应用的挂载节点,修改如下:

    <template> <div id="nav"> <el-menu class="el-menu-demo" :router="true" mode="horizontal"> <el-menu-item index="/">Home</el-menu-item>  <el-menu-item index="/vue">vue应用</el-menu-item>  <el-menu-item index="/react">react应用</el-menu-item> </el-menu> <router-view /> <div id="vue"></div> <div id="react"></div> </div></template>
  • 子应用修改

    我们需要根据协议,在子应用的入口文件中导出三个生命周期函数,以供主应用在适当的时机调用:

    /** * 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap */export async function bootstrap() { }/** * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法 * @param {*} props  */export async function mount(props) { console.log(props);}/** * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例 * @param {*} props  */export async function unmount(props) { console.log(app);}

    为了能让子应用也可独立运行,我们可以根据qiankun在全局注册一个变量window.__POWERED_BY_QIANKUN__,来区分子应用是否运行在qiankun中:

    // vueimport { createApp } from 'vue'import App from './App.vue'import router from './router'
    let app = null
    function render(props) { app = createApp(App).use(router) //挂载到自己的html.基座会拿到挂载后的html,直接插入 app.mount('#app')}
    if (window.__POWERED_BY_QIANKUN__) { // 动态添加publicPath __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}
    if (!window.__POWERED_BY_QIANKUN__) { // render()}
    // 子组件的协议
    /** * 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap */export async function bootstrap() { }/** * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法 * @param {*} props */export async function mount(props) { console.log(props); render(props)}/** * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例 * @param {*} props */export async function unmount(props) { console.log(app); app.unmount() // 卸载}
    // reactimport React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';

    function render() { ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') );
    }if (window.__POWERED_BY_QIANKUN__) { // 动态添加publicPath __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}
    if (!window.__POWERED_BY_QIANKUN__) { render()}


    export async function bootstrap() { };export async function mount() { render()};export async function unmount() { ReactDOM.unmountComponentAtNode(document.getElementById('root'))};

  • 子应用配置修改

    //vue vue.config.jsmodule.exports = { devServer: { headers: { 'Access-Control-Allow-Origin': '*' //支持跨域 } }, configureWebpack: { output: { library: 'vueApp', libraryTarget: 'umd',
    } }, publicPath: 'http://localhost:8081/'}
    // react config-overrides.jsmodule.exports = { webpack: (config) => { config.output.library = 'reactApp'; config.output.libraryTarget = 'umd'; config.output.publicPath = 'http://localhost:3000/' // 静态资源路径 return config }, devServer: (configFunction) => { return function (proxy, allowedHost) { const config = configFunction(proxy, allowedHost); config.headers = { "Access-Control-Allow-Origin": "*" } return config } }}