低代码平台-自定义组件系统
自定义组件系统是什么
这里的自定义组件系统是基于低代码平台的能力扩展出相应的api和工具提供给用户自定义组件的能力。
为什么要写自定义组件系统
低代码平台是把很多通用的东西抽象出来的东西。那么就会诞生一个问题:如果通用能力不满足场景需要怎么办?
举个例子:低代码平台的按钮组件都是通用的,假如此时业务方想要一些符合业务方独有的组件怎么办?
解决办法:
-
低代码平台去开发
-
业务方开发
低代码平台打造的是通用平台,通用是一个基本原则。 如果前者的话,平台会夹杂一些业务方的特殊逻辑,这就违背了通用的原则,而后者的话,就没有这些问题,业务方想怎么改都是他们的事情,平台不用关心,平台只要提供对应的api和工具就可以了。
自定义组件开发步骤
-
工具和平台说明
工具名称 地址 能力 magic-publish www.npmjs.com/package/mag… 发布打包好的组件代码到静态资源服务器上 magichotreplacementplugin www.npmjs.com/package/mag… 提供组件调试时的热更新能力 远方低代码平台 ----- 多场景互动页面制作工具,可以制作类似bilibili法外狂徒交互视频這种交互形式的互动页面 -
初始化组件项目
可以使用脚手架初始化一个项目,当然你也可以自己搭一个。这里和我们平时搭项目没啥区别
-
组件项目改造
-
根目录增加component.config.json-组件描述
{ "id": "12", "name": "按钮", "img": "..." }
-
webpack配置
// 输出文件改成umd filename固定static/js/bundle.js output: { filename: "static/js/bundle.js", library: "webpackUMD", libraryTarget: "umd", libraryExport: "default", } // 增加远方代码平台的热更新组件 plugins: [ isEnvDevelopment && new MagicHotReplacementPlugin(require("../component.config.json"))
-
入口文件导出组件
import App from "./App"; export default App;
-
run start script
-
平台开启调试功能
-
调试组件 当组件项目运行时,并且开启了平台自定义功能,则会往当前场景添加你自定义的组件
-
组件发布
打包组件,然后通过magic-publish命令行工具进行发布
原理
核心逻辑
-
调试原理
-
调试更新原理
-
线上逻辑
-
核心逻辑代码实现
- 组件渲染 原理其实和微组件差不多
export const Custom: React.FC<IProps> = React.memo(({ scriptPath, id }) => {
const containRef = useRef<HTMLDivElement>(null);
const renderComponent = () => {
const current = containRef.current;
if (current) {
fetch(scriptPath)
.then(res => res.text())
.then(code => {
/**
1.伪造CommonJS
2.执行代码 - 实际上是执行一个匿名函数
3.拿到导出的组件
4.render一次
*/
const exports = {};
const module = { exports };
eval(code);
const C = module.exports as React.FunctionComponent;
ReactDOM.render(<C />, current);
});
}
};
useEffect(() => {
const current = containRef.current;
if (current) {
renderComponent();
if (dep.includes(id)) { // 发布订阅 收集组件重新打包后要执行的回调
dep.on('magicOk', renderComponent);
}
}
}, [containRef]);
return <div className={$style.cutomComponent} ref={containRef}></div>;
-
热更新原理
const express = require("express"); const http = require("http"); const cors = require("cors"); class MagicHotReplacementPlugin { constructor(componentConfig) { const app = express(); app.use(cors()); const server = http.createServer(app); //开启一个websocket服务 const io = require("socket.io")(server, { cors: { origin: "*", //前端请求地址 methods: ["GET", "POST"], credentials: true, allowEIO3: true, }, transport: ["websocket"], }); this.clientSockets = []; io.on("connection", (client) => { client.emit("ready", componentConfig); this.clientSockets.push(client); client.on("disconnect", () => { /* … */ const socketIndex = this.clientSockets.indexOf(client); this.clientSockets.splice(socketIndex, 1); }); }); server.listen(1024); } apply(compiler) { compiler.hooks.done.tap("MagicHotReplacementPlugin", () => { this.clientSockets.forEach((socket) => socket.emit("magicOk")); // 打包结束 广播 }); } } module.exports = MagicHotReplacementPlugin;
这个本来想用webpack提供的热更新能力的,但是遇到一个问题就是当代码重新打包后,HotReplacementPlugin拉取的hot-update.json路径是根据当前的host,看了一下配置发现好像没有提供配置这个路径的能力。 因此就根据热更新的原理重新实现了一个简单的更新逻辑。但是实际上性能还是没有webpack的热更新能力好。webpack的热更新可以去重新加载变更的模块,而我的是重新走一遍渲染逻辑。
-
渲染sdk的逻辑 实际上和编辑器的渲染能力一样
写在最后
其实整个系统还是在初步阶段,之后低代码平台还会继续迭代,渲染的方式也会继续探索weex、原生动态化等等。
最后愿诸君心想事成。
转载自:https://juejin.cn/post/7168103383780311070