自建仿React引擎(三)

  在上一篇《自建仿React引擎(二)》中,已經(jīng)針對React.js里面其中一個重要的對象props的作用以及實(shí)現(xiàn)進(jìn)行了講解,接下來,我準(zhǔn)備講解refs的作用及實(shí)現(xiàn)機(jī)制,在處理這個refs的時候,遇到了不少的麻煩,花了一些時間思考解決方案。

  首先,我們來看看refs在React.js中的作用,具體看下圖。

image

我們在組件中定義ref,代碼示例如下:

image

編譯后的代碼如下:

image

  注意,在父組件使用到的ref屬性,作為子組件的入?yún)⑦M(jìn)行傳遞。此時,如果按照傳統(tǒng)的做法,先創(chuàng)建父組件,再創(chuàng)建子組件,ref又是子組件的入?yún)ⅲ敲匆獙?shí)現(xiàn)refs的機(jī)制也就很簡單了,直接在父組件的refs對象中,通過指定this.refs[ref] = 子組件進(jìn)行賦值即可,然而,事實(shí)上React的組件創(chuàng)建流程是基于深度優(yōu)先的創(chuàng)建機(jī)制,簡要流程示意圖如下。

image

  React.js組件在渲染的過程中,會創(chuàng)建兩類不同的對象,一個是ReactClass,一個是ReactElement。其中,ReactClass是自頂層至底層創(chuàng)建的順序,而ReactElement則是自底層往頂層創(chuàng)建的順序。

  組件渲染時,先通過React.createClass創(chuàng)建出class對象,再通過class對象的render方法創(chuàng)建ReactElement對象,然后通過ReactDOM將ReactElement對象進(jìn)行渲染。因此,實(shí)現(xiàn)refs的關(guān)鍵點(diǎn)就在于在創(chuàng)建子ReactElement對象的時候,要準(zhǔn)確的找到最外層父組件ReactClass對象。

  也許有人會說,那簡單啊,在創(chuàng)建子組件的時候,碰到ref的,都通過一個統(tǒng)一的全局變量緩存起來,不就可以解決了嗎?首先,從編譯后的代碼中,我們可以看得出,子組件是沒有任何標(biāo)識被哪個父組件包含,其次,父組件也沒有標(biāo)識這是最外層父組件,再者,子組件也允許有自己內(nèi)部的ref,這樣子通過全局變量緩存起來的解決方案就會相當(dāng)復(fù)雜。(React的實(shí)現(xiàn)是基于對象繼承,以代理的模式實(shí)現(xiàn),有興趣可以去看一下源碼)。

  經(jīng)過再三的思考,發(fā)現(xiàn)此處適合用棧模型來解決會比較省事,于是編寫了一個簡單的棧模型,具體代碼如下。

image

  有了這個棧模型之后,我們就可以比較輕松的實(shí)現(xiàn)獲取當(dāng)前父組件class對象了。根據(jù)上面React組件渲染的流程,我們只要在React.createClass的時候,將class對象入棧,在當(dāng)前class創(chuàng)建完ReactElement對象后,將class對象出棧。這樣子就能保證子ReactElement在創(chuàng)建的時候,可以輕松獲取當(dāng)前運(yùn)行環(huán)節(jié)最頂層的組件class對象,將自己與頂層組件class對象中的refs關(guān)聯(lián)起來。

關(guān)鍵代碼示例如下:

1.React.createClass

class.refs = {};//創(chuàng)建refs對象

ClassStacks.push(class);//入棧

2.React.createElement

if(class){//組件

var _node = class.render();

ClassStacks.pop(); //render完成,出棧。

if(props != undefined && props["ref"] != undefined) {

ClassStacks.get().refs[props["ref"]] = class;//此時棧的頂層元素是父組件,綁定的ref對象是組件

}

}else{//DOM元素

if(props != undefined && props["ref"] != undefined) {

ClassStacks.get().refs[props["ref"]] = _dom;//此時棧的頂層元素是父組件,綁定的組件是DOM元素

}

}

  經(jīng)過這樣子的改造,我們就完成了組件refs的開發(fā)工作。下期,我們會針對state狀態(tài)管理機(jī)制的實(shí)現(xiàn)進(jìn)行講解,謝謝大家。


引擎全部代碼如下:

/**

  • class運(yùn)時期的棧模型

*/

var ClassStacks = (function() {

var vClasses = new Array();

return {

get: function() {

return vClasses[vClasses.length - 1] || {

props: {},

refs: {}

};

},

push: function(cls) {

vClasses.push(cls);

},

pop: function() {

return vClasses.pop();

}

}

})();

var React = {

createClass: function(cfg) { //直接量對象cfg

var _f = function(props) { //將當(dāng)前結(jié)點(diǎn)的屬性存放到直接量對象cfg中的props屬性

if(cfg.getDefaultProps != undefined &&

typeof(cfg.getDefaultProps) === 'function') {

cfg.props = cfg.getDefaultProps();

}

//傳入?yún)?shù)

for(var key in props) {

cfg.props[key] = props[key];

}

cfg.refs = {};//默認(rèn)添加refs對象

cfg.props.children = []; //定義children對象

ClassStacks.push(cfg);//入棧

return cfg;

}

return _f;

},

createElement: function() {

var tagName = arguments[0]; //第1個入?yún)⑹墙Y(jié)點(diǎn)的名稱

var props = arguments[1] || {}; //第2個入?yún)⑹钱?dāng)前節(jié)點(diǎn)的屬性

if(typeof(tagName) == 'string') {

var _node = document.createElement(tagName);

if(props) {

for(var key in props) {

if(key == 'ref') {

ClassStacks.get().refs[value] = _node;//綁定DOM元素

} else{

_node.setAttribute(key, props[key]);

}

}

}

//第3個入?yún)ⅲê竺娴膮?shù)都為當(dāng)前節(jié)點(diǎn)的孩子節(jié)點(diǎn)

for(var index = 2; index < arguments.length; index++) {

_node.appendChild(arguments[index]);

}

return _node;

} else {

//組織處理chidren數(shù)組

var children = [];

for(var index = 2; index < arguments.length; index++) {

var param = arguments[index];

if(param && typeof param === 'object' &&

Array == param.constructor) { //處理children數(shù)組傳遞數(shù)組的情況

children = children.concat(param);

} else {

children.push(param);

}

}

var vDom = tagName(props); //創(chuàng)建Class。

vDom.props.children = children; //存放children對象

var _node = vDom.render(); //執(zhí)行createClass返回的_f函數(shù),執(zhí)行cfg直接量對象里面的render方法

ClassStacks.pop(); //render完成,出棧。

if(props != undefined && props["ref"] != undefined) {

ClassStacks.get().refs[props["ref"]] = vDom;//此時棧的頂層元素是父組件

}

return _node;

}

}

}

var ReactDOM = {

render: function(ele, container) {

container.appendChild(ele);

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,516評論 2 379

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

  • It's a common pattern in React to wrap a component in an ...
    jplyue閱讀 3,291評論 0 2
  • 在《自建仿React引擎(一)》中,已實(shí)現(xiàn)了基礎(chǔ)的解析引擎,可以正確加載React編寫的代碼,但是為了兼容Reac...
    飆豬狂閱讀 359評論 0 0
  • 原教程內(nèi)容詳見精益 React 學(xué)習(xí)指南,這只是我在學(xué)習(xí)過程中的一些閱讀筆記,個人覺得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,852評論 1 18
  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,093評論 2 35
  • 這是一個深夜,點(diǎn)點(diǎn)星光點(diǎn)綴在漆黑的夜空,夜幕籠罩著大地,偶爾來往的汽車的響動顯的那么突兀,但這片夜幕中更多的是寂靜...
    商昕璐閱讀 285評論 0 0