setState
是 React 框架最經常使用的命令,它是用來更新狀態的,這也是 React 框架劃時代的功能。前端
可是 setState
函數是 react
包導出的,他們又是如何與 react-dom
react-native
react-art
這些包結合的呢?react
經過 how-does-setstate-know-what-to-do 這篇文章,能夠解開這個祕密。git
setState
函數是在 React.Component
組件中調用的,因此最天然的聯想是,更新 DOM 的邏輯在 react
包中實現。github
可是 react
卻能夠和 react-dom
react-native
react-art
這些包打配合,甚至與 react-dom/server
配合在服務端運行,那能夠確定 react
包中不含有 DOM 更新邏輯。數據庫
因此能夠推斷,平臺相關的 UI 更新邏輯分佈在平臺相關的包裏,react
包只作了代理。編程
從 react 0.14 版本以後,引擎代碼就從 react 包中抽離了,react 包僅僅作通用接口抽象。小程序
也就是說,react 包定義了標準的狀態驅動模型的 API,而 react-dom
react-native
react-art
這些包是在各自平臺的具體實現。react-native
各平臺具體的渲染引擎實現被稱爲 reconciler,經過這個連接能夠看到 react-dom
react-native
react-art
這三個包的 reconciler 實現。架構
這說明了 react 包僅告訴你 React 擁有哪些語法,而並不關心如何實現他們,因此咱們須要結合 react 包與 react-xxx 一塊兒使用。框架
對於 context
,react 包僅僅會作以下定義:
// A bit simplified
function createContext(defaultValue) {
let context = {
_currentValue: defaultValue,
Provider: null,
Consumer: null
};
context.Provider = {
$$typeof: Symbol.for("react.provider"),
_context: context
};
context.Consumer = {
$$typeof: Symbol.for("react.context"),
_context: context
};
return context;
}
複製代碼
具體用到時,由 react-dom
和 react-native
決定用何種方式實現 MyContext.Provider
這個 API。
這也說明了,若是你不一樣步升級 react
與 react-dom
版本的話,就可能碰到這樣的報錯:fail saying these types are invalid,緣由是 API 定義與實現不匹配。
每一個平臺對 UI 更新邏輯的實現,會封裝在 updater
函數裏,因此不一樣平臺代碼會爲組件添加各自的 updater
實現:
// Inside React DOM
const inst = new YourComponent();
inst.props = props;
inst.updater = ReactDOMUpdater;
// Inside React DOM Server
const inst = new YourComponent();
inst.props = props;
inst.updater = ReactDOMServerUpdater;
// Inside React Native
const inst = new YourComponent();
inst.props = props;
inst.updater = ReactNativeUpdater;
複製代碼
不一樣於 props
, updater
沒法被直接調用,由於這個 API 是由 react 引擎在 setState
時調用的:
// A bit simplified
setState(partialState, callback) {
// Use the `updater` field to talk back to the renderer!
this.updater.enqueueSetState(this, partialState, callback);
}
複製代碼
關係能夠這麼描述:react
-> setState -> updater <- react-dom
等。
Hooks 的原理與 setState
相似,當調用 useState
或 useEffect
時,其內部調用以下:
// In React (simplified a bit)
const React = {
// Real property is hidden a bit deeper, see if you can find it!
__currentDispatcher: null,
useState(initialState) {
return React.__currentDispatcher.useState(initialState);
},
useEffect(initialState) {
return React.__currentDispatcher.useEffect(initialState);
}
// ...
};
複製代碼
ReactDOM 提供了 __currentDispatcher
(簡化的說法):
// In React DOM
const prevDispatcher = React.__currentDispatcher;
React.__currentDispatcher = ReactDOMDispatcher;
let result;
try {
result = YourComponent(props);
} finally {
// Restore it back
React.__currentDispatcher = prevDispatcher;
}
複製代碼
能夠看到,Hooks 的原理與 setState
基本一致,但須要注意 react
與 react-dom
之間傳遞了 dispatch
,雖然你看不到。但這個 dispatch
必須對應到惟一的 React 實例,這就是爲何 Hooks 不容許同時加載多個 React 實例的緣由。
和 updater
同樣,dispatch
也能夠被各平臺實現重寫,好比 react-debug-hooks
就重寫了 dispatcher。
因爲須要同時實現 readContext
, useCallback
, useContext
, useEffect
, useImperativeMethods
, useLayoutEffect
, useMemo
, useReducer
, useRef
, useState
,工程量比較浩大,建議瞭解基本架構就足夠了,除非你要深刻參與 React 生態建設。
與其餘 React 分析文章不一樣,本文並無過於刨根問題的上來就剖析 reconciler 實現,而是問了一個最基本的疑問:爲何 setState
來自 react
包,但實現卻在 react-dom
裏?React 是如何實現這個 magic 的?
經過這個疑問,咱們瞭解了 React 更上層的抽象能力,如何用一個包制定規範,用 N 包去實現它。
在平常編程中,接口也擁有的強大力量,下面舉幾個例子。
因爲 RN、Weex、Flux 的某些不足,愈來愈多的人選擇 「一個思想三端實現」 的方式作跨三端的 UI 組件,這樣既兼顧了性能,又能夠照顧到平臺差別性,對不一樣平臺組件細節作定製優化。
要實施這個方案,最大問題就是接口約定。必定要保證三套實現遵循同一套 API 接口,業務代碼才能夠實現 「針對任意一個平臺編寫,自動移植到其餘平臺」。
比較經常使用的作法是,經過一套統一的 API 文件約束,固定組件的輸入輸出,不一樣平臺的組件作平臺具體實現。這個思想和 React 一模一樣。
固然 RN 這些框架自己也是同一接口在不一樣平臺實現的典型,只是作的不夠完全,JS 與 Native 的通訊致使了性能不如源生。
通用數據查詢服務也比較流行,經過磨平各數據庫語法,讓用戶經過一套 SQL 查詢各類類型數據庫的數據。
這個方案中,一套通用的查詢語法就相似 React 定義的 API,執行階段會轉化爲各數據庫平臺的 SQL 方言。
如今這種方案很火。經過基於 template 或者 jsx 的語法,一鍵發佈到各平臺小程序應用。
這種方案必定會抽象一套通用語法,甚至幾乎等價與 react
與 react-dom
的關係:全部符合規範的語法,轉化爲各小程序平臺的實現。
這種分平臺實現方案與跨平臺方案仍是有很大區別的,像 JAVA 虛擬機本質仍是一套實現方案。而分平臺的實現能夠帶來最原生的性能與體驗,一樣收到的約束也最大,應該其 API 應該是全部平臺支持的一個子集。
另外,這種方案不只能夠用於 一套規範,不一樣平臺的實現,甚至能夠用在 「同一平臺的實現」。
不管是公司仍是開源節界,都有許多重複的輪子或者平臺,若是經過技術委員會約定一套平臺的實現規範,你們都遵循這個規範開發平臺,那將來就比較好作收斂,或者說收斂的第一步都是先統一 API 規範。
留下一個思考題:還有沒有利用 setState
規範與實現分離的思想案例?歡迎留下你的答案。
若是你想參與討論,請點擊這裏,每週都有新的主題,週末或週一發佈。前端精讀 - 幫你篩選靠譜的內容。