React 原始碼解析之ReactElement
本小書大部分內容來自作者 Jokcy 的 《React 原始碼解析》:react.jokcy.me/
本文已同步在我的部落格:ruizhengyun.cn/blog/post/f…
感謝 Jokcy 讓我深度瞭解 React。就如他所說,在決定閱讀 React 原始碼時認為不會是一件很難的事,但是真正開始閱讀之後才發現,事情沒那麼簡答,需要足夠的耐心、獨立思考和靜下新來(因為你會碰到之前編碼沒有見過的寫法和概念等等)。
ReactElement.js
整體部分
// 保留的 props const RESERVED_PROPS = { key: true, ref: true, __self: true, __source: true, }; const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // 此標記允許我們將其唯一標識為React元素 $$typeof: REACT_ELEMENT_TYPE, // 屬於元素的內建屬性 type: type, key: key, ref: ref, props: props, // 記錄負責建立此元素的元件。 _owner: owner, }; if (__DEV__) {/*...*/} return element; } export function createElement(type, config, children) { let propName; // Reserved names are extracted(提取保留名稱) const props = {}; let key = null; let ref = null; let self = null; let source = null; if (config != null) { if (hasValidRef(config)) { ref = config.ref; } if (hasValidKey(config)) { key = '' + config.key; } self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object for (propName in config) { if ( hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) ) { props[propName] = config[propName]; } } } const childrenLength = arguments.length - 2; if( childrenLength === 1) { props.children = children; } else { const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } if (__DEV__) { /*...*/ } props.children = childArray; } return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props ); } 複製程式碼
分析createElement
函式
function createElement(type, config, children) {} 複製程式碼
-
該函式接收 3 個引數,分別是
type, config, children
; -
呼叫
ReactElement
,ReactElement
內部返回一個物件element
;
config
邏輯
if(config != null){ if(hasValidRef(config)) { ref = config.ref; } if(hasValidKey(config)){ key = '' + config.key; } self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // 剩餘的屬性將新增到新的 props 物件中 for (propName in config) { if(hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { props[propName] = config[propName]; } } } 複製程式碼
這段程式碼做了
-
對
ref
和key
做了驗證(對於這種校驗方法無需內部實現,保持乾淨,也便於閱讀); -
遍歷
config
把內建的幾個屬性(剔除原型鏈上的屬性和規定要剔除的屬性)丟到props
中去;
children
邏輯
const childrenLength = arguments.length - 2; if( childrenLength === 1) { props.children = children; } else { const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } if (__DEV__) { /*...*/ } props.children = childArray; } 複製程式碼
第一行可以看出,取出第二個引數後的引數,然後判斷長度是否> 1
:
-
> 1
就代表有多個children
,這個時候props.children
會是一個數組, 所以後面在對props.children
進行遍歷的時候需要注意它是否是陣列 ,當然你也可以利用React.Children
中的 API,下文中也會對React.Children
中的 API 進行講解; -
=== 1
就是一個物件;
返回值
const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // 此標記允許我們將其唯一標識為React元素 $$type: REACT_ELEMENT_TYPE, // 屬於元素的內建屬性 type: type, key: key, ref: ref, props: props, // 記錄負責建立此元素的元件。 _owner: owner, }; if (__DEV__) {/*...*/} return element; } 複製程式碼
核心就是通過$$type
來識別這是個ReactElement
,後會看到很多類似的型別。
注意:通過 JSX 寫的<APP />
代表ReactElement
,APP
代表 React Component (元件)。