前言
我 si 在做 low-code 平台时候,用户可以在平台查询物料、使用物料等,当使用某个组件时候就需要远程加载组件
远程组件加载
概述
加载远程 js 资源并渲染成组件
过程
- 通过物料 cli 的 sdk 启动一个物料项目
- 物料开发人员编写组件打包成(umd 规范)
- cli 工具将组件上传到物料平台
- 物料平台提供一个组件接口
- 浏览器端调用该接口,获取其中的内容
- 将内容渲染出来
方案
script 加载
function loadScript(url) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.setAttribute("src", url);
script.addEventListener("load", (res) => {
const component = window["xx"]; // res. library
resolve(component);
});
script.onerror = reject;
});
}
const MyComponent = () => {
useEffect(() => {
const url = "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js";
loadScript(url);
}, []);
return <div></div>;
};
优点
- 比较简单
缺点
- 组件都会挂载到 window 上造成变量污染
- 组件都会挂载到 window 上影响加载性能
沙箱隔离
function sandboxBlock(code) {
const obj = {};
// 代理了window对象
const proxyWindow = new Proxy(window, {
get(target, key) {
return target[key] || obj[key];
},
set(target, key, value) {
// new function 运行 set的值放在obj中
return (obj[key] = value);
},
});
window.proxyWindow = proxyWindow;
const codeTel = `
(function (window) {
with (window) {
${code}
}
}).call(window.proxyWindow, window.proxyWindow)
`;
new Function(codeTel)();
const targetKey = Object.keys(obj)[0];
const target = obj[targetKey];
return target.default ? target.default : target;
}
function loadScript(url) {
return fetch(url)
.then((response) => response.text())
.then((code) => {
const component = sandboxBlock(code);
return component;
});
}
const MyComponent = () => {
useEffect(() => {
const url = "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js";
loadScript(url);
}, []);
return <div></div>;
};
优点
- 自执行函数隔离出一个 js 运行的环境
- 不会去污染 window
es6 import
import("https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js").then(
(mod) => {}
);