react源碼解析-4ReactElement與ReactElementValidator

顧名思義,這兩個文件裡面的都是對於react元素的一些操作,一個用於生產環境,一個用於開發環境的校驗。

ReactElement

先不看其他的方法,我們首先來看一下ReactElement到底是什麼樣的。

ReactElement函數是一個工廠函數,創建新的react元素;不支持class模式,不要new它;instanceof也不奏效,可以檢測$$typeof是不是Symbol.for(『react.element』)。

首先創建一個element對象,參數里的type、key、ref、props、owner放進去,然後就可以把這個對象return了。

不過在開發模式下,我們在這個對象裡面額外添加了_store作為驗證標記。

再把_store、_self、_source賦值並且設置為不能重新定義屬性,不能枚舉,不能寫入,最後調用Object.freeze使對象不可再改變。

var ReactElement = function(type, key, ref, self, source, owner, props) {n var element = {n // This tag allow us to uniquely identify this as a React Elementn $$typeof: REACT_ELEMENT_TYPE,nn // Built-in properties that belong on the elementn type: type,n key: key,n ref: ref,n props: props,nn // Record the component responsible for creating this element.n _owner: owner,n };nn if (__DEV__) {n // The validation flag is currently mutative. We put it onn // an external backing store so that we can freeze the whole object.n // This can be replaced with a WeakMap once they are implemented inn // commonly used development environments.n element._store = {};nn // To make comparing ReactElements easier for testing purposes, we maken // the validation flag non-enumerable (where possible, which shouldn // include every environment we run tests in), so the test frameworkn // ignores it.n Object.defineProperty(element._store, validated, {n configurable: false,n enumerable: false,n writable: true,n value: false,n });n // self and source are DEV only properties.n Object.defineProperty(element, _self, {n configurable: false,n enumerable: false,n writable: false,n value: self,n });n // Two elements created in two different places should be consideredn // equal for testing purposes and therefore we hide it from enumeration.n Object.defineProperty(element, _source, {n configurable: false,n enumerable: false,n writable: false,n value: source,n });n if (Object.freeze) {n Object.freeze(element.props);n Object.freeze(element);n }n }nn return element;n};n

createElement

createElement(type, config, children)n

用jsx寫的代碼都會被轉換成createElement,你無需直接調用它。

type是你要創建的元素的類型,可以是html的div或者span,也可以是其他的react組件,注意大小寫。

從config中獲取props、key、ref、self、source。

向props加入children,如果是一個就放一個對象,如果是多個就放入一個數組。

那如果type.defaultProps有默認的props時,並且對應的props裡面的值是undefined,把默認值賦值到props中。

這時就可以直接return一個調用ReactElement的執行結果了。

在開發環境里,我們還會對key和ref進行校驗。

export function createElement(type, config, children) {n var propName;nn // Reserved names are extractedn var props = {};nn var key = null;n var ref = null;n var self = null;n var source = null;nn if (config != null) {n if (hasValidRef(config)) {n ref = config.ref;n }n if (hasValidKey(config)) {n key = + config.key;n }nn self = config.__self === undefined ? null : config.__self;n source = config.__source === undefined ? null : config.__source;n // Remaining properties are added to a new props objectn for (propName in config) {n if (n hasOwnProperty.call(config, propName) &&n !RESERVED_PROPS.hasOwnProperty(propName)n ) {n props[propName] = config[propName];n }n }n }nn // Children can be more than one argument, and those are transferred onton // the newly allocated props object.n var childrenLength = arguments.length - 2;n if (childrenLength === 1) {n props.children = children;n } else if (childrenLength > 1) {n var childArray = Array(childrenLength);n for (var i = 0; i < childrenLength; i++) {n childArray[i] = arguments[i + 2];n }n if (__DEV__) {n if (Object.freeze) {n Object.freeze(childArray);n }n }n props.children = childArray;n }nn // Resolve default propsn if (type && type.defaultProps) {n var defaultProps = type.defaultProps;n for (propName in defaultProps) {n if (props[propName] === undefined) {n props[propName] = defaultProps[propName];n }n }n }n if (__DEV__) {n if (key || ref) {n if (n typeof props.$$typeof === undefined ||n props.$$typeof !== REACT_ELEMENT_TYPEn ) {n var displayName =n typeof type === functionn ? type.displayName || type.name || Unknownn : type;n if (key) {n defineKeyPropWarningGetter(props, displayName);n }n if (ref) {n defineRefPropWarningGetter(props, displayName);n }n }n }n }n return ReactElement(n type,n key,n ref,n self,n source,n ReactCurrentOwner.current,n props,n );n}n

createFactory

這個就是相當於createElement的第一個參數type給制定了,返回給你個createElement函數。

export function createFactory(type) {n var factory = createElement.bind(null, type);n factory.type = type;n return factory;n}n

##cloneElement

cloneElement(element, config, children)n

返回一個克隆的新元素,擁有原始元素的props和新的props,原始元素的key和ref也會被保留。幾乎等價於

<element.type {...element.props} {...props}>{children}</element.type>n

首先將原始元素的props複製一份;

再將key、ref、self、source、owner保留;

如果config中有ref、owner、key,使用config中的;

填充defaultProps,優先使用config,其次是原始元素的;

children放到props里;

返回ReactElement。

export function cloneElement(element, config, children) {n var propName;nn // Original props are copiedn var props = Object.assign({}, element.props);nn // Reserved names are extractedn var key = element.key;n var ref = element.ref;n // Self is preserved since the owner is preserved.n var self = element._self;n // Source is preserved since cloneElement is unlikely to be targeted by an // transpiler, and the original source is probably a better indicator of then // true owner.n var source = element._source;nn // Owner will be preserved, unless ref is overriddenn var owner = element._owner;nn if (config != null) {n if (hasValidRef(config)) {n // Silently steal the ref from the parent.n ref = config.ref;n owner = ReactCurrentOwner.current;n }n if (hasValidKey(config)) {n key = + config.key;n }nn // Remaining properties override existing propsn var defaultProps;n if (element.type && element.type.defaultProps) {n defaultProps = element.type.defaultProps;n }n for (propName in config) {n if (n hasOwnProperty.call(config, propName) &&n !RESERVED_PROPS.hasOwnProperty(propName)n ) {n if (config[propName] === undefined && defaultProps !== undefined) {n // Resolve default propsn props[propName] = defaultProps[propName];n } else {n props[propName] = config[propName];n }n }n }n }nn // Children can be more than one argument, and those are transferred onton // the newly allocated props object.n var childrenLength = arguments.length - 2;n if (childrenLength === 1) {n props.children = children;n } else if (childrenLength > 1) {n var childArray = Array(childrenLength);n for (var i = 0; i < childrenLength; i++) {n childArray[i] = arguments[i + 2];n }n props.children = childArray;n }nn return ReactElement(element.type, key, ref, self, source, owner, props);n}n

isValidElement

通過$$typeof判斷一個對象是否是react元素。

export function isValidElement(object) {n return (n typeof object === object &&n object !== null &&n object.$$typeof === REACT_ELEMENT_TYPEn );n}n

ReactElementValidator

ReactElementValidator就是在開發環境下對ReactElement的方法多了一些校驗。

createElementWithValidation

首先校驗type是否是合法的:string、function、symbol、number。

校驗了子節點的key,確保每個數組中的元素都有唯一的key。

校驗了props是否符合設置的proptypes。

createFactoryWithValidation

把type設為不可枚舉,並且在get的時候警告,不建議直接訪問Factory.type

cloneElementWithValidation

校驗了子節點的key;校驗了proptypes。


推薦閱讀:

前端 ,後端 關於數據交互的問題?
關於redux在項目運用中的一些問題?
[Vue 2] 新版本探究之 performance 相關

TAG:React | 前端框架 | 源码阅读 |