react-live-runner 代码初探

213 阅读2分钟

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 ----> 渲染错误界面

说明:

  1. LiveProvider,是整个react-live-runner容器,使用 LiveContent.Provider,提供上下文。上下文包括:
    • element,渲染的React元素
    • error,编译错误的提示
    • code,编辑的代码
    • scope,编辑代码中的局部依赖
  2. LivePreview,代码的实时渲染组件,由 sucrase编译后,传递给 useRunner,在传递给LiveProvider ,渲染 element
  3. LiveEditor,代码编辑器组件,渲染 code
  4. LiveError,编译错误显示组件,渲染 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