一、前言
最近研究了一些低代码开发平台,发现里面都有代码编辑的能力。所以就有一个疑问,这些代码在应用预览时/上线后是怎么执行的?
简化一下,用下面这段代码表示(这段代码只是一个字符串),就是我怎么在应用运行的时候去执行 add
或 subtract
方法。
export function add ( a, b ) {
return a + b;
}
export function subtract ( a, b ) {
return a - b;
}
二、前期的思考
2.1 正则 + eval
第一个想法是,用正则把所有的方法名及该方法对应的完整内容提取出来,然后用 eval
或者 new Function
去执行。
但是这个想法有一个问题,就是它没有方便的办法把和这个方法关联的函数也提取出来。所以就 放弃 了。
function log () {
console.log('my log');
}
export function add ( a, b ) {
log()
return a + b;
}
2.2 export + babel
然后就是发现这些平台在定义函数的时候,都使用了 export
方式导出方法,并且没有限制 ES6 语法的使用,所以就想着用 babel
试一下。
使用的是 babel在线转码的工具,转换效果为:
在看到这两行代码的时候,整个思路也就清晰了:我们可以把这段代码中 export
的方法都挂载到 exports
对象上。
exports.add = add;
exports.subtract = subtract;
三、实现思路
3.1 引入babel
使用 @babel/standalone
, 这个是在浏览器上使用的。
3.2 代码
// 编辑区的代码
const source = `
export function add ( a, b ) {
return a + b;
}
`
// 创建 exports 对象
const exports = {};
// 使用 babel 转换
const output = Babel.transform(source, {
presets: ['env']
}).code;
// 创建 function
const fn = new Function('exports', output);
// 执行fn, 并将 exports 对象传进去。执行后,add 方法就会绑定到 exports 对象上了。
fn(exports);
// 调用 exports 上的 add 方法
const result = exports['add'](1, 2);
console.log(result);
3.3 执行过程
source
转码,即output
为:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.add = add;
function add(a, b) {
return a + b;
}
const fn = new Function('exports', output);
这行代码表示的是
function fn(exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.add = add;
function add(a, b) {
return a + b;
}
}
-
执行
fn(exports)
就和执行上面那个函数是一样的,这样add
就赋值到exports
上了。 -
最终执行
exports.add
就行了。
四、其他
执行上下文的绑定
在调用的时候,可以用 bind/apply/call
这种方式去处理。
获取方法列表
Object.keys(exports)
异步方法
export async function add ( a, b ) {
return Promise.resolve(a + b);
}
考虑到兼容性的话,需要调整 babel 的配置。这个我本地没有测试,只是一个想法。
五、后记
这只是一种基础的实现方式,具体的还需要根据业务进行调整。当然 new Function
存在的安全问题和性能问题,也是需要在业务里考虑的。