- npm react-live-runner,在线代码实时调试库
- npm sucrase,代码编译库,这里面用于将 jsx 代码编译为
ES5 代码 - react-live-runner 源码仿写 (gitee.com)
1 整体结构
flowchart LR
开始 -- 传入`code`,`scope` --> LiveProvider
LiveContent -- 提供上下文 --> LiveContent.Provider --提供上下文对象--> LiveProvider
LiveProvider --传入code,score--> useRunner
useRunner --传入code,score--> sucrase
sucrase -- 注入 import, 编译代码 --> useRunner -- 返回 element,error --> LiveProvider
LiveProvider --上下文加入element和error--> LiveContent.Provider
LiveContent -- 提供element --> LivePreview ----> 渲染预览界面
LiveContent -- 提供code --> LiveEditor ----> 渲染编辑界面
LiveContent -- 提供error --> LiveError ----> 渲染错误界面
说明:
LiveProvider,是整个react-live-runner容器,使用LiveContent.Provider,提供上下文。上下文包括:- element,渲染的React元素
- error,编译错误的提示
- code,编辑的代码
- scope,编辑代码中的局部依赖
LivePreview,代码的实时渲染组件,由sucrase编译后,传递给useRunner,在传递给LiveProvider,渲染elementLiveEditor,代码编辑器组件,渲染codeLiveError,编译错误显示组件,渲染error
2 useRunner
evalCode是执行code的方法,import也在这里注入。
import { createElement } from "react";
import { transform as _transform } from "sucrase";
export const transform = (code) => {
return _transform(code, {
transforms: ["jsx", "typescript", "imports"],
production: true,
}).code.substring(13); // remove leading `"use strict";`
};
export function evalCode(code, scope) {
const require = (key) => {
const obj = {
...scope.import,
};
return obj[key];
};
const exports = {};
const fn = new Function("exports", "require", code);
fn(exports, require);
console.log(exports);
return exports.default;
}
export function useRunner({
code = "",
scope = {
import: {},
},
}) {
const transformCode = transform(code);
console.log(transformCode);
let element = null;
try {
element = createElement(evalCode(transformCode, scope));
} catch (error) {
// console.log(new Error(error));
return {
element,
error,
};
}
return {
element,
error: null,
};
}
3 sucrase 代码编译
编辑的源代码
import { Button } from "antd";
import React from "react";
function Com(props) {
return (
<div>
<Button type="primary">Button</Button>
demo content 123
</div>
);
}
export default Com;
通过 sucrase.transform编译编译后的代码
Object.defineProperty(exports, "__esModule", { value: true });
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var _antd = require("antd");
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
function Com(props) {
return _react2.default.createElement(
"div",
null,
_react2.default.createElement(_antd.Button, { type: "primary" }, "Button"),
"demo content 123"
);
}
exports.default = Com;
可以看出exports存在两个属性(__esModule和default),default 就是想要的 jsx 代码(函数式组件),
其中
exports.default = Com;
刚好对应了 【2】中的 18行
const exports = {};
这样就把编译后的"函数式组件"暴露在外部函数中。
其中
var _antd = require("antd");
var _react = require("react");
刚好对应了【2】中
const require = (key) => {
const obj = {
...scope.import,
};
return obj[key];
};
// 例如:require('react')
这样就解决了代码中的 import