React(虛擬)DOM術(shù)語[一源看世界][之React]

閱讀源碼前无蜂,提前了解下作者對一些術(shù)語的定義,有助于更好地理解源碼温学。以下為根據(jù)官方文檔關(guān)于React (Virtual) DOM Terminology描述進(jìn)行翻譯并配上源碼粟害,以更深入地了解這些概念

在React的名詞術(shù)語中,有五種主要的類型需要加以區(qū)別
. ReactElement / ReactElement Factory
. ReactNode
. ReactComponent / ReactComponent Class


React Elements

React中主要的類型是ReactElement茧跋,它有四種屬性: type慰丛,propskeyref瘾杭。從以下源碼可以看出它就是一個普通的對象诅病,通過$$typeof屬性來識別是否是React Element,值為Symbol.for('react.element’)或60103

var ReactElement = function(type, key, ref, self, source, owner, props) {
  var element = {
    // This tag allow us to uniquely identify this as a React Element
    $$typeof: REACT_ELEMENT_TYPE,

    // 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,
  };
  ...
  return element;
};

你可以通過React.createElement來創(chuàng)建這些對象粥烁。

var root = React.createElement('div');

我們來看下React.createElement的源碼贤笆,看看它做了啥?

ReactElement.createElement = function(type, config, children) {
  ...

  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.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];
    }
    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];
      }
    }
  }
  ...
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props
  );
};

從源碼中我們可以看到:

  1. config中可以包含保留屬性ref页徐,key苏潜,__self__source
  2. React Element的props屬性包含了除保留屬性外的所有config的屬性值变勇,children和type(通過React.createClass創(chuàng)建的類或稱構(gòu)造函數(shù))的defaultProps的所有屬性值
  3. ReactElement.createElement可以傳不止3個參數(shù),第3個以及后面的參數(shù)都作為child傳進(jìn)來,如:
var root = React.createElement('ul', { className: 'my-list' }, child1, child2);

你可以創(chuàng)建多個ReactElement搀绣,它們之間的父子包含關(guān)系形成了一個樹結(jié)構(gòu)飞袋,將最top的ReactElement和它將所在的容器(一個普通的DOM element,可以是HTMLElement 或者 SVGElement)傳給ReactDOM.render方法链患,這樣就可以在頁面上渲染出這個組件及其所包含的子組件

ReactElement不是DOM element巧鸭,它是DOM element的一個輕量的,無狀態(tài)的麻捻,不可變的虛擬表示纲仍,就是傳說中的虛擬DOM

ReactDOM.render(root, document.getElementById('example'));

可以通過傳遞一個屬性對象給第2個參數(shù)為組件的DOM element增加屬性,可以通過傳遞子React element對象給第3個參數(shù)為組件DOM element增加子的DOM element

var child = React.createElement('li', null, 'Text Content');
var root = React.createElement('ul', { className: 'my-list' }, child);
ReactDOM.render(root, document.getElementById('example'));

如果你用JSX贸毕,那么以下代碼跟上面的代碼是等價郑叠,JSX編譯器會將以下JSX語法解析成上面的JS代碼

var root = <ul className="my-list">
             <li>Text Content</li>
           </ul>;
ReactDOM.render(root, document.getElementById('example'));

看累了嗎?小休息一下再接著看唄C鞴鳌O绺铩!


Factories

ReactElement-factory是一個可以生成特定type的ReactElement對象的工廠函數(shù)摊腋。React內(nèi)置了有用的創(chuàng)建工廠函數(shù)的方法

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

這里小普及一下Function.prototype.bind函數(shù)沸版,它是用來創(chuàng)建一個新的函數(shù),新的函數(shù)的this關(guān)鍵字指向第1個參數(shù)(如果為null則保持不變)兴蒸,第2, ...個參數(shù)的值作為參數(shù)值按順序傳給原來的函數(shù)视粮。舉個栗子吧

var foo = function(p1, p2) { console.log(this, p1, p2); }

var barContext = {};
var bar = foo.bind(barContext, 'hello'); // bar函數(shù)的this關(guān)鍵字指向了barContext,傳給foo函數(shù)的p1參數(shù)的值為'hello'

bar('daniel') // 相當(dāng)于 foo('hello', 'daniel')橙凳,但兩個函數(shù)this關(guān)鍵字指向不同

有了Factory工廠函數(shù)你就不用每次都敲打React.createElement('div')了馒铃。- Well done,程序員就應(yīng)該能懶則懶

var div = React.createFactory('div');
var root = div({ className: 'my-div' });
ReactDOM.render(root, document.getElementById('example'));

React本身也內(nèi)置了很多針對一般的HTML標(biāo)簽的Factory方法

var root = React.DOM.ul({ className: 'my-list' },
             React.DOM.li(null, 'Text Content')
           );

React Nodes

一個React Node可以是以下:
. React Element
. string (亦稱ReactText)
. number (亦稱ReactText)
. 一組React Node (亦稱ReactFragment)
它們已作為其它React Element的屬性的形式(props.children)來表示子元素痕惋,從而創(chuàng)建了一個樹結(jié)構(gòu)的組件


React Components

一個ReactComponent Class就是一個javascript類(或稱為構(gòu)造函數(shù))区宇,它同時也稱為某個ReactElement的類型(type)

var MyComponent = React.createClass({
  render: function() {
    ...
  }
});

當(dāng)這個構(gòu)造函數(shù)被調(diào)用時,返回的是一個至少有render方法的對象值戳,這個對象被稱為ReactComponent(即ReactComponent Class的實(shí)例)

var component = new MyComponent(props); // 千萬不要這樣去創(chuàng)建ReactComponent

除了測試议谷,不要自己去調(diào)用構(gòu)造函數(shù),交給React來處理這事就好堕虹。
將ReactComponent Class傳給createElement方法卧晓,返回一個ReactElement

var element = React.createElement(MyComponent);

或者使用JSX:

var element = <MyComponent />;

當(dāng)這個element傳給ReactDom.render方法時,React會幫你調(diào)用構(gòu)造函數(shù)并返回ReactComponent

var component = ReactDOM.render(element, document.getElementById('example'));

如果你傳入相同類型的ReactElement和相同的DOM容器繼續(xù)調(diào)用ReactDOM.render赴捞,它會一直返回同一個實(shí)例逼裆。這個實(shí)例是有狀態(tài)的

var componentA = ReactDOM.render(<MyComponent />, document.getElementById('example'));
var componentB = ReactDOM.render(<MyComponent />, document.getElementById('example'));
componentA === componentB; // true

從源碼中src/renderers/dom/client/ReactMount.js來驗(yàn)證這個邏輯。

 _renderSubtreeIntoContainer: function(parentComponent, nextElement, container, callback) {
    ...

    var prevComponent = getTopLevelWrapperInContainer(container);

    if (prevComponent) {
      var prevWrappedElement = prevComponent._currentElement;
      var prevElement = prevWrappedElement.props;
      if (shouldUpdateReactComponent(prevElement, nextElement)) {
        var publicInst = prevComponent._renderedComponent.getPublicInstance();
        var updatedCallback = callback && function() {
          callback.call(publicInst);
        };
        ReactMount._updateRootComponent(
          prevComponent,
          nextWrappedElement,
          container,
          updatedCallback
        );
        return publicInst;
      } else {
        ...
      }
    }
    ...
  },

可以看出是通過shouldUpdateReactComponent方法來判斷是否只更新并返回原來的實(shí)例publicInst赦政,而shouldUpdateReactComponent針對ReactElement會判斷typekey屬性是否相等

這就是為什么不讓你自己創(chuàng)建ReactComponent的原因胜宇,因?yàn)镽eact作了優(yōu)化的處理耀怜,可不是隨隨便便就New New New哦。

ReactComponentrender方法(就是你在聲明一個組件時定義的render方法)會返回另外一個ReactElement桐愉,所以這些Component可以進(jìn)行組合财破。最終會解析成DOM element實(shí)例并插入到document中


正式的類型定義

入口

ReactDOM.render = (ReactElement, HTMLElement | SVGElement) => ReactComponent;

Nodes 和 Elements

type ReactNode = ReactElement | ReactFragment | ReactText;

type ReactElement = ReactComponentElement | ReactDOMElement;

type ReactDOMElement = {
  type : string,
  props : {
    children : ReactNodeList,
    className : string,
    etc.
  },
  key : string | boolean | number | null,
  ref : string | null
};

type ReactComponentElement<TProps> = {
  type : ReactClass<TProps> | ReactFunctionalComponent<TProps>,
  props : TProps,
  key : string | boolean | number | null,
  ref : string | null
};

type ReactFragment = Array<ReactNode | ReactEmpty>;

type ReactNodeList = ReactNode | ReactEmpty;

type ReactText = string | number;

type ReactEmpty = null | undefined | boolean;

Classes and Components

type ReactClass<TProps> = (TProps) => ReactComponent<TProps>;

type ReactComponent<TProps> = {
  props : TProps,
  render : () => ReactElement
};

type ReactFunctionalComponent<TProps> = (TProps) => ReactElement;

最后,期待吐槽从诲,期待指教W罅 !系洛!

--EOF--

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俊性,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子描扯,更是在濱河造成了極大的恐慌定页,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荆烈,死亡現(xiàn)場離奇詭異拯勉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)憔购,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門宫峦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人玫鸟,你說我怎么就攤上這事导绷。” “怎么了屎飘?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵妥曲,是天一觀的道長。 經(jīng)常有香客問我钦购,道長檐盟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任押桃,我火速辦了婚禮葵萎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唱凯。我一直安慰自己羡忘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布磕昼。 她就那樣靜靜地躺著卷雕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪票从。 梳的紋絲不亂的頭發(fā)上漫雕,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天滨嘱,我揣著相機(jī)與錄音,去河邊找鬼蝎亚。 笑死九孩,一個胖子當(dāng)著我的面吹牛先馆,可吹牛的內(nèi)容都是我干的发框。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼煤墙,長吁一口氣:“原來是場噩夢啊……” “哼梅惯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仿野,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤铣减,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后脚作,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體葫哗,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年球涛,在試婚紗的時候發(fā)現(xiàn)自己被綠了劣针。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡亿扁,死狀恐怖捺典,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情从祝,我是刑警寧澤襟己,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站牍陌,受9級特大地震影響擎浴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毒涧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一贮预、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧链嘀,春花似錦萌狂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至霹琼,卻和暖如春务傲,著一層夾襖步出監(jiān)牢的瞬間凉当,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工售葡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留看杭,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓挟伙,卻偏偏與公主長得像楼雹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尖阔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 原教程內(nèi)容詳見精益 React 學(xué)習(xí)指南贮缅,這只是我在學(xué)習(xí)過程中的一些閱讀筆記,個人覺得該教程講解深入淺出介却,比目前大...
    leonaxiong閱讀 2,834評論 1 18
  • 以下內(nèi)容是我在學(xué)習(xí)和研究React時谴供,對React的特性、重點(diǎn)和注意事項(xiàng)的提取齿坷、精練和總結(jié)桂肌,可以做為React特性...
    科研者閱讀 8,232評論 2 21
  • 本筆記基于React官方文檔,當(dāng)前React版本號為15.4.0永淌。 1. 安裝 1.1 嘗試 開始之前可以先去co...
    Awey閱讀 7,705評論 14 128
  • GUIDS 第一章 為什么使用React崎场? React 一個提供了用戶接口的JavaScript庫。 誕生于Fac...
    jplyue閱讀 3,534評論 1 11
  • 無端的指責(zé) 莫名的猜忌 無理的懷疑 陷入了僵局 我不再解釋 你沒有追問 好似陌生人 緣分就此斷裂 彼此沒了留戀 從...
    花少顏閱讀 145評論 0 4