vue3+vue-router4+qiankun+vite 微前端实践落地

291 阅读1分钟

背景

主子应用前端工程同域名,不同nginx服务的微前端落地

主应用

main.js

注册子应用

registerMicroApps([{
    name: 'sub',
    entry: '/sub-asst',
    container: '#sub',  // 主视口ID
    activeRule: '/sub', // 注意entry和activeRule不能一致
    props: {
        getToken: () => getToken(),
        getRefreshToken: () => get('refresh_token'),
    },
}],{
    beforeLoad: [
        async (loadApp) => {
            window.console.log('before load', loadApp);
        },
    ],
    beforeMount: [
        async (mountApp) => {
            window.console.log('before mount', mountApp);
        },
    ],
    afterMount: [
        async (mountApp) => {
            window.console.log('after mount', mountApp, document.querySelector('#app'));
        },
    ],
    afterUnmount: [
        async (unloadApp) => {
            window.console.log('after unload', unloadApp);
        },
    ],
})

app.vue 或者主视口所在组件文件

启动子应用

start({
    excludeAssetFilter: (assetUrl) => {
        const whiteList = [];
        const whiteWords = ['baidu', 'map'];
        if (whiteList.includes(assetUrl)) {
            return true;
        }
        return whiteWords.some((w) => assetUrl.includes(w));
    },
    prefetch: true,
    sandbox: true,
    singular: true,
});

nginx配置

配置子系统静态资源代理和接口代理

    location /sub-asst {
        proxy_pass 子系统前端服务地址;
        proxy_set_header Host $host:$server_port;
        proxy_connect_timeout 30s;
        proxy_read_timeout 30s;
        proxy_send_timeout 30s;
        proxy_http_version 1.1;
    }
    location /sub/api {
        proxy_pass 子系统后端服务地址;
        ...
    }

子应用

安装vite-plugin-qiankun

npm install vite-plugin-qiankun

vite.coonfig.js

import qiankun from 'vite-plugin-qiankun';
return {
    plugins[
        ...other plugins,
        qiankun('sub', {
                useDevMode,
        })],
    base: mode.command === 'serve' ? './' : env.VITE_BUNDLE_PATH,
}

VITE_BUNDLE_PATH 配置为/sub-asst,与主应用注册时的entry保持一致

main.js


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

let app: any;
const render = (props: any) => {
    app = createApp(App);
    app.use(pinia) // pinia 存储
        .use(router) // 路由
    const { container } = props;
    let c = container ? container.querySelector('#app') : '#app';
    app.mount(c);
};
if (qiankunWindow.__POWERED_BY_QIANKUN__) {
    window.__POWERED_BY_QIANKUN__ = qiankunWindow.__POWERED_BY_QIANKUN__;
    renderWithQiankun({
        mount(props: any) {
            if (props) {
                Session.set('token', props.getToken());
                Session.set('refresh_token', props.getRefreshToken());
            }
            render(props);
        },
        bootstrap() {
            console.log('bootstrap');
        },
        unmount(props: any) {
            app.unmount();
            app = null;
            console.log(props, 'unmount');
        },
        update(props: any) {
            console.log(props.token);
            console.log(props, 'update');
        },
    });
} else {
    render({});
}

代码里再根据 window.POWERED_BY_QIANKUN 标记位隐藏菜单栏和头部菜单

request.ts

const service: AxiosInstance = axios.create({
  baseURL: '/sub/api', // 请求地址与activeRule保持一致,后续加api方便前端识别接口进行转发
  timeout: 50000, // 全局超时时间
});

nginx配置

    location / {
        rewrite /sub-asst/(.*) /$1 break;
        try_files $uri $uri/ /index.html;
    }