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

資訊專欄INFORMATION COLUMN

React源碼系列一之createElement

Zhuxy / 2326人閱讀

前言:使用react也有二年多了,一直停留在使用層次。雖然很多時候這樣是夠了。但是總覺得不深入理解其背后是的實(shí)現(xiàn)邏輯,很難體會框架的精髓。最近會寫一些相關(guān)的一些文章,來記錄學(xué)習(xí)的過程。

備注:react和react-dom源碼版本為16.8.6 本文適合使用過React進(jìn)行開發(fā),并有一定經(jīng)驗(yàn)的人閱讀。

好了閑話少說,我們一起來看源碼吧
寫過react知道,我們使用react編寫代碼都離不開webpackbabel,因?yàn)?b>React要求我們使用的是class定義組件,并且使用了JSX語法編寫HTML。瀏覽器是不支持JSX并且對于class的支持也不好,所以我們都是需要使用webpack的jsx-loaderjsx的語法做一個轉(zhuǎn)換,并且對于ES6的語法和react的語法通過babelbabel/preset-react、babel/env@babel/plugin-proposal-class-properties等進(jìn)行轉(zhuǎn)義。不熟悉怎么從頭搭建react的我的示例代碼就放在這。

好了,我們從一個最簡單實(shí)例demo來看react到底做了什么

1、createElement

下面是我們的代碼

import React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(
    

11111

, document.getElementById("root") );

這是頁面上的效果

我們現(xiàn)在看看在瀏覽器中的代碼是如何實(shí)現(xiàn)的:

react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render(react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", {
  style: {
    color: "red"
  }
}, "11111"), document.getElementById("root"));

最終經(jīng)過編譯后的代碼是這樣的,發(fā)現(xiàn)原本的

11111

變成了一個react.createElement的函數(shù),其中原生標(biāo)簽的類型,內(nèi)容都變成了參數(shù)傳入這個函數(shù)中.這個時候我們大膽的猜測react.createElement接受三個參數(shù),分別是元素的類型、元素的屬性、子元素。好了帶著我們的猜想來看一下源碼。

我們不難找到,源碼位置在位置 ./node_modules/react/umd/react.development.js:1941

function createElement(type, config, children) {
  var propName = void 0;

  // 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;
    }
    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$1.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);
}

首先我們來看一下它的三個參數(shù)
第一個type:我們想一下這個type的可能取值有哪些?

第一種就是我們上面寫的原生的標(biāo)簽類型(例如h1、div,span等);

第二種就是我們React組件了,就是這面這種App

class App extends React.Component {

    static defaultProps = {
        text: "DEMO"
    }
    render() {
        return (

222{this.props.text}

) } }

第二個config:這個就是我們傳遞的一些屬性
第三個children:這個就是子元素,最開始我們猜想就三個參數(shù),其實(shí)后面看了源碼就知道這里其實(shí)不止三個。

接下來我們來看看react.createElement這個函數(shù)里面會幫我們做什么事情。
1、首先會初始化一些列的變量,之后會判斷我們傳入的元素中是否帶有有效的keyref的屬性,這兩個屬性對于react是有特殊意義的(key是可以優(yōu)化React的渲染速度的,ref是可以獲取到React渲染后的真實(shí)DOM節(jié)點(diǎn)的),如果檢測到有傳入key,ref,__self__source這4個屬性值,會將其保存起來。

2、接著對傳入的config做處理,遍歷config對象,并且剔除掉4個內(nèi)置的保留屬性(key,ref,__self,__source),之后重新組裝新的configprops。這個RESERVED_PROPS是定義保留屬性的地方。

    var RESERVED_PROPS = {
      key: true,
      ref: true,
      __self: true,
      __source: true
    };

3、之后會檢測傳入的參數(shù)的長度,如果childrenLength等于1的情況下,那么就代表著當(dāng)前createElement的元素只有一個子元素,那么將內(nèi)容賦值到props.children。那什么時候childrenLength會大于1呢?那就是當(dāng)你的元素里面涉及到多個子元素的時候,那么children將會有多個傳入到createElement函數(shù)中。例如:

    ReactDOM.render(
        

111
222

, document.getElementById("root") );

編譯后是什么樣呢?

   react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render(
    react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", {
      style: {
        color: "red"
      },
      key: "22"
    }, 
    react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, "111"), 
    react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", null, "222")), 
    document.getElementById("root")
  );

這個時候react.createElement拿到的arguments.length就大于3了。也就是childrenLength大于1。這個時候我們就遍歷把這些子元素添加到props.children中。
4、接著函數(shù)將會檢測是否存在defaultProps這個參數(shù),因?yàn)楝F(xiàn)在的是一個最簡單的demo,而且傳入的只是原生元素,所以沒有defaultProps這個參數(shù)。那么我們來看下面的例子:

    import React, { Component } from "react";
    import ReactDOM from "react-dom";
    class App extends Component {
        static defaultProps = {
            text: "33333"
        }
        render() {
            return (

222{this.props.text}

) } } ReactDOM.render( , document.getElementById("root") );

編譯后的

     var App =
    /*#__PURE__*/
    function (_Component) {
      _inherits(App, _Component);
    
      function App() {
        _classCallCheck(this, App);
    
        return _possibleConstructorReturn(this, _getPrototypeOf(App).apply(this, arguments));
      }
    
      _createClass(App, [{
        key: "render",
        value: function render() {
          return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", null, "222", this.props.text);
        }
      }]);
    
      return App;
    }(react__WEBPACK_IMPORTED_MODULE_0__["Component"]);
    
    _defineProperty(App, "defaultProps", {
      text: "33333"
    });
    
    react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render(
      react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(App, null), 
      document.getElementById("root")
      );

發(fā)現(xiàn)傳入react.createElement的是一個App的函數(shù),class經(jīng)過babel轉(zhuǎn)換后會變成一個構(gòu)造函數(shù)。有興趣可以自己去看babel對于class的轉(zhuǎn)換,這里就不解析轉(zhuǎn)換過程,總得來說就是返回一個App的構(gòu)造函數(shù)傳入到react.createElement中.如果type傳的東西是個對象,且typedefaultProps這個東西并且props中對應(yīng)的值是undefined,那就defaultProps的值也塞props里面。這就是我們組價默認(rèn)屬性的由來。

5、 檢測keyref是否有賦值,如果有將會執(zhí)行defineKeyPropWarningGetterdefineRefPropWarningGetter兩個函數(shù)。

function defineKeyPropWarningGetter(props, displayName) {
  var warnAboutAccessingKey = function () {
    if (!specialPropKeyWarningShown) {
      specialPropKeyWarningShown = true;
      warningWithoutStack$1(false, "%s: `key` is not a prop. Trying to access it will result " + "in `undefined` being returned. If you need to access the same " + "value within the child component, you should pass it as a different " + "prop. (https://fb.me/react-special-props)", displayName);
    }
  };
  warnAboutAccessingKey.isReactWarning = true;
  Object.defineProperty(props, "key", {
    get: warnAboutAccessingKey,
    configurable: true
  });
}

function defineRefPropWarningGetter(props, displayName) {
  var warnAboutAccessingRef = function () {
    if (!specialPropRefWarningShown) {
      specialPropRefWarningShown = true;
      warningWithoutStack$1(false, "%s: `ref` is not a prop. Trying to access it will result " + "in `undefined` being returned. If you need to access the same " + "value within the child component, you should pass it as a different " + "prop. (https://fb.me/react-special-props)", displayName);
    }
  };
  warnAboutAccessingRef.isReactWarning = true;
  Object.defineProperty(props, "ref", {
    get: warnAboutAccessingRef,
    configurable: true
  });
}

我么可以看出這個二個方法就是給keyref添加了警告。這個應(yīng)該只是在開發(fā)環(huán)境才有其中isReactWarning就是上面判斷keyref是否有效的一個標(biāo)記。
6、最后將一系列組裝好的數(shù)據(jù)傳入ReactElement函數(shù)中。

2、ReactElement
var ReactElement = function (type, key, ref, self, source, owner, props) {
  var element = {
    $$typeof: REACT_ELEMENT_TYPE,
    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner
  };

  {
    element._store = {};
    Object.defineProperty(element._store, "validated", {
      configurable: false,
      enumerable: false,
      writable: true,
      value: false
    });

    Object.defineProperty(element, "_self", {
      configurable: false,
      enumerable: false,
      writable: false,
      value: self
    });

    Object.defineProperty(element, "_source", {
      configurable: false,
      enumerable: false,
      writable: false,
      value: source
    });
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element);
    }
  }

  return element;
};

其實(shí)里面非常簡單,就是將傳進(jìn)來的值都包裝在一個element對象中

$$typeof:其中REACT_ELEMENT_TYPE是一個常量,用來標(biāo)識該對象是一個ReactElement

var hasSymbol = typeof Symbol === "function" && Symbol.for;
var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for("react.element") : 0xeac7;

從代碼上看如果支持Symbol就會用Symbol.for方法創(chuàng)建一個keyreact.elementsymbol,否則就會返回一個0xeac7

type -> tagName或者是一個函數(shù)

key -> 渲染元素的key

ref -> 渲染元素的ref

props -> 渲染元素的props

_owner -> Record the component responsible for creating this element.(記錄負(fù)責(zé)創(chuàng)建此元素的組件,默認(rèn)為null)

_store -> 新的對象

_store中添加了一個新的對象validated(可寫入),
element對象中添加了_self_source屬性(只讀),最后凍結(jié)了element.propselement。
這樣就解釋了為什么我們在子組件內(nèi)修改props是沒有效果的,只有在父級修改了props后子組件才會生效

最后就將組裝好的element對象返回了出來,提供給ReactDOM.render使用。到這有關(guān)的主要內(nèi)容我們看完了。下面我們來補(bǔ)充一下知識點(diǎn)

Object.freeze

Object.freeze方法可以凍結(jié)一個對象,凍結(jié)指的是不能向這個對象添加新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該對象已有屬性的可枚舉性、可配置性、可寫性。該方法返回被凍結(jié)的對象。

const obj = {
    a: 1,
    b: 2
};

Object.freeze(obj);

obj.a = 3; // 修改無效

需要注意的是凍結(jié)中能凍結(jié)當(dāng)前對象的屬性,如果obj中有一個另外的對象,那么該對象還是可以修改的。所以React才會需要凍結(jié)element和element.props。

if (Object.freeze) {
  Object.freeze(element.props);
  Object.freeze(element);
}
后續(xù)更多文章將在我的github第一時間發(fā)布,歡迎關(guān)注。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/103283.html

相關(guān)文章

  • React 源碼深度解讀(四):首次自定義組件渲染 - Part 1

    摘要:本篇開始介紹自定義組件是如何渲染的。組件將自定義組件命名為,結(jié)構(gòu)如下經(jīng)過編譯后,生成如下代碼構(gòu)建頂層包裝組件跟普通元素渲染一樣,第一步先會執(zhí)行創(chuàng)建為的。調(diào)用順序已在代碼中注釋。先看圖,這部分內(nèi)容將在下回分解 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非...

    Warren 評論0 收藏0
  • React 源碼深度解讀(一):首次DOM元素渲染 - Part 1

    摘要:調(diào)用棧是這樣的這里生成的我們將其命名為,它將作為參數(shù)傳入到。整個的調(diào)用棧是這樣的組件間的層級結(jié)構(gòu)是這樣的到此為止,頂層對象已經(jīng)構(gòu)造完畢,下一步就是調(diào)用來自的方法,進(jìn)行頁面的渲染了。通過表達(dá)的結(jié)構(gòu)最終會轉(zhuǎn)化為一個純對象,用于下一步的渲染。 歡迎關(guān)注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言...

    daydream 評論0 收藏0
  • 零基礎(chǔ)如何學(xué)爬蟲技術(shù)

    摘要:楚江數(shù)據(jù)是專業(yè)的互聯(lián)網(wǎng)數(shù)據(jù)技術(shù)服務(wù),現(xiàn)整理出零基礎(chǔ)如何學(xué)爬蟲技術(shù)以供學(xué)習(xí),。本文來源知乎作者路人甲鏈接楚江數(shù)據(jù)提供網(wǎng)站數(shù)據(jù)采集和爬蟲軟件定制開發(fā)服務(wù),服務(wù)范圍涵蓋社交網(wǎng)絡(luò)電子商務(wù)分類信息學(xué)術(shù)研究等。 楚江數(shù)據(jù)是專業(yè)的互聯(lián)網(wǎng)數(shù)據(jù)技術(shù)服務(wù),現(xiàn)整理出零基礎(chǔ)如何學(xué)爬蟲技術(shù)以供學(xué)習(xí),http://www.chujiangdata.com。 第一:Python爬蟲學(xué)習(xí)系列教程(來源于某博主:htt...

    KunMinX 評論0 收藏0
  • 剖析 React 源碼:先熱個身

    摘要:我們先來看下這個函數(shù)的一些神奇用法對于上述代碼,也就是函數(shù)來說返回值是。不管你第二個參數(shù)的函數(shù)返回值是幾維嵌套數(shù)組,函數(shù)都能幫你攤平到一維數(shù)組,并且每次遍歷后返回的數(shù)組中的元素個數(shù)代表了同一個節(jié)點(diǎn)需要復(fù)制幾次。這是我的 React 源碼解讀課的第一篇文章,首先來說說為啥要寫這個系列文章: 現(xiàn)在工作中基本都用 React 了,由此想了解下內(nèi)部原理 市面上 Vue 的源碼解讀數(shù)不勝數(shù),但是反觀...

    sean 評論0 收藏0
  • React系列 --- createElement, ReactElement與Component部

    摘要:僅針對數(shù)據(jù)屬性描述有效獲取該屬性的訪問器函數(shù)。當(dāng)且僅當(dāng)指定對象的屬性可以被枚舉出時,為。凍結(jié)及其對象主要目的是為提高測試環(huán)境下效率,將的一些屬性配置為不可枚舉,進(jìn)行遍歷的時候跳過這些屬性。 React系列 React系列 --- 簡單模擬語法(一)React系列 --- Jsx, 合成事件與Refs(二)React系列 --- virtualdom diff算法實(shí)現(xiàn)分析(三)React...

    xuhong 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<