关于微前端的一些浅显知识,主要是通过看别人的文章来了解,blog记录的内容和别人的文章会有重叠。
1.什么是微前端?
- 将不同的功能分成多个子应用。
- 通过主应用来加载这些子应用。
- 核心:先拆后合
2.为什么要使用微前端?
问题:不同团队间开发同一个应用,技术栈不同怎么办?
解决:将应用分为若干个子应用,再将子应用打包,当路径切换时加载不同的子应用。这样每个子应用都是独立的,子应用们的技术栈不受限制。
3.如何落地微前端?
子应用之间通信?
- 基于URL进行数据传递
- 基于CustomEvent(自定义事件)实现通信
- 基于props主子应用应用间通信(类似于父子组件通信?)
- 使用全局变量通信
总结:子应用可以独立构建,运行时动态加载,主子应用完全解耦,并且技术栈无关,靠的是协议接入。
关于qiankun
概念:qiankun是目前比较完善的一个微前端解决方案。
以下实战内容借鉴blog:https://blog.csdn.net/webyouxuan/article/details/107603165
- 主应用编写(Vue中的路由配置)
<el-menu :router="true" mode="horizontal">
<el-menu-item index="/">首页</el-menu-item>
<el-menu-item index="/vue">vue应用</el-menu-item>
<el-menu-item index="/react">react应用</el-menu-item>
</el-menu>
<router-view v-show="$route.name"></router-view>
<div v-show="!$route.name" id="vue"></div>
<div v-show="!$route.name" id="react"></div>
- 注册子应用
import {registerMicroApps,start} from 'qiankun'
const apps = [
{
name:'vueApp',
entry:'//localhost:10000',
container:'#vue',
activeRule:'/vue'
},
{
name:'reactApp',
entry:'//localhost:20000',
container:'#react',
activeRule:'/react'
}
]
registerMicroApps(apps);
start();
- 子Vue应用
let instance = null;
function render(){
instance = new Vue({
router,
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();
}
- Vue中的webpack配置文件
module.exports = {
devServer:{
port:10000,
headers:{
'Access-Control-Allow-Origin':'*' //允许访问跨域
}
},
configureWebpack:{
output:{
library:'vueApp',
libraryTarget:'umd'
}
}
}
- 子React应用(为了表明技术栈无关性,使用一个React项目)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
function render() {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
}
if(!window.__POWERED_BY_QIANKUN__){
render()
}
export async function bootstrap() {}
export async function mount() {render();}
export async function unmount() {
ReactDOM.unmountComponentAtNode(document.getElementById("root"));
}
- React中的webpack配置文件
module.exports = {
webpack: (config) => {
config.output.library = `reactApp`;
config.output.libraryTarget = "umd";
config.output.publicPath = 'http://localhost:20000/'
return config
},
devServer: function (configFunction) {
return function (proxy, allowedHost) {
const config = configFunction(proxy, allowedHost);
config.headers = {
"Access-Control-Allow-Origin": "*",
};
return config;
};
},
};
- React中的路由配置
import { BrowserRouter, Route, Link } from "react-router-dom"
const BASE_NAME = window.__POWERED_BY_QIANKUN__ ? "/react" : "";
function App() {
return (
<BrowserRouter basename={BASE_NAME}>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Route path="/" exact render={() => <h1>hello home</h1>}></Route>
<Route path="/about" render={() => <h1>hello about</h1>}></Route>
</BrowserRouter>
);
}
CSS隔离
子应用之间的样式隔离
Dynamic Stylesheet动态样式表,当应用切换时移除掉老应用样式,再添加新应用样式,保证在一个时间点内只有一个应用的样式表生效。
主应用和子应用之间样式隔离
- 约定项目前缀
- CSS-Modules 打包时生成不冲突的选择器名
- Shadow DOM 真正意义上的隔离
- css-in-js
let shadowDom = shadow.attachShadow({ mode: 'open' }); // open/close设置可否从外部获取
let pElement = document.createElement('p');
pElement.innerHTML = 'hello world';
let styleElement = document.createElement('style');
styleElement.textContent = `
p{color:red}
`
//shadow DOM 内部的元素始终不会影响到它的外部元素,可以实现真正意义上的隔离
shadowDom.appendChild(pElement);
shadowDom.appendChild(styleElement)
JS沙箱机制
沙箱(Sandboxie)的概念
- 是一个虚拟系统程序,允许你在沙盘环境中运行浏览器或其他程序,运行所产生的变化可以随后删除。
- 类似沙盒的独立作业环境,在其内部运行的程序并不能对硬盘产生永久性的影响。其为一个独立的虚拟环境,可以用来测试不受信任的应用程序或上网行为。
运行子应用的环境是内部沙箱环境。
- 当应用沙箱挂载或卸载记录快照,在切换时恢复沙箱环境
- Proxy代理沙箱,不影响全局环境
沙箱一块不太好理解,之后再对内容进行更新补充。