React实时预览react-live源码解析

 更新时间:2022年08月15日 11:51:42   作者:嘿嘿Z  
这篇文章主要为大家介绍了React实时预览react-live源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

react-live 是一个 react 的实时编辑器,可直接编辑 react 代码,并实时预览。可以看下官方的预览图:

本文针对的源码版本

src
├── components
│   ├── Editor
│   │   └── index.js
│   └── Live
│       ├── LiveContext.js
│       ├── LiveEditor.js
│       ├── LiveError.js
│       ├── LivePreview.js
│       ├── LiveProvider.js
│       └── LiveProvider.test.js
├── constants
│   └── theme.js
├── hoc
│   └── withLive.js
├── index.js
└── utils
    ├── test
    │   ├── errorBoundary.test.js
    │   ├── renderer.js
    │   └── transpile.test.js
    └── transpile
        ├── errorBoundary.js
        ├── evalCode.js
        ├── index.js
        └── transform.js

源码解读

输入内容

先看下导出内容,包括:

  • Editor:编辑器
  • LiveProvider:实时编辑环境的 ProviderContext.Provider
  • LiveEditor:实时编辑上下文的编辑器
  • LiveError:实时编辑上下文的报错
  • LivePreview:实时编辑上下文的预览
  • LiveContext:实时编辑的 Context
  • withLive:实时编辑上下文的 HOC

文件结构和组件拆分一目了然。

Provider

先看下 Provider,它提供了以下内容:

  • element:实时编辑输出的元素
  • error:当前的报错信息
  • code:当前编辑的代码
  • language:代码语言
  • theme:代码编辑器主题
  • disabled:是否禁用
  • onError:报错的回调
  • onChange:代码编辑时的回调

Provider 用来收集代码变更,然后通过 transpileAsync 将代码编译生成组件实例:

function transpileAsync(newCode) {
    const errorCallback = error => {
        setState({ error: error.toString(), element: undefined });
    };
    try {
        const transformResult = transformCode ? transformCode(newCode) : newCode;
        return Promise.resolve(transformResult)
            .then(transformedCode => {
                const renderElement = element => setState({ error: undefined, element });
                // Transpilation arguments
                const input = {
                    code: transformedCode,
                    scope
                };
                if (noInline) {
                    setState({ error: undefined, element: null }); // Reset output for async (no inline) evaluation
                    renderElementAsync(input, renderElement, errorCallback);
                } else {
                    renderElement(generateElement(input, errorCallback));
                }
            })
            .catch(errorCallback);
    } catch (e) {
        errorCallback(e);
        return Promise.resolve();
    }
}

renderElementAsync 可以先无视,主要是用于 noInline 模式下调用 render 进行渲染,逻辑与非 noInline 模式下类似。

generateElement

实时预览的核心部分就在这里了,它会将代码先进行编译,然后执行代码,取得返回值。

const generateElement = ({ code = '', scope = {} }, errorCallback) => {
    // NOTE: Remove trailing semicolon to get an actual expression.
    const codeTrimmed = code.trim().replace(/;$/, '');
    // NOTE: Workaround for classes and arrow functions.
    const transformed = transform(`return (${codeTrimmed})`).trim();
    return errorBoundary(evalCode(transformed, { React, ...scope }), errorCallback);
};

代码如上,它会先去掉头尾空白,然后去掉结尾的分号,这一步是为了下一步的 return 拼接能够正常返回。通过 return 拼接让 react-live 能够支持下述语法直接渲染:

直接写一个匿名函数:

() => <h3>So functional. Much wow!</h3>;

直接写 jsx

<h3>Hello World!</h3>

class 组件:

class Comp extends React.Component {
    render() {
        return <center>component</center>;
    }
}

不过也导致了一定的学习成本,如果写多个函数,多个组件,嵌套等情况下会让人觉得语法很奇怪。

transform 就是将代码通过 sucrase 进行转译,处理 jsxclass 这些语法,可以理解为通过 babel 转译。

早期的 react-live 通过 buble 进行转译,能够支持 jsx 注释,现在由于 sucrase 不支持 jsx 注释,所以新版无法使用 jsx 注释来控制 jsx 渲染引擎。

/** @jsx mdx */
// 新版上述注释会失效

随后将转译的代码通过 evalCode 转换为 React element,此处会将 scopeReact 传入 evalCode 中。

const evalCode = (code, scope) => {
    const scopeKeys = Object.keys(scope);
    const scopeValues = scopeKeys.map(key => scope[key]);
    return new Function(...scopeKeys, code)(...scopeValues);
};

evalCode 中使用 new Function 来构造函数,scope 就是在这里作为参数进行注入。如果对 new Function 不理解的可以看我之前一篇关于 JS 沙箱的文章。

errorBoundary 则是一个简单的 HOC,用来捕获生成的组件运行时的错误信息,通过 errorCallback 抛出。

const errorBoundary = (Element, errorCallback) => {
    return class ErrorBoundary extends Component {
        componentDidCatch(error) {
            errorCallback(error);
        }
        render() {
            return typeof Element === 'function' ? <Element /> : React.isValidElement(Element) ? Element : null;
        }
    };
};

上面就是 react-live 能够实时预览的核心代码了。下面再看下其它几个组件,都比较简单。

其他组件

LivePreview 会接受 Provider 中的 Element,将其渲染。

LiveError 接受 Provider 中的 error 进行渲染。

LiveEditor 则是接收 ProvidercodelanguagethemedisabledonChange,提供编辑功能。

它的编辑器则是通过 useEditable 编辑,Prism 进行代码高亮。

总结

上述便是 react-live 的核心代码,内容并不多,通过 sucrase 实时编译代码,然后通过 new Function 构造函数注入 scope 来生成 element 实现实时预览,设计上通过拆离 EditorErrorPreview 三部分,可以让使用者自由组合组件的位置、样式。

以上就是React实时预览react-live源码解析的详细内容,更多关于react live实时预览的资料请关注脚本之家其它相关文章!

相关文章

  • 一文学会使用Remix写API接口

    一文学会使用Remix写API接口

    这篇文章主要为大家介绍了一文学会Remix写API接口实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • React组件通信的实现示例

    React组件通信的实现示例

    在React中,组件通信是一个重要的概念,它允许不同组件之间进行数据传递和交互,本文主要介绍了React组件通信的实现示例,感兴趣的可以了解一下
    2023-11-11
  • 详解React 的数据流和生命周期

    详解React 的数据流和生命周期

    这篇文章主要介绍了React 的数据流和生命周期,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • 如何深入理解React的ref 属性

    如何深入理解React的ref 属性

    关于 Refs ,React 官网讲解的对于新手来说不太友好,还是自己一字一句解读后并以代码验证的方式后真正理解的.
    2021-05-05
  • ReactNative踩坑之配置调试端口的解决方法

    ReactNative踩坑之配置调试端口的解决方法

    本篇文章主要介绍了ReactNative踩坑之配置调试端口的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • React实现倒计时功能组件

    React实现倒计时功能组件

    这篇文章主要为大家详细介绍了如何通过React实现一个倒计时功能组件,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解下
    2023-09-09
  • React学习之事件绑定的几种方法对比

    React学习之事件绑定的几种方法对比

    这篇文章主要给大家介绍了关于React学习之事件绑定的几种方法对比,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-09-09
  • 再次谈论React.js实现原生js拖拽效果引起的一系列问题

    再次谈论React.js实现原生js拖拽效果引起的一系列问题

    React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.本文给大家介绍React.js实现原生js拖拽效果,需要的朋友一起学习吧
    2016-04-04
  • react-native-fs实现文件下载、文本存储的示例代码

    react-native-fs实现文件下载、文本存储的示例代码

    本篇文章主要介绍了react-native-fs实现文件下载、文本存储的示例代码,具有一定的参考价值,有兴趣的可以了解下
    2017-09-09
  • React获取Java后台文件流并下载Excel文件流程解析

    React获取Java后台文件流并下载Excel文件流程解析

    这篇文章主要介绍了React获取Java后台文件流下载Excel文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06

最新评论