# 前端微服务

# 微前端是什么

微前端框架是一种类似于微服务的架构,他将微服务理念应用于浏览器端,即将 Web 应用由单一的单位应用转变为多个小型的前端应用聚合为一的应用。

                    用 户
                      |
                   底层路由
                   消息总线
                  模块加载器  // 核心,主要用于调度子应用,决定何时展示哪一个子应用,可以理解为电源
    |-----------------|------------|-------------|
Base前端基础模块    前端模块1      前端模块2       前端模块3
                      |            |             |
                  Service 1    Service 2      Service 3
【https://www.cnblogs.com/scdisplay/p/11648701.html#!comments】
电(加载器) -> 电线(包装器) -> 电器底座(主应用) -> 电器(子应用)

# 涉及到的知识

# SystemJs(加载器) // 不用这个好像也可以 用 fetch 等请求子模块之后加载

# singleSpa(包装器)

Vue 为主模块的 demo

// 主应用文件
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>demo</title>
    <script type="systemjs-importmap">
      {
        "imports":{
          "navbar": "http://localhost:8080/app.js",
          "single-spa": "https://cdnjs.cloudflare.com/ajax/libs/single-spa/4.3.7/system/single-spa.min.js"
        }
      }
    </script>
    // link rel="preload" 预加载 as="script" crossorigin="anonymous" 支持跨域
    <link rel="preload" href="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js" as="script" crossorigin="anonymous" />
    <script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/systemjs/dist/extras/amd.js"></script>
    <script>
      (function(){
        System.import("single-spa")
        .then((res)=>{
          var singleSpa = res;
          // 加载子项目入口js文件(模块加载)
          const loadingFunction = ()=> System.import("navbar");
          // 当url前缀为 /react的时候,返回true()
          // location=> location.pathname.startsWith('/nav');
          const activityFunction = location=>true;
          // 注册应用
          singleSpa.registerApplication('nav', loadingFunction, activityFunction)
          // singleSpa 启动
          singleSpa.start();
        })
      })()
    </script>
  </head>
</html>

# 子模块

# react

import React from "react";
import React from "react-dom";
import singleSpaReact from "single-spa-react";
import RootComponent from "./root.component";

if(process.env.NODE_ENV === 'develop'){
  // 开发环境直接渲染
  ReactDOM.render(<RootComponent />, document.getElementById("root"));
}
// 创造生命周期实例
const reactLifecycles = singleSpaReact({
  React,
  ReactDOM,
  rootComponent: RootComponent,
  demolementGetter: ()=>document.getElementById("root");
})
// 项目启动钩子
export const bootstrap = [
  reactLifecycles.bootstrap,
]
// 项目启动后的钩子
export const mount = [
  reactLifecycles.mount,
]
// 项目卸载的钩子
export const unmount = [
  reactLifecycles.unmount,
]

# vue-cli 3

# vue.config.js 配置

1,因为是用 systemjs 去加载模块的,所以要修改 webpack 的配置改成输出为 amd 规范

config.output.library = "single"
config.output.libraryTarget="amd"

2,配置一个 devServer 指定地址,以及设置 headers 容许跨域 3,修改输出目录,关闭 hash 命名,关闭 map

const path = require("path");
const env = process.env.NODE_ENV;
module.exports = {
  configureWebpack: config=>{
    config.output.library = "single";
    config.output.libraryTarget = "amd";
    config.entry={app:["./src/main.js"], store: "./src/Store.js"}
    if(env === "production"){

    } else {

    }
  },
  // 输出目录
  outputDir: path.resolve(__dirname, "./single"),
  // 关闭hash
  filenameHashing: false,
  // 静态文件路径
  productionSourceMap: false,
  devServer: {
    port: "8201",
    headers: {
      "Access-Control-Allow-Origin": "*",
    }
  }
}

# main.js 改造

import Vue from "vue";
import App from "./App.vue";
import singleSpaVue from "single-spa-vue";
import store from "./store/index";

Vue.config.productionTip = false

// 开发时候的
// new Vue({
//   render: h=> h(App)
// }).$mount("#app")

// single-spa配置
const vueLifecycles = singleSpaVue({
  Vue,
  appOptions: {
    el: "#vue-app",
    store,
    render: h=>h(App)
  }
})
export const bootstrap = [
  vueLifecycles.bootstrap,
];
export function mount(props){
  // redux传入Vuex
  store.commit("setGlobalEventDistributor", props.globalEventDistributor)
  store.commit("setStore", props.store)
  createdemolement();
  return vueLifecycles.mount(props);
}
export const unmount = [
  vueLifecycles.unmount,
];

function createdemolement(){
  let el = window.document.getElementById('vue-app');
  if(!el){
    el=window.document.createElement("div");
    el.id="vue-app";
    document.body.appendChild(el);
  }
  return el;
}

# Store.js 用于各模块之间的通信,后续会详细介绍

import {createStore, combineReducers} from "redux";
const initalState = {
  refresh: 0
}
function render(state = initialState, action){
  switch(action.type){
    case "REFRESH":
      return {
        ...state,
        refresh: state.refresh+1
      }
    default:
      return state
  }
}
export const storeInstance = createStore(
  combineReducers({
    namespace: ()=>"vue",
    render
  })
)