背景:公司中台推出了一个模块,在快速搭建表单模块添加了在线代码,在线编辑代码保存后在对应页面查看效果。
得到需求后去研究了下CodePen这样的项目,太难了,看着头疼。
想到了babel-standalone这样支持在线编译的库,实现对react组件、ES6代码转为ES5以支持现代浏览器。
实现需要两个部分:
1.代码编辑器:这个网上比较多,个人比较喜欢vscode。
2.解析代码。
自己实现的解析代码部分(支持IE11):
先贴上自个的开源库及npm组件:Github、NPM组件
入口为createElement方法,最终返回一个对象,对象内包含转换后的代码transformCode以及React组件node。
import { transform as BabelTransform } from 'babel-standalone';
const getCodeFunc = (code: string, scope: { [key: string]: any }) => {
const keys = Object.keys(scope);
const values = keys.map((key) => scope[key]);
const res = new Function(...keys, code);
return res(...values);
};
const transform = (code: string) => {
const text: string = `
() => {
${code}
}
`;
const option = {
presets: ['es2015', 'react', 'stage-1'],
};
let transformCode = BabelTransform(text, option).code;
//过滤转换后的代码
return transformCode
.replace(/(\(function \(\) \{)[\r\n]/, '')
.replace(/\}\)\;$/, '');
};
/**
* es6代码转换为es5
*/
interface transformReturns {transformCode: string;node:ReactNode;};
type FuncType = (code: string, scope: { [key: string]: any }) => transformReturns;
//code代码
//scope函数作用域内支持的模块,比如传入React
export const createElement: FuncType = (code = '', scope = {}) => {
const transformCode = transform(code.trim().replace(/;$/, ''));
return { transformCode, node: getCodeFunc(transformCode, scope) };
};
这样在页面中使用createElement方法获取组件进行渲染即可,同时捕获代码中的报错信息。
import React from 'react';
import { useMemo } from 'react';
const CodeNode = ({ code, scope={} }) => {
const TransformNode = useMemo(() => {
let currNode:any = null;
try {
let { transformCode, node } = createElement(code, scope);
currNode = node;
} catch (error) {
currNode = error.toString();
}
return currNode;
}, [code]);
if(!TransformNode) return null;
//报错时输出信息
if(typeof TransformNode === 'string') return TransformNode;
//渲染组件
return <TransformNode />
}
export default () => {
//代码字符串
const code = `() => {
const {text, setText} = React.useState('');
React.useEffect(() => {
setText('hello world');
}, []);
return <div>{text}</div>
}`;
//作用域
const scope = { React };
return <CodeNode code={code} scope={scope} />;
}
存在的缺点:
- 只支持预先传入函数内可用的模块。
- 只支持行内样式。
等闲暇下来再进行改造,琢磨琢磨有啥好的方法解决模块的问题。