微前端 qiankun + Vite + Vue3 + Vue2

2023-03-21  本文已影响0人  Avery_G

qiankun 是什么

qiankun 是基于 single-spa 的微前端实现库,可以帮助大家更快速的构建一个前端微架构体系。

微前端是什么

官方给出的概念是:微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。简单理解就是一个大型项目中,有一个主应用,N 个子应用,各个应用之间可以独立开发,独立运行,独立部署。

为什么要用微前端

我们应该都有这样的经历,一个项目一直在更新迭代,参与的人也越来越多,代码也越来越多,导致项目越来越不好管理,打包速度越来越慢,打包体积越来越大,部署不方便。尤其是在有一个很小的需求要更新时,我们需要重新打包部署整个项目,就很头大。所以我们就需要微前端,把一个单一的应用,转换为多个可以独立开发、测试、部署的小应用。但用户是无感知的,依旧认为是一个产品。

开始搭建微前端的主应用(基座)

安装 qiankun

yarn add qiankun # 或者 npm i qiankun -S

在主应用中注册微应用

我们在 src 文件夹下新建一个 qiankun/index.ts ,用来放我们的 qiankun 的配置信息

// src/qiankun/index.ts
import {registerMicroApps, start} from 'qiankun';

registerMicroApps([
    {
        name: 'qiankun-demo-b', // 子应用的注册名称,即子应用 package.json 中的 name 
        entry: '//localhost:5174', // 子应用的启动地址
        container: '#app-b',  // 承载子应用的容器,在主应用中,有一个 ID 为 app-b 的 div,用来承载子应用
        activeRule: '/about',  // 点击哪个路由会进入子应用
    },
    /*
    * 比如,我在 app.vue 文件中注册了两个路由
    * <template>
          <nav>
            <RouterLink to="/">Home</RouterLink>
            <RouterLink to="/about">About</RouterLink>
          </nav>
        
          <RouterView />
        </template>
    * 那么这个 activeRule: '/about' 对应的就是 <RouterLink to="/about">About</RouterLink> 这个路由
    * 之后,我在 about.vue 这个文件中,添加一个承载子应用的 div,那么这个 container: '#app-b' 对应的就是 <div id="app-b"></div> 这个 div
    * <template>
          <div class="about">
            <h1>This is an about page 111</h1>
            <div id="app-b"></div>
          </div>
        </template>
    * 有多个子应用,可以按这种方式来配置多个
    * */
    {
        name: 'qiankun-demo-d',
        entry: '//localhost:8080',
        container: '#app-d',
        activeRule: '/car',
    }
]);

start();

在 main.ts 中引入

// 引入qiankun
import './qiankun/index'

微应用

Vue3 + Vite

我们先来看 Vue 微应用,Vue 微应用有一个点需要注意的就是 Vite , qiankun 与 Vite 不能一起使用,所以需要额外安装一个插件,以下是使用 Vite 时 qiankun 的配置

npm install vite-plugin-qiankun

微应用需要在自己的入口 js 导出 bootstrapmountunmount 三个生命周期钩子,以供主应用在适当的时机调用。
我们来修改子应用里的 main.ts 文件

// main.ts
import {createApp} from 'vue'
import App from './App.vue'
import router from './router'

import './assets/main.css'

import {renderWithQiankun, qiankunWindow} from 'vite-plugin-qiankun/dist/helper'

let instance: any = null
function render(props: any = {}) {
    const { container } = props
    instance = createApp(App)
    instance.use(router)
    instance?.mount(container ? container.querySelector('#app') : '#app')
    console.log('开始加载相关内容')
}
/*
* bootstrap :
* 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
* mount :
*  应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
* unmount :
*  应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
* update :
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
* */
renderWithQiankun({
    mount(props: any) {
        // 应用每次进入都会调用 mount 方法,所以我们在这里初始化一些内容
        render(props)
    },
    bootstrap() {
        console.log('微应用初始化的时候调用一次')
    },
    update() {
        console.log('update')
    },
    unmount(props: any) {
        console.log('unmount:应用每次 切出/卸载 会调用的方法', props)
        instance.unmount()
        instance._container.innerHTML = ''
        instance = null
    }
})
/*
* 通过 qiankunWindow.__POWERED_BY_QIANKUN__ 判断是不是 qiankun 渲染的,如果不是 qiankun 渲染的,需要调用以下 render 方法来初始化一些内容
* */
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
    console.log('并不是qiankun渲染')
    render()
}

打包配置修改,需要配置微应用的名字与端口号,使其与主应用配置的微应用保持一致

 // vite.config.js
import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import qiankun from 'vite-plugin-qiankun'

export default defineConfig({
    plugins: [
        vue(),
        qiankun('vue-app', { // 微应用名字,与主应用注册的微应用名字保持一致
            useDevMode: true
        })
    ],
    server: {
        port: 5174  // 微应用端口号,与主应用注册的微应用保持一致
    },
    resolve: {
        alias: {
            '@': fileURLToPath(new URL('./src', import.meta.url))
        }
    }
})

Vue3 不使用 Vite

这里我们就不需要安装任何插件了,只需在微应用的入口文件导出 bootstrapmountunmount 三个生命周期钩子,以供主应用在适当的时机调用就可以了。
同样还是修改 main.ts 文件,这里的思路和 Vue3 + Vite 的是一样的,只是写法略微不同而已

// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'


let app = null;

function render(props = {}) {
    const { container } = props;
    app = createApp(App);
    app
        .use(store)
        .use(router)
        .mount(container ? container.querySelector("#app") : "#app");
}

// 独立运行时
// 这里和 Vue3 + Vite 不太一样,是通过 window.__POWERED_BY_QIANKUN__ 来判断是否是 qiankun 渲染的
if (!window.__POWERED_BY_QIANKUN__) {
    console.log('独立运行')
    render();
}

export async function bootstrap() {
    console.log('微应用初始化的时候调用一次');
}
export async function mount(props) {
    console.log("mount", props);
    render(props);
}
export async function unmount() {
    app.unmount();
}

打包配置修改,需要配置微应用的名字与端口号,使其与主应用配置的保持一致

 // vue.config.ts
const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package');
module.exports = defineConfig({
    transpileDependencies: true,
    publicPath: "http://localhost:8080/",
    devServer: {
        port: 8080, // 微应用端口号,与主应用注册的微应用保持一致
        headers: {
            'Access-Control-Allow-Origin': '*',
        },
    },
    configureWebpack: {
        output: {
            library: `${name}-[name]`,
            libraryTarget: 'umd', // 把微应用打包成 umd 库格式
        },
    },

})

Vue2

Vue2 和 Vue3 中不使用 Vite 是一样的,唯一的区别就是 Vue2 和 Vue3 的语法不太一样,直接上代码

// main.js 
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import routes from './router';
import store from './store';

Vue.config.productionTip = false;

let router = null;
let instance = null;
function render(props = {}) {
    const { container } = props;
    router = new VueRouter({
        base: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/',
        mode: 'history',
        routes,
    });

    instance = new Vue({
        router,
        store,
        render: (h) => h(App),
    }).$mount(container ? container.querySelector('#app') : '#app');
}

// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
    render();
}

export async function bootstrap() {
    console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
    console.log('[vue] props from main framework', props);
    render(props);
}
export async function unmount() {
    instance.$destroy();
    instance.$el.innerHTML = '';
    instance = null;
    router = null;
}

打包配置修改

 // vue.config.js
const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package');
module.exports = defineConfig({
    transpileDependencies: true,
    publicPath: "http://localhost:8080/",
    devServer: {
        port: 8080,
        headers: {
            'Access-Control-Allow-Origin': '*',
        },
    },
    configureWebpack: {
        output: {
            library: `${name}-[name]`,
            libraryTarget: 'umd', // 把微应用打包成 umd 库格式
        },
    },

})

这里只介绍了子应用为 Vue 的情况,当然我们也可以创建一个 React 或者 Angular 的子应用,其配置方法 qiankun 中都有示例

上一篇下一篇

猜你喜欢

热点阅读