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實時預覽的資料請關註WalkonNet其它相關文章!

推薦閱讀: