成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

ReactElement源碼解析

mumumu / 1155人閱讀

摘要:在源碼添加的注釋在。解析的主要調(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 createFactory
export 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

相關(guān)文章

  • React源碼解析之React.createElement()和ReactElement()

    摘要:一語(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)換。 這邊是 ...

    BlackMass 評(píng)論0 收藏0
  • React源碼解析ReactDOM.render源碼

    摘要:的創(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 ...

    joywek 評(píng)論0 收藏0
  • React源碼解析之React.children.map()

    摘要:一例子看到一個(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);...

    kuangcaibao 評(píng)論0 收藏0
  • ReactDOM.render源碼解析-1

    摘要:本文將對(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...

    _Zhao 評(píng)論0 收藏0
  • React前端學(xué)習(xí)小結(jié)

    摘要:正式開(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ā)者從...

    iOS122 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<