# 前端微服务
# 微前端是什么
微前端框架是一种类似于微服务的架构,他将微服务理念应用于浏览器端,即将 Web 应用由单一的单位应用转变为多个小型的前端应用聚合为一的应用。
用 户
|
底层路由
消息总线
模块加载器 // 核心,主要用于调度子应用,决定何时展示哪一个子应用,可以理解为电源
|-----------------|------------|-------------|
Base前端基础模块 前端模块1 前端模块2 前端模块3
| | |
Service 1 Service 2 Service 3
【https://www.cnblogs.com/scdisplay/p/11648701.html#!comments】
电(加载器) -> 电线(包装器) -> 电器底座(主应用) -> 电器(子应用)
# 涉及到的知识
# SystemJs(加载器) // 不用这个好像也可以 用 fetch 等请求子模块之后加载
# singleSpa(包装器)
// 主应用文件
<!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
})
)