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
:實時編輯環境的Provider
,Context.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
進行轉譯,處理 jsx
、class
這些語法,可以理解為通過 babel
轉譯。
早期的 react-live
通過 buble
進行轉譯,能夠支持 jsx
註釋,現在由於 sucrase
不支持 jsx
註釋,所以新版無法使用 jsx
註釋來控制 jsx
渲染引擎。
/** @jsx mdx */ // 新版上述註釋會失效
隨後將轉譯的代碼通過 evalCode
轉換為 React element
,此處會將 scope
和 React
傳入 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
則是接收 Provider
的 code
、language
、theme
、disabled
、onChange
,提供編輯功能。
它的編輯器則是通過 useEditable
編輯,Prism
進行代碼高亮。
總結
上述便是 react-live
的核心代碼,內容並不多,通過 sucrase
實時編譯代碼,然後通過 new Function
構造函數註入 scope
來生成 element
實現實時預覽,設計上通過拆離 Editor
、Error
、Preview
三部分,可以讓使用者自由組合組件的位置、樣式。
以上就是React實時預覽react-live源碼解析的詳細內容,更多關於react live實時預覽的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- react進階教程之異常處理機制error Boundaries
- React中Portals與錯誤邊界處理實現
- react入門級詳細筆記
- React中的JSX { }的使用
- React報錯解決之ref返回undefined或null