【微前端】微前端笔记(二)

前言

  • 第一篇的single-spa模式存在样式没有隔离,以及需要手动引入js的问题。

样式隔离方案

  • 主应用与子应用解决样式隔离一般有以下几种方案:
  • BEM 即约定项目前缀
  • CSSMoudule 打包时生成不冲突的选择器
  • shadowDom 真正意义上隔离
  • css in js

shadow dom

  • 看一个例子:
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Document</title>
	</head>
	<body>
		<div>
			<p>hellow world</p>
			<div id="shadow"></div>
		</div>
	</body>
	<script>
		let shadowDom = shadow.attachShadow({ mode: "closed" });
		let p = document.createElement("p");
		p.innerHTML = "yehuozhili";
		let style = document.createElement("style");
		style.textContent = `
            p{color:red};
        `;
		shadowDom.appendChild(style);
		shadowDom.appendChild(p);
	</script>
</html>
  • 其中影子dom 里放入了样式表,整个document有2个p标签,但是受影响的只有影子标签的p ,外界不受影响。

沙箱方案

  • proxy
  • 快照
  • 来看一个快照的例子:
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Document</title>
	</head>
	<body>
		<div>
			<p>hellow world</p>
			<div id="shadow"></div>
		</div>
	</body>
	<script>
		class SnapshotSandbox {
			constructor() {
				this.proxy = window;
				this.modifyPropsMap = {};
				this.active(); //先走了active 并且把window上属性存到了windowsnapshot上
			}
			active() {
				this.windowSnapshot = {};
				for (const prop in window) {
					if (window.hasOwnProperty(prop)) {
						this.windowSnapshot[prop] = window[prop];
					}
				}
				Object.keys(this.modifyPropsMap).forEach((p) => {//应用修改的
					window[p] = this.modifyPropsMap[p];
				});
			}
			inactive() {
				//失活 就是当前和开始进入的快照进行比对,不一样就存到修改map,同时恢复开始进入的快照
				for (const prop in window) {
					if (window.hasOwnProperty(prop)) {//不一样的属性存到修改map里
						if (window[prop] !== this.windowSnapshot[prop]) {
							this.modifyPropsMap[prop] = window[prop];
							window[prop] = this.windowSnapshot[prop];
						}
					}
				}
			}
		}

		let sandbox = new SnapshotSandbox();
		((window) => {
			window.a = 1;
			window.b = 2;
			console.log(window.a, window.b);
			sandbox.inactive();
			console.log(window.a, window.b);
			sandbox.active();
			console.log(window.a, window.b);
		})(sandbox.proxy);
	</script>
</html>
  • 其实就是浅比较window然后恢复就完了。
  • proxy方案:
class ProxySandbox {
    constructor() {
        const rawWindow = window;
        const fakeWindow = {}
        const proxy = new Proxy(fakeWindow, {
            set(target, p, value) {
                target[p] = value;
                return true
            },
            get(target, p) {
                return target[p] || rawWindow[p];//这里就是取不到会找window的
            }
        });
        this.proxy = proxy
    }
}
let sandbox1 = new ProxySandbox();
let sandbox2 = new ProxySandbox();
window.a = 1;
((window) => {
    window.a = 'hello';
    console.log(window.a)
})(sandbox1.proxy);
((window) => {
    window.a = 'world';
    console.log(window.a)
})(sandbox2.proxy);

乾坤

  • 乾坤是基于single-spa进行封装的
  • 首先,我们创建3个应用,qiankun-base,qiankun-react,qiankun-vue
  • 编写base
  • 安装element-ui 与乾坤

main.js

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import "element-ui/lib/theme-chalk/index.css";
import ElementUI from "element-ui";
import { registerMicroApps, start } from "qiankun";
Vue.config.productionTip = false;

Vue.use(ElementUI);

const apps = [
	{
		name: "vueApp", //应用名
		entry: "//localhost:10000", //默认会加载这个html,解析里面的js 动态的执行(子应用必须支持跨域)
		container: "#vue", //容器名
		activeRule: "/vue", //激活路径
	},
	{
		name: "reactApp",
		entry: "//localhost:20000",
		container: "#react",
		activeRule: "/react",
	},
];

registerMicroApps(apps); //注册应用
start(); //开启
new Vue({
	router,
	render: (h) => h(App),
}).$mount("#app");

app.vue

<template>
	<div >
		<el-menu :router="true" mode="horizontal">
			<el-menu-item index="/">Home</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></router-view>
    <div id='vue'></div>
    <div id='react'></div>
	</div>
</template>
<style>
  • 打开页面并且路由可以切换,会改变路由并且发请求就ok了。
  • 下面修改子应用:
  • 首先,需要修改子应用的前置路由:
const router = new VueRouter({
	mode: "history",
	base: "/vue",
	routes,
});
  • 修改main.js,这个方式和一的基本都是一个意思:
import Vue from "vue";
import App from "./App.vue";
import router from "./router";

Vue.config.productionTip = false;

let instance = null;
function render() {
	instance = new Vue({
		router,
		render: (h) => h(App),
	}).$mount("#app"); //仍然挂app上,父应用会拿到html将其插入。
}

if (!window.__POWERED_BY_QIANKUN__) {
	//这个就跟上一节那个独立运行一个意思
	render();
}
if (window.__POWERED_BY_QIANKUN__) {
	//这个就跟上一节那个父应用加载一个意思
	__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

export async function bootstrap() {}

export async function mount(props) {
	render(props);
}

export async function unmount(props) {
	instance.$destroy();
}
  • 修改vue.config.js,使其打包成一个库:
module.exports = {
	devServer: {
		port: 10000,
		headers: {
			"Access-Control-Allow-Origin": "*",
		},
	},
	configureWebpack: {
		output: {
			library: "vueApp",
			libraryTarget: "umd",
		},
	},
};
  • 启动子应用,可以发现,子应用可以独立运行,切换也无问题,父应用也独立运行,切换也无问题。
  • 感觉乾坤比single-spa方便很多啊。
  • 父应用可以通过配置apps传递props,子应用可以在导出方法那获取相应位置进行接收。
  • 下面配置react应用:
  • 首先安装react-app-rewried 安装react-router-dom
  • 修改package.json的script
  • 修改config-overrides.js
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;
		};
	},
};
  • 修改端口号:
PORT=20000
WDS_SOCKET_PORT=20000

app.js

import React from "react";
import "./App.css";
import { BrowserRouter, Route, Link } from "react-router-dom";

function App() {
	return (
		<BrowserRouter basename="/react">
			<Link to="/">首页</Link>
			<Link to="/about">关于页</Link>
			<Route path="/" exact render={() => <div>首页</div>}></Route>

			<Route path="/about" exact render={() => <div>关于页</div>}></Route>
		</BrowserRouter>
	);
}

export default App;

index.js

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"));
}
  • 这样就完成了3个页面互相切换且都可以独立运行或者结合运行了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

业火之理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值