【有道云笔记】微前端 note.youdao.com/s/1Nzooipm
const express = require("express"); const path = require("path");
const router = express.Router(); console.log("aaa: ", path.join(__dirname, "./")); router.use(express.static(path.join(__dirname, "./"))); router.get("/*", (req, res) => { console.log("index", req.url); res.sendFile(path.join(__dirname, req.url.split("/").slice(2).join("/"))); });
const app = express(); app.use(router); app.listen(3001, () => { console.log("server is running on port 3001"); });
Document<!-- 加载子应用的div -->
<div id="container"></div>
<!--
以前我们的js都是放到沙箱(自己实现的)中跑的 -> iframe
我们的css隔离 (scopedCSS) -> webComponent(shadowRoot)
渲染采用webComponent (拉取html模板 生成自定义组件插入到指定的dom种)
-->
<script>
const strTmpWithCSS = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="inner">hello jw</div>
<style>
div{background:red;color:#fff}
</style>
</body>
</html>
`
const strScript = `
window.a = 100; // 此属性不会影响父应用
console.log(window.a); // 100
const ele = document.querySelector('#inner')
console.log(ele);
`
function createIframe(){
const iframe = document.createElement('iframe');
iframe.src = 'about:blank'
document.body.appendChild(iframe);
return iframe
}
function createSandbox(){
const sandbox = {
iframe:createIframe(), // 创建了一个iframe沙箱
shadowRoot:null
}
return sandbox
}
function injectTemplate(sandbox,template){
const wrapper = document.createElement('div');
wrapper.innerHTML = template;
sandbox.shadowRoot.appendChild(wrapper)
}
function runScirptInSandbox(sandbox,script){
const iframeWindow = sandbox.iframe.contentWindow;
const scriptElement = iframeWindow.document.createElement('script');
// 获取head 将script插入进去
const headElement = iframeWindow.document.querySelector('head');
// 我们希望在脚本执行之前,有些方法用的是父应用的
// document.querySelector 应该用的不是iframe中的 -》 shadowRoot 去弄
// 添加弹框的时候 document.createElement().appendChild() -> 代理到全局的window上去
// iframe中的路由管理 history.pushState -> 将一些常用方法进行同步到主应用
// ......
Object.defineProperty(iframeWindow.Document.prototype, 'querySelector',{
get(){
// 加载的脚本内部调用了querySelector
// document.querySeletor('abc') -> sandbox.shadowRoot['querySelector']
return new Proxy(sandbox.shadowRoot['querySelector'],{
apply(target,thisArgs,args){
return thisArgs.querySelector.apply(sandbox.shadowRoot,args)
}
})
}
})
scriptElement.textContent = script;
headElement.appendChild(scriptElement)
}
function createCustomElement(){
class WujieApp extends HTMLElement{
connectedCallback(){
// 1)创建沙箱
const sandbox = createSandbox();
// 2)创建shadowdDOM
sandbox.shadowRoot = this.attachShadow({mode:'open'})
// 3)将html、css放入到shadowdDOM
injectTemplate(sandbox,strTmpWithCSS)
// 4)将js放入沙箱执行
runScirptInSandbox(sandbox,strScript)
}
}
window.customElements.define('wujie-app',WujieApp);
container.appendChild(document.createElement('wujie-app'))
}
// 定义一个组件来使用
createCustomElement()
</script>
笔记
-
shadowDom 里面不能是 script标签,闭合标签会和上面的父script 标签闭合;
-
puppeteer 安装失败
-
报错信息:
... ERROR: Failed to set up Chromium r563942! Set "PUPPETEER_SKIP_DOWNLOAD" env variable to skip download. Error: Download failed: server returned code 404. URL: https://mirrors.tools.huawei.com/chromium-browser-snapshots/Win_x64/563942/chrome-win32.zip ... -
报错原因:你的电脑中其他配置指定了 chrome-win32.zip 这个包的版本号为 563942,而这个版本比较老了,华为镜像仓上没有这个版本的资源。
注:上面的错误信息也有可能不是 563942 版本,但原因是一样的,就是没找到指定版本的资源。
-
解决方案:由于该npm 包是 微组件测试功能所依赖的,所以可以分两种情况来对其进行处理,一种情况是你不使用 微组件测试功能,另一种情况是你使用 微组件测试功能。
-
不使用测试功能。可以通过跳过该依赖的安装来解决,在工程的 .yarnrc 和 .npmrc 中修改下面配置:
// .yarnrc - puppeteer_skip_chromium_download false + puppeteer_skip_chromium_download true// .npmrc - puppeteer_skip_chromium_download=false + puppeteer_skip_chromium_download=true -
使用测试功能。将指定版本号的配置设置为空,让其下载最新版本的 chrome-win32.zip 包。在工程根目录的 .yarnrc 和 .npmrc 中分别新增下面配置:
// .yarnrc PUPPETEER_CHROMIUM_REVISION ""// .npmrc PUPPETEER_CHROMIUM_REVISION=
-