React源碼學習系列(一)—— ReactElement與ReactComponent

概述

本系列文章將通過React赞弥、ReactDOM的幾個關鍵方法,如 ReactDOM.render嗦枢、 this.setState開始,對React源碼進行解讀附鸽。

與一般源碼解析的文章不同脱拼,本系列文章不會在文中一步步通讀代碼,而是通過對關鍵方法的探究坷备,一步步了解React內(nèi)部的原理熄浓。

閱讀前,您應具備React省撑、JSX赌蔑、ES6的實踐經(jīng)驗,同時應了解babel竟秫、chrome斷點調(diào)試等娃惯。

本文所閱讀的React源碼版本為 v15.6.2,可點擊此下載肥败。

從React.createElement及JSX講起

眾所周知趾浅,JSX只是為React.createElement(component, props, ...children)方法提供的語法糖,如以下代碼(參考文檔):

// JSX語法
let app = (<div id='app'>Hello World!</div>);

在babel編譯后:

// babel編譯后:
let app = React.createElement('div', {id: 'app'}, 'Hello World!');

appconsole.log出來后馒稍,得到以下的結果:

element.png

接下來皿哨,我們到源碼中ReactElement.js查看 React.createElement 方法,如下:

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

  // Reserved names are extracted
  var props = {};

  var key = null;
  var ref = null;
  var self = null;
  var source = null;

  if (config != null) {
    // 對config參數(shù)的處理纽谒,您可直接查看源碼证膨,此處隱藏
  }

  // 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) {
    // 處理 children多于1個時...
  }

  // 寫入元素的defaultProps
  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);
};

可以看到其調(diào)用了名為 ReactElement 的工廠方法, 該工廠方法主要代碼如下:

var ReactElement = function (type, key, ref, self, source, owner, props) {
  var element = {
    // 增加一個屬性鼓黔,用來標識這個對象是一個 ReactElement '對象'
    $$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
  };

  if (process.env.NODE_ENV !== 'production') {
      // 開發(fā)環(huán)境中的提示... 
  }

  return element;
};

至此椎例,我們在JSX中所寫的元素,都是使用React.createElement創(chuàng)建ReactElement實例请祖。

所謂“組件” Component

在Reactv16之前,有三種自定義組件的方式:

// v16.0.0以后已經(jīng)廢棄此方法
var Hello = React.createClass({
    render(){
        return <p>Hello world, {this.props.guest}!</p>
    }
})
// 無狀態(tài)組件推薦
var Bye = function(props){
    return (
        <p>Bye Bye, {props.guest}!</p>
    )
}
// ES6類繼承
class SayHi extends React.Component {
    render(){
        return <p>Hi, {this.props.guest}!</p>
    }
}

我們從ES6類繼承的方式來著手脖祈,查看 React.js 文件肆捕,可以找到 React.Component 實際為 ReactBaseClasses.js 中定義的 ReactComponent構造函數(shù)(類)

function ReactComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

ReactComponent.prototype.isReactComponent = {};

ReactComponent.prototype.setState = function (partialState, callback) {
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};

ReactComponent.prototype.forceUpdate = function (callback) {
  this.updater.enqueueForceUpdate(this);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'forceUpdate');
  }
};

此處可以看到我們熟悉的props、context都是在實例化這個構造函數(shù)時傳入的盖高。
setStateforceUpdate都是原型上的方法慎陵,參數(shù)中傳入的 updater 將在我們分析 ReactDOM.render 這個方法的時候詳細分析。

我們知道喻奥,其實class本質(zhì)上就是原型繼承的語法糖席纽,使用class定義的 組件 類,實際也是一個 'function'撞蚕, 與無狀態(tài)組件定義的時候是一樣的润梯。
那么,組件與上一節(jié)所講的ReactElement有什么聯(lián)系呢?

我們通過以下代碼來分析:

class Hello extends React.Component {
  render(){
    return (
      <div>
        <h2>{this.props.title}</h2>
      </div>
    )
  }
}

console.log(<Hello />) // <Hello /> 相當于 React.createElement(Hello)

查看以下結果:


component.png

對比上一次的結果纺铭,我們發(fā)現(xiàn)只有type這里是存在差異的寇钉,而這種差異帶來的,就是React在渲染元素舶赔、組件時候的差異了扫倡,這個將放在下一篇,也許是下下篇講竟纳。

關于組件容易混淆的概念

// No.1
class Hello extends React.Component {
  render(){
    return (
      <h2>Hello world!</h2>
    )
  }
}

// No.2
var HelloCom = <Hello />

// No.3
var HelloEle = (
  <div>
    <h2>Hello world!</h2>
  </div>
)

以上代碼中的 Hello撵溃、HelloCom、HelloEle哪些是“組件” 锥累?

答案: 只有 Hello 一個缘挑。

首先看 組件的定義: 參考文檔

在這里,HelloCom 揩悄、 HelloEle實際上已經(jīng)是ReactElement的一個“實例”(說實例不太恰當卖哎,但足夠形象)了,而非如組件定義的:是一個可復用的部件删性,接收props亏娜,并返回元素。

總結

本篇了解了React中ReactElementReactComponent部分的源碼蹬挺,并從中了解到我們在JSX中所寫的dom在Javascript中會被轉(zhuǎn)換成一個對象维贺。
讀到這里,您可能會想巴帮,那這些對象是怎么被轉(zhuǎn)換并插入到我們的dom結構中呢溯泣?敬請期待下一篇關于ReactDOM.render的分析。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末榕茧,一起剝皮案震驚了整個濱河市垃沦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌用押,老刑警劉巖肢簿,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜻拨,居然都是意外死亡池充,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門缎讼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來收夸,“玉大人,你說我怎么就攤上這事血崭∥韵В” “怎么了厘灼?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長序苏。 經(jīng)常有香客問我手幢,道長,這世上最難降的妖魔是什么忱详? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任围来,我火速辦了婚禮,結果婚禮上匈睁,老公的妹妹穿的比我還像新娘监透。我一直安慰自己,他們只是感情好航唆,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布胀蛮。 她就那樣靜靜地躺著,像睡著了一般糯钙。 火紅的嫁衣襯著肌膚如雪粪狼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天任岸,我揣著相機與錄音再榄,去河邊找鬼。 笑死享潜,一個胖子當著我的面吹牛困鸥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播剑按,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼疾就,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了艺蝴?” 一聲冷哼從身側響起猬腰,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猜敢,沒想到半個月后漆诽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡锣枝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了兰英。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撇叁。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖畦贸,靈堂內(nèi)的尸體忽然破棺而出陨闹,到底是詐尸還是另有隱情楞捂,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布趋厉,位于F島的核電站寨闹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏君账。R本人自食惡果不足惜繁堡,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望乡数。 院中可真熱鬧椭蹄,春花似錦、人聲如沸净赴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玖翅。三九已至翼馆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間金度,已是汗流浹背应媚。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留审姓,地道東北人珍特。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像魔吐,于是被迫代替她去往敵國和親扎筒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

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

  • 原教程內(nèi)容詳見精益 React 學習指南酬姆,這只是我在學習過程中的一些閱讀筆記嗜桌,個人覺得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,813評論 1 18
  • 以下內(nèi)容是我在學習和研究React時辞色,對React的特性骨宠、重點和注意事項的提取、精練和總結相满,可以做為React特性...
    科研者閱讀 8,222評論 2 21
  • 3. JSX JSX是對JavaScript語言的一個擴展語法层亿, 用于生產(chǎn)React“元素”,建議在描述UI的時候...
    pixels閱讀 2,809評論 0 24
  • HTML模版 之后出現(xiàn)的React代碼嵌套入模版中立美。 1. Hello world 這段代碼將一個一級標題插入到指...
    ryanho84閱讀 6,221評論 0 9
  • 不知道從什么時候開始喜歡上了坐火車匿又,特別是長途火車〗ㄌ悖或許是因為火車比較慢碌更,能讓走有足夠的時間撐著下巴對著車窗外不斷...
    葉小諾閱讀 521評論 9 0