溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

React實(shí)時(shí)預(yù)覽react-live源碼分析

發(fā)布時(shí)間:2022-08-15 16:25:17 來(lái)源:億速云 閱讀:160 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“React實(shí)時(shí)預(yù)覽react-live源碼分析”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“React實(shí)時(shí)預(yù)覽react-live源碼分析”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。

    引言

    react-live 是一個(gè) react 的實(shí)時(shí)編輯器,可直接編輯 react 代碼,并實(shí)時(shí)預(yù)覽。

    本文針對(duì)的源碼版本

    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

    源碼解讀

    輸入內(nèi)容

    先看下導(dǎo)出內(nèi)容,包括:

    • Editor:編輯器

    • LiveProvider:實(shí)時(shí)編輯環(huán)境的 Provider,Context.Provider

    • LiveEditor:實(shí)時(shí)編輯上下文的編輯器

    • LiveError:實(shí)時(shí)編輯上下文的報(bào)錯(cuò)

    • LivePreview:實(shí)時(shí)編輯上下文的預(yù)覽

    • LiveContext:實(shí)時(shí)編輯的 Context

    • withLive:實(shí)時(shí)編輯上下文的 HOC

    文件結(jié)構(gòu)和組件拆分一目了然。

    Provider

    先看下 Provider,它提供了以下內(nèi)容:

    • element:實(shí)時(shí)編輯輸出的元素

    • error:當(dāng)前的報(bào)錯(cuò)信息

    • code:當(dāng)前編輯的代碼

    • language:代碼語(yǔ)言

    • theme:代碼編輯器主題

    • disabled:是否禁用

    • onError:報(bào)錯(cuò)的回調(diào)

    • onChange:代碼編輯時(shí)的回調(diào)

    Provider 用來(lái)收集代碼變更,然后通過(guò) transpileAsync 將代碼編譯生成組件實(shí)例:

    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 可以先無(wú)視,主要是用于 noInline 模式下調(diào)用 render 進(jìn)行渲染,邏輯與非 noInline 模式下類似。

    generateElement

    實(shí)時(shí)預(yù)覽的核心部分就在這里了,它會(huì)將代碼先進(jìn)行編譯,然后執(zhí)行代碼,取得返回值。

    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);
    };

    代碼如上,它會(huì)先去掉頭尾空白,然后去掉結(jié)尾的分號(hào),這一步是為了下一步的 return 拼接能夠正常返回。通過(guò) return 拼接讓 react-live 能夠支持下述語(yǔ)法直接渲染:

    直接寫一個(gè)匿名函數(shù):

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

    直接寫 jsx

    <h4>Hello World!</h4>

    class 組件:

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

    不過(guò)也導(dǎo)致了一定的學(xué)習(xí)成本,如果寫多個(gè)函數(shù),多個(gè)組件,嵌套等情況下會(huì)讓人覺(jué)得語(yǔ)法很奇怪。

    transform 就是將代碼通過(guò) sucrase 進(jìn)行轉(zhuǎn)譯,處理 jsxclass 這些語(yǔ)法,可以理解為通過(guò) babel 轉(zhuǎn)譯。

    早期的 react-live 通過(guò) buble 進(jìn)行轉(zhuǎn)譯,能夠支持 jsx 注釋,現(xiàn)在由于 sucrase 不支持 jsx 注釋,所以新版無(wú)法使用 jsx 注釋來(lái)控制 jsx 渲染引擎。

    /** @jsx mdx */
    // 新版上述注釋會(huì)失效

    隨后將轉(zhuǎn)譯的代碼通過(guò) evalCode 轉(zhuǎn)換為 React element,此處會(huì)將 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 來(lái)構(gòu)造函數(shù),scope 就是在這里作為參數(shù)進(jìn)行注入。如果對(duì) new Function 不理解的可以看我之前一篇關(guān)于 JS 沙箱的文章。

    errorBoundary 則是一個(gè)簡(jiǎn)單的 HOC,用來(lái)捕獲生成的組件運(yùn)行時(shí)的錯(cuò)誤信息,通過(guò) 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 能夠?qū)崟r(shí)預(yù)覽的核心代碼了。下面再看下其它幾個(gè)組件,都比較簡(jiǎn)單。

    其他組件

    LivePreview 會(huì)接受 Provider 中的 Element,將其渲染。

    LiveError 接受 Provider 中的 error 進(jìn)行渲染。

    LiveEditor 則是接收 Providercode、languagetheme、disabledonChange,提供編輯功能。

    它的編輯器則是通過(guò) useEditable 編輯,Prism 進(jìn)行代碼高亮。

    讀到這里,這篇“React實(shí)時(shí)預(yù)覽react-live源碼分析”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI