微前端的方案采用

一.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.未完待续