微前端的方案采用
一.iframe
只需简单的几句代码,就可以将子应用内嵌入主应用中,但是有各种缺点,所以不建议使用
二.single-spa
安装single-spa
npm install single-spa --save
主应用
1.需要手动的加载子应用的资源
import * as singleSpa from "single-spa"; import axios from "axios"; const runScript = (url) => { return new Promise((resolve, reject) => { const script = document.createElement("script"); script.src = url; script.onload = resolve; script.onerror = reject; const firstScript = document.getElementsByTagName("script")[0]; firstScript.parentNode.insertBefore(script, firstScript); }); }; const getManifest = (url, bundle) => new Promise(async (resolve, reject) => { const { data } = await axios.get(url); const { entrypoints, publicPath } = data; const assets = entrypoints[bundle].assets; for (let i = 0; i < assets.length; i++) { await runScript(publicPath + assets[i]).then(() => { if (i === assets.length - 1) { resolve(); } }); } }); singleSpa.registerApplication( "vue-child", async () => { let vueChild = null await getManifest("http://127.0.0.1:3000/stats.json",'app').then(resp=>{ vueChild = window.vueChild }) // await runScript("http://127.0.0.1:3000/js/chunk-vendors.js"); 如果资源较少 可以直接写死需要加载的资源 // await runScript("http://127.0.0.1:3000/js/app.js"); return vueChild; }, (location) => location.pathname.startsWith("/vue") //路由匹配规则,返回true就渲染 ); singleSpa.start();
2.设置需要挂载的节点
<div id="vue"> </div>
3.设置路由,访问路径
import VueRouter from 'vue-router' import Vue from 'vue' Vue.use(VueRouter) const routes = [{ path:'/vue', name:'vue' }] const router = new VueRouter({ mode:'history', routes }) export default router
子应用
1.main.js里面暴露出给父应用调用的方法
import Vue from "vue"; import App from "./App.vue"; import router from "./router"; import singleSpaVue from "single-spa-vue"; Vue.config.productionTip = false; const appOptions = { el: "#vue", //挂载在父应用中的id为vue的标签中 router, render: (h) => h(App), }; const vueLifeCycles = singleSpaVue({ Vue, appOptions, }); //协议接入,父应用会调用这些方法 export const bootstrap = vueLifeCycles.bootstrap; export const mount = vueLifeCycles.mount; export const unmount = vueLifeCycles.unmount; //如果是父应用引用我 动态设置子应用的publicPath if (window.singleSpaNavigate) { __webpack_public_path__ = "http://localhost:10000/"; } //没有调用我的时候,我自己可以跑起来 if (!window.singleSpaNavigate) { delete appOptions.el; new Vue(appOptions).$mount("#app"); }
2. 路由base设置成主应用访问的链接 例如 /vue
3.vue.config.js里面配置
const StatsPlugin = require("stats-webpack-plugin"); //可以统计打包的数据,方便主应用加载资源 module.exports = { publicPath: "//127.0.0.1:3000", configureWebpack: { output: { library: "vueChild", libraryTarget: "window", //或者umd }, plugins: [ new StatsPlugin('stats.json',{ chunkModules: false, entryPoints: true, source: false, chunks: false, modules: false, assets: false, children: false, exclude: [/node_modules/], }), ], }, };
4.需要手动处理css隔离,新建postcss.config.js
module.exports ={ plugins:{ 'postcss-selector-namespace':{ namespace(){ return '.single-vue-child' } } } }
single-spa最大的作用就是加载资源,其他的缺点还是挺多的,隔离这块就没有很好地处理
三.qiankun
安装qiankun
npm install qiankun --save
是踩在single-spa的肩膀上
主应用
1.注册子应用
import { registerMicroApps, start } from "qiankun"; const apps = [ { name: "vue", entry: "//localhost:8080", container: "#vue", activeRule: "/vue", }, ]; registerMicroApps(apps) start()
2.挂载子应用
<div id="vue"></div>
子应用
1.在main.js中暴露方法
let instance = null; function render() { instance = new Vue({ router, store, render: h => h(App) }).$mount("#app"); } if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } if (!window.__POWERED_BY_QIANKUN__) { render(); } export async function bootstrap() { } export async function mount(props) { render(); } export async function unmount() { instance.$destroy(); }
2.vue.config.js的配置
解决跨域问题
headers: { "Access-Control-Allow-Origin": "*" },
配置导出的文件名
configureWebpack: { output: { library: "vue", libraryTarget: "umd" }, }
四.qiankun的各种踩坑
1.路由切换css样式出错,部署后刷新404
这些是没处理主应用history模式下的各种配置问题,需要设置vue.config.js 里面的publicPath:'/',nginx配置
location / {
try_files $uri $uri/ /index.html;
}
2.微应用打包之后,css中的字体文件和图片加载404
//vue-cli3项目写法 chainWebpack(config) { // set svg-sprite-loader config.module .rule("svg") .exclude.add(resolve("src/icons")) .end(); //处理icons config.module .rule("icons") .test(/\.svg$/) .include.add(resolve("src/icons")) .end() .use("svg-sprite-loader") .loader("svg-sprite-loader") .options({ symbolId: "icon-[name]", }) .end(); //fonts config.module .rule("fonts") .use("url-loader") .loader("url-loader") .options({}) .end(); }
3.未完待续