摘要:在源碼添加的注釋在。解析的主要調(diào)用如下當(dāng)然在下還有些其他的調(diào)用。此外,凍結(jié)一個(gè)對(duì)象后該對(duì)象的原型也不能被修改。方法封閉一個(gè)對(duì)象,阻止添加新屬性并將所有現(xiàn)有屬性標(biāo)記為不可配置。
前言
ReactElement并不像之前所談的PureComponent和Component那樣被頻繁的顯示使用,但我估計(jì)他應(yīng)該是在react暴露出的api中被調(diào)用最為頻繁的,關(guān)于此看完后面便知。ReactElement中暴露出createElement,createFactory,cloneElement,isValidElement,cloneAndReplaceKey五個(gè)方法,總共400來(lái)行代碼,比較容易。
文章中如有不當(dāng)之處,歡迎交流指點(diǎn)。react版本16.8.2。在源碼添加的注釋在githubreact-source-learn。jsx與ReactElement
在使用react時(shí)我們經(jīng)常在render方法返回(函數(shù)組件的話(huà)可能是直接返回)類(lèi)似下面的代碼。
測(cè)試
這就是傳說(shuō)中的jsx語(yǔ)法,js并沒(méi)有這種東西,這種語(yǔ)法最終都會(huì)被轉(zhuǎn)換成標(biāo)準(zhǔn)的js。請(qǐng)看下圖:
發(fā)現(xiàn)這些jsx被轉(zhuǎn)化成了js,每個(gè)組件或者h(yuǎn)tml標(biāo)簽轉(zhuǎn)化后都是調(diào)用React.createElement(type, config, children)。這里的React.createElement其實(shí)就是ReactElement.createElement。由此可以推測(cè),ReactElement暴露的方法是調(diào)用最頻繁的。
createElement解析createElement的主要調(diào)用如下:
createElement -> ReactElement
當(dāng)然在dev下還有些其他的調(diào)用。
createElement源碼如下
/** * Create and return a new ReactElement of the given type. * See https://reactjs.org/docs/react-api.html#createelement */ // jsx轉(zhuǎn)換后調(diào)用的方法 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 // 將config中的數(shù)據(jù)放到props中, key,ref,__self,__source除外 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. // children生成 const childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } if (__DEV__) { if (Object.freeze) { Object.freeze(childArray); } } props.children = childArray; } // Resolve default props // 復(fù)制默認(rèn)props if (type && type.defaultProps) { const defaultProps = type.defaultProps; for (propName in defaultProps) { if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } } // 這里不然從props中讀key, 和ref, 但是里邊事實(shí)上就是沒(méi)有的 if (__DEV__) { if (key || ref) { const displayName = typeof type === "function" ? type.displayName || type.name || "Unknown" : type; if (key) { // displayName: 構(gòu)造函數(shù)名, 或標(biāo)簽名 a , h1 defineKeyPropWarningGetter(props, displayName); } if (ref) { defineRefPropWarningGetter(props, displayName); } } } // 就一個(gè)普通對(duì)象 return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, ); }
createElement主要做了如下事情:
將特殊屬性從config取出, 如key,ref,__self,__source
將非特殊屬性?huà)斓絧rops上,比如上邊那個(gè)圖中的className
將第三個(gè)及之后的參數(shù)掛到props.children上,多個(gè)是生成數(shù)組,單個(gè)是直接掛
默認(rèn)值defaultProps的處理
將處理好的數(shù)據(jù)作為參數(shù)調(diào)用ReactElement并返回
ReactElement源碼如下
// 這個(gè)函數(shù)做的事非常簡(jiǎn)單, 就是將傳進(jìn)來(lái)的參放到一個(gè)對(duì)象里邊返回 // 其中source, self在生產(chǎn)模式?jīng)]有返回 // owner 變成了_owner // 開(kāi)發(fā)模式下, 返回了將source, self也掛在了返回的對(duì)象上, 變成了_source, _self const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // 一個(gè)Symobol或者16進(jìn)制數(shù), //用于表示ReactElement類(lèi)型 // Built-in properties that belong on the element type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. _owner: owner, }; // 這里邊放了self, source if (__DEV__) { // The validation flag is currently mutative. We put it on // an external backing store so that we can freeze the whole object. // This can be replaced with a WeakMap once they are implemented in // commonly used development environments. element._store = {}; // To make comparing ReactElements easier for testing purposes, we make // the validation flag non-enumerable (where possible, which should // include every environment we run tests in), so the test framework // ignores it. Object.defineProperty(element._store, "validated", { configurable: false, enumerable: false, writable: true, value: false, }); // self and source are DEV only properties. Object.defineProperty(element, "_self", { configurable: false, enumerable: false, writable: false, value: self, }); // Two elements created in two different places should be considered // equal for testing purposes and therefore we hide it from enumeration. Object.defineProperty(element, "_source", { configurable: false, enumerable: false, writable: false, value: source, }); // Object.freeze() 方法可以?xún)鼋Y(jié)一個(gè)對(duì)象。一個(gè)被凍結(jié)的對(duì)象再也不能被修改; // 凍結(jié)了一個(gè)對(duì)象則不能向這個(gè)對(duì)象添加新的屬性,不能刪除已有屬性,不能修改該對(duì)象已有屬性的可枚舉性、 // 可配置性、可寫(xiě)性,以及不能修改已有屬性的值。 // 此外,凍結(jié)一個(gè)對(duì)象后該對(duì)象的原型也不能被修改。freeze() 返回和傳入的參數(shù)相同的對(duì)象。 // Object.seal()方法封閉一個(gè)對(duì)象,阻止添加新屬性并將所有現(xiàn)有屬性標(biāo)記為不可配置。當(dāng)前屬性的值只要可寫(xiě)就可以改變。 // Object.preventExtensions()方法讓一個(gè)對(duì)象變的不可擴(kuò)展,也就是永遠(yuǎn)不能再添加新的屬性。 if (Object.freeze) { Object.freeze(element.props); Object.freeze(element); } } return element; };
他幾乎是沒(méi)做什么事情的,就是將傳入的參數(shù)放到一個(gè)對(duì)象返回,加了一個(gè)$$typeof標(biāo)識(shí)ReactElement。其中使用了一個(gè)Object.freeze方法,這個(gè)方法不太常用,意思是凍結(jié)一個(gè)對(duì)象,使其不能被修改,相關(guān)的還有Object.seal,Object.preventExtensions,可以找些文檔了解下。
小結(jié)下ReactElement.createElement
ReactElement.createElement最終返回的是一個(gè)普通的對(duì)象,對(duì)參數(shù)進(jìn)行了校驗(yàn),提取等操作。上面為解析dev下的代碼,去看一下會(huì)發(fā)現(xiàn)也是比較有趣的。
createFactory,cloneAndReplaceKey,cloneElement和isValidElement createFactoryexport function createFactory(type) { const factory = createElement.bind(null, type); // Expose the type on the factory and the prototype so that it can be // easily accessed on elements. E.g. `.type === Foo`. // This should not be named `constructor` since this may not be the function // that created the element, and it may not even be a constructor. // Legacy hook: remove it factory.type = type; return factory; // 這樣 // return function factory(...args) { // return createElement(type, ...args); // } }
這個(gè)方法很簡(jiǎn)單,是對(duì)createElement的一個(gè)柯里化的操作。
cloneAndReplaceKey// 克隆reactElement并將key改為新key export function cloneAndReplaceKey(oldElement, newKey) { const newElement = ReactElement( oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props, ); return newElement; }
從舊的ReactElement對(duì)象生成一個(gè)新的,將key屬性替換成新的
isValidElement/** * Verifies the object is a ReactElement. * See https://reactjs.org/docs/react-api.html#isvalidelement * @param {?object} object * @return {boolean} True if `object` is a ReactElement. * @final */ export function isValidElement(object) { // 還是很?chē)?yán)謹(jǐn)?shù)? return ( typeof object === "object" && object !== null && object.$$typeof === REACT_ELEMENT_TYPE ); }
判斷一個(gè)值是不是ReactElement,使用了創(chuàng)建時(shí)掛上去的$$typeof
cloneElement// 和createElement基本相同 export function cloneElement(element, config, children) { invariant( !(element === null || element === undefined), "React.cloneElement(...): The argument must be a React element, but you passed %s.", element, ); let propName; // Original props are copied const props = Object.assign({}, element.props); // Reserved names are extracted let key = element.key; let ref = element.ref; // Self is preserved since the owner is preserved. const self = element._self; // Source is preserved since cloneElement is unlikely to be targeted by a // transpiler, and the original source is probably a better indicator of the // true owner. const source = element._source; // Owner will be preserved, unless ref is overridden let owner = element._owner; if (config != null) { if (hasValidRef(config)) { // Silently steal the ref from the parent. ref = config.ref; owner = ReactCurrentOwner.current; } if (hasValidKey(config)) { key = "" + config.key; } // Remaining properties override existing props let defaultProps; if (element.type && element.type.defaultProps) { defaultProps = element.type.defaultProps; } for (propName in config) { if ( hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) ) { if (config[propName] === undefined && defaultProps !== undefined) { // Resolve default props props[propName] = defaultProps[propName]; } else { props[propName] = config[propName]; } } } } // Children can be more than one argument, and those are transferred onto // the newly allocated props object. const childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } props.children = childArray; } return ReactElement(element.type, key, ref, self, source, owner, props); }
這段代碼和createElement非常相似,不同之處在于他是返回第一個(gè)參數(shù)ReactElement的一個(gè)副本。他的key,ref等屬性和提供的需要被克隆的ReactElement的相同,props也是原來(lái)的props,但是可以傳入config修改。
/packages/react.js小結(jié)至此,/packages/react.js總的最最重要的東西已經(jīng)分析完了,關(guān)于hooks等其他內(nèi)容就像不分析了。這里邊的代碼其實(shí)并沒(méi)有做什神奇的事情,ReactElement只是創(chuàng)建和操作普通對(duì)象,Component和PureComponent只是定義了兩個(gè)簡(jiǎn)單的構(gòu)造函數(shù),定義了幾個(gè)方法,其中比較重要的應(yīng)該是updater,但是到目前為止還沒(méi)有看到他的身影。這些東西都不涉及dom操作,是平臺(tái)無(wú)關(guān)的。
這里代碼都比較好理解,后面就將進(jìn)入深水區(qū)了,要開(kāi)始研究ReactDOM里邊的render了。加油!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/103429.html
摘要:一語(yǔ)法轉(zhuǎn)換到語(yǔ)法從轉(zhuǎn)換到會(huì)用到,所以先熟悉下到的轉(zhuǎn)換。對(duì)于庫(kù)作者而言,凍結(jié)對(duì)象可防止有人修改庫(kù)的核心對(duì)象。 showImg(https://segmentfault.com/img/remote/1460000019757204); 一、JSX語(yǔ)法轉(zhuǎn)換到Js語(yǔ)法從 JSX 轉(zhuǎn)換到 JS 會(huì)用到React.createElement(),所以先熟悉下 JSX 到 JS 的轉(zhuǎn)換。 這邊是 ...
摘要:的創(chuàng)建組件,其實(shí)根源還是調(diào)用了編譯之后一般寫(xiě)法建議用來(lái)進(jìn)行源碼的跟蹤鏈接從源碼角度來(lái)看創(chuàng)建一個(gè)組件的過(guò)程中發(fā)生了什么。 https://github.com/jimwmg/Rea... 1 React.createClass( ) var HelloWorld = React.createClass({ render : function(){ return ...
摘要:一例子看到一個(gè)有趣的現(xiàn)象,就是多層嵌套的數(shù)組經(jīng)過(guò)后,平鋪成了,接下來(lái)以該例解析二作用源碼進(jìn)行基本的判斷和初始化后,調(diào)用該方法就是重命名了,即解析注意,該數(shù)組在里面滾了一圈后,會(huì)結(jié)果三作用的包裹器源碼第一次第二次如果字符串中有連續(xù)多個(gè)的話(huà) showImg(https://segmentfault.com/img/remote/1460000019968077?w=1240&h=698);...
摘要:本文將對(duì)源碼做一個(gè)初步解析。首先在方法中校驗(yàn)參數(shù)是否合法,然后調(diào)用在中,調(diào)用拿到了的一個(gè)實(shí)例,調(diào)用拿到了,用于注入到,和作為返回值,調(diào)用開(kāi)始調(diào)度過(guò)程在中,首先清理了中的所有子節(jié)點(diǎn),然后了一個(gè)并返回是如何調(diào)度的是一個(gè)什么樣的類(lèi)的操作是在哪里 初步看了react-dom這個(gè)包的一些源碼,發(fā)現(xiàn)其比react包要復(fù)雜得多,react包中基本不存在跨包調(diào)用的情況,他所做的也僅僅是定義了React...
摘要:正式開(kāi)始系統(tǒng)地學(xué)習(xí)前端已經(jīng)三個(gè)多月了,感覺(jué)前端知識(shí)體系龐雜但是又非常有趣。更新一個(gè)節(jié)點(diǎn)需要做的事情有兩件,更新頂層標(biāo)簽的屬性,更新這個(gè)標(biāo)簽包裹的子節(jié)點(diǎn)。 正式開(kāi)始系統(tǒng)地學(xué)習(xí)前端已經(jīng)三個(gè)多月了,感覺(jué)前端知識(shí)體系龐雜但是又非常有趣。前端演進(jìn)到現(xiàn)在對(duì)開(kāi)發(fā)人員的代碼功底要求已經(jīng)越來(lái)越高,幾年前的前端開(kāi)發(fā)還是大量操作DOM,直接與用戶(hù)交互,而React、Vue等MVVM框架的出現(xiàn),則幫助開(kāi)發(fā)者從...
閱讀 2078·2021-10-12 10:12
閱讀 794·2021-09-24 09:47
閱讀 1195·2021-08-19 11:12
閱讀 3482·2019-08-29 13:06
閱讀 691·2019-08-26 11:43
閱讀 2578·2019-08-23 17:20
閱讀 1156·2019-08-23 16:52
閱讀 2607·2019-08-23 14:27