React createElement方法使用原理分析介紹
摘要
在上一篇說過,React創(chuàng)建元素有兩種方式:
第一種是通過JSX方式創(chuàng)建,第二種是通過React.createElement方法創(chuàng)建。但是通過JSX創(chuàng)建React元素的方式,最終也會經(jīng)過babel進行轉(zhuǎn)換,再用React.createElement進行元素創(chuàng)建。
而這一篇文章,主要是講一下React.createElement是如何創(chuàng)建React元素的。
1.創(chuàng)建方法
為了了解React.createElement這個方法,我們自己手動去實現(xiàn)一個簡易版的。
OK,首先我們通過React.createElement方法創(chuàng)建一個React元素,并且打印出來:
const b = React.createElement('div',{data: '1234'},'oneTwo') console.log(b);
打印出來的結(jié)果為:
所以,如果我們要實現(xiàn)出來React.createElement這個方法,定義的時候我們可以這么寫:
function createElement(type, config, children){ let props = {} let myType = '' let key = '' let ref = '' return { $$typeOf: 'react.element', type: myType, props, key, ref } }
這里我們不管**$$typeOf**這個變量,只考慮其他的變量,最終返回的對象應該是這個數(shù)據(jù)結(jié)構(gòu)。
2.處理type
好了,方法的定義已經(jīng)寫完了。針對于傳進來的這三個參數(shù),我們一個一個看。
第一個參數(shù)type,它代表的是創(chuàng)建React元素的類型。
這里面可以是傳統(tǒng)的HTML元素,例如div,h1,span等標簽。
也可以是一個React組件(注意這里哦)。
而React創(chuàng)建組件又有兩種方式,所以type的值,有三種數(shù)據(jù)類型:
(1)字符串:例如"div",“h1”,“span”,"p"等標簽
(2)函數(shù):通過函數(shù)的方式創(chuàng)建React組件
(3)類:通過class的方式創(chuàng)建React組件
而這個時候就有一個問題!
class Demo { } typeof Demo
上面的值應該為什么呢?答案是function,所以在這里我們只需要考慮type為string和function兩種類型即可。
所以我們可以寫一個判斷類型的方法:
function isValidElementType(type){ if(typeof type === 'string' || typeof type === 'function'){ return true; } return false; }
在我們的主方法里面引用:
function createElement(type, config, children){ let props = {} let myType = '' let key = '' let ref = '' if(isValidElementType(type)){ myType = type; } return { $$typeOf: 'react.element', type: myType, props, key, ref } }
3.處理config
對于React.createElement方法的第二個參數(shù),接收看一個對象。而對象下所有的屬性,都會被放在props里面。
這句話對不對呢?其實也不是很對,是有特例的,如果在這個對象下,有key或者ref這兩個屬性。它是不會被放在props里面的,而是直接賦值給key和ref
Ok,有了上面的話,我們就可以對config進行處理了:
if(config != null){ if(config.ref){ ref = config.ref } if(config.key){ key = config.key } for(let propName in config){ if(!(['ref','key'].includes(propName))){ props[propName] = config[propName] } } }
我們只需要把config的key和ref分別給到我們返回對象里面的key和ref。再便利一下config,拿出來的屬性和值把key和ref排除。最終我們的config屬性就處理好了。
4.處理children
最后一步就是處理children屬性了。而React.createElement方法的第三個參數(shù),也可以是第四個參數(shù)(就是后面的所有參數(shù))。都可以為字符串,或者是React元素。
這里的React元素我們不管它是通過JSX創(chuàng)建的,還是通過React.createElement方法創(chuàng)建的都可以
而參數(shù)的情況分兩種:
第一種是只有三個參數(shù),也就是children為一個值。這個時候props里面的children就是該字符串。
第二種是參數(shù)大于三個,這個時候,props里面的children是一個數(shù)組,數(shù)組里的元素就是后面的所有參數(shù)。
OK,有了上面的基礎(chǔ),就可以對children進行處理了:
let childrenLength = arguments.length - 2 if(childrenLength === 1){ props.children = children }else if(childrenLength > 1){ let children = new Array(childrenLength) for(let i = 0;i<childrenLength;i++){ children[i] = arguments[i+2] } props.children = children }
這里通過arguments來判斷參數(shù)的個數(shù),進而確定分支條件。
然后再根據(jù)情況,確定props里的children。
最后再貼上完整的createElement方法(簡易版):
function createElement(type, config, children){ let props = {} let myType = '' let key = '' let ref = '' if(isValidElementType(type)){ myType = type; } if(config != null){ if(config.ref){ ref = config.ref } if(config.key){ key = config.key } for(let propName in config){ if(!(['ref','key'].includes(propName))){ props[propName] = config[propName] } } } let childrenLength = arguments.length - 2 if(childrenLength === 1){ props.children = children }else if(childrenLength > 1){ let children = new Array(childrenLength) for(let i = 0;i<childrenLength;i++){ children[i] = arguments[i+2] } props.children = children } return { $$typeOf: 'react.element', type: myType, props, key, ref } }
5.對比真正的React.createElement源碼
OK,上面只是實現(xiàn)了一個比較簡單的React.createElement方法,但是懂了其中的過程,我們就可以看一下真正的React.createElement源碼:
isValidElementType方法
function isValidElementType(type) { if (typeof type === 'string' || typeof type === 'function') { return true; } // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill). if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) { return true; } if (typeof type === 'object' && type !== null) { if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object // types supported by any Flight configuration anywhere since // we don't know which Flight build this will end up being used // with. type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) { return true; } } return false; }
這里面也主要就是判斷type的類型,不過判斷情況多了幾種React自帶的元素類型。
createElement方法
function createElement(type, config, children) { var propName; // Reserved names are extracted var props = {}; var key = null; var ref = null; var self = null; var source = null; if (config != null) { if (hasValidRef(config)) { ref = config.ref; { warnIfStringRefCannotBeAutoConverted(config); } } if (hasValidKey(config)) { { checkKeyStringCoercion(config.key); } 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]; } } } // Children can be more than one argument, and those are transferred onto // the newly allocated props object. var childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { var childArray = Array(childrenLength); for (var i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } { if (Object.freeze) { Object.freeze(childArray); } } props.children = childArray; } // Resolve default props if (type && type.defaultProps) { var defaultProps = type.defaultProps; for (propName in defaultProps) { if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } } { if (key || ref) { var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; if (key) { defineKeyPropWarningGetter(props, displayName); } if (ref) { defineRefPropWarningGetter(props, displayName); } } } return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props); };
這個方法主要是對config和children進行處理。
其余的部分就不粘過來了,對源碼感興趣的可以自己打斷點嘗試一哈!
到此這篇關(guān)于React createElement方法使用原理分析介紹的文章就介紹到這了,更多相關(guān)React createElement內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react redux中如何獲取store數(shù)據(jù)并將數(shù)據(jù)渲染出來
這篇文章主要介紹了react redux中如何獲取store數(shù)據(jù)并將數(shù)據(jù)渲染出來,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08react?card?slider實現(xiàn)滑動卡片教程示例
這篇文章主要為大家介紹了react?card?slider實現(xiàn)滑動卡片教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09使用 Rails API 構(gòu)建一個 React 應用程序的詳細步驟
這篇文章主要介紹了使用 Rails API 構(gòu)建一個 React 應用程序的詳細步驟,主要包括后端:Rails API部分,前端:React部分及React組件的相關(guān)操作,具有內(nèi)容詳情跟隨小編一起看看吧2021-08-08react?hooks?計數(shù)器實現(xiàn)代碼
這篇文章主要介紹了react?hooks計數(shù)器實現(xiàn)代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08