摘要:首先是創(chuàng)建了一個(gè)構(gòu)造函數(shù),他的原型指到的原型然后創(chuàng)建了一個(gè)加上了和一樣的屬性這里為啥不用。的原型指向的實(shí)例修改原型的屬性使其正確指向的構(gòu)造函數(shù),并掛一個(gè)的屬性。
每次都信誓旦旦的給自己立下要好好學(xué)習(xí)react源碼的flag,結(jié)果都是因?yàn)槟硞€(gè)地方卡住了,或是其他原因沒看多少就放棄了。這次又給自己立個(gè)flag-堅(jiān)持看完react源碼。為了敦促自己,特開設(shè)這樣一個(gè)專欄來記錄自己的學(xué)習(xí)歷程,這意味著這個(gè)專欄的文章質(zhì)量并不高,你可以拿來參考參考,切莫全信,我不想誤人子弟,后面要是學(xué)有所成再考慮產(chǎn)出些好點(diǎn)的文章。 要是發(fā)現(xiàn)文章中有什么不當(dāng)之處,歡迎批評交流。我看的源碼版本是16.8.2。我是用在源碼加注釋的方法學(xué)習(xí)的,放在github上。
為了看react源碼,我查找了不少資料,這里推薦兩個(gè)參考資料,個(gè)人覺得寫得不錯(cuò)。
慕課網(wǎng)一個(gè)課的電子書,他有個(gè)源碼解析的視頻教程,應(yīng)該不錯(cuò),不過我沒買。
一個(gè)知乎專欄,寫得很清晰,只不過是15.6.2的, 在react16里面一些方法找不到了。
Component, PureComponent是我們最常用的東西,我們經(jīng)常繼承他們來創(chuàng)建組件。因此,我選擇從這幾個(gè)最最常用的東西入手開始欣賞React源碼。他們都位于packages/react目錄下,入口在index.js,index.js里邊導(dǎo)出的其實(shí)是src下的React.js里的東西,在React.js中可以看到React暴露的API。在React.js中可以找到上面說述的Component,PureComponent和ReactElement相關(guān)線索。
ComponentComponent和PureComponent都位于/packages/react/src/ReactBaseClasses.js。
這兩個(gè)東西都是構(gòu)造函數(shù),或者稱為類。
Component的構(gòu)造函數(shù)長成如下這樣:
/** * Base class helpers for the updating state of a component. */ // 經(jīng)常去繼承他,原來這個(gè)構(gòu)造行數(shù)是這樣的 function Component(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // renderer. // 這個(gè)new的時(shí)候需要注意updater是哪里來的, 這個(gè)updater與setState應(yīng)該有很大關(guān)系 this.updater = updater || ReactNoopUpdateQueue; }
這并沒有什么神奇的,他接收三個(gè)參數(shù),掛到this上。具體是這三個(gè)參數(shù)是啥,我目前也是不清楚的,因?yàn)槲覀兤綍r(shí)使用都是extends他而并沒有new他,new的過程應(yīng)該是框架去做的,這個(gè)得到后面再做分析。后面分析時(shí)需要注意updater,感覺這里會(huì)是一個(gè)重點(diǎn),他有一個(gè)默認(rèn)值,ReactNoopUpdateQueue,去看了下他的代碼,他是一個(gè)對象,掛了一些方法,這里也就不展開了,我也沒太細(xì)看。
Component的原型上掛了一些方法和屬性,isReactComponent屬性,setState方法,forceUpdate方法,代碼如下:
// 通常isXxx都是boolean類型的,這里比較奇怪,后面需要關(guān)注下 Component.prototype.isReactComponent = {}; /** * ...這里有很多說明,可以直接去看 * * @param {object|function} partialState Next partial state or function to * produce next partial state to be merged with current state. * @param {?function} callback Called after state is updated. * @final * @protected */ // 原來我們平時(shí)調(diào)用的setState就這么幾行啊,但是看他是調(diào)用的updater的enqueueSetState, // 相關(guān)實(shí)現(xiàn)應(yīng)該在那里邊了, 可以updater這個(gè)東西很厲害 Component.prototype.setState = function(partialState, callback) { // 這里是個(gè)參數(shù)校驗(yàn),校驗(yàn)不通過的話會(huì)給提示信息,并拋出異常 invariant( typeof partialState === "object" || typeof partialState === "function" || partialState == null, "setState(...): takes an object of state variables to update or a " + "function which returns an object of state variables.", ); this.updater.enqueueSetState(this, partialState, callback, "setState"); }; /** * ...這里有很多說明,可以直接去看 * * @param {?function} callback Called after update is complete. * @final * @protected */ // 很少用到這個(gè)方法啊, 但他和setState一樣都是Component原型上的方法 Component.prototype.forceUpdate = function(callback) { this.updater.enqueueForceUpdate(this, callback, "forceUpdate"); };
其實(shí)Component的原型上掛載的東西也沒什么神奇的,其中非常重要的是updater的enqueueSetState,enqueueForceUpdate方法,進(jìn)一步說明了updater是后面分析的重點(diǎn)。
接下來的一段代碼是用來在開發(fā)模式下標(biāo)記廢棄的api的,在開發(fā)模式下回給寫提示,代碼如下:
// 這里是標(biāo)識(shí)一些廢棄的api, 開發(fā)模式會(huì)報(bào)出來提醒開發(fā)這注意 if (__DEV__) { const deprecatedAPIs = { isMounted: [ "isMounted", "Instead, make sure to clean up subscriptions and pending requests in " + "componentWillUnmount to prevent memory leaks.", ], replaceState: [ "replaceState", "Refactor your code to use setState instead (see " + "https://github.com/facebook/react/issues/3236).", ], }; const defineDeprecationWarning = function(methodName, info) { Object.defineProperty(Component.prototype, methodName, { get: function() { lowPriorityWarning( false, "%s(...) is deprecated in plain JavaScript React classes. %s", info[0], info[1], ); return undefined; }, }); }; for (const fnName in deprecatedAPIs) { if (deprecatedAPIs.hasOwnProperty(fnName)) { defineDeprecationWarning(fnName, deprecatedAPIs[fnName]); } } }
__DEV__這個(gè)東西我沒找到是在哪里掛到全局的(知道的同學(xué)可以留言指點(diǎn)下),但是看變量名可以推測他是開發(fā)模式標(biāo)識(shí),這個(gè)提示我們在做一些給別人用的東西時(shí),接口協(xié)議約定十分重要,一旦約定就不能輕易變更,確實(shí)需要變更時(shí)需要通知調(diào)用方調(diào)整。回頭來,這里標(biāo)識(shí)廢棄了isMounted,replaceState兩個(gè)方法,其實(shí)他們被挪到了updater里邊。
PureComponent開始用React時(shí)老大Rewview我的代碼時(shí)經(jīng)常寫評論,“你這個(gè)Component可以改成PureComponent”,當(dāng)時(shí)一直不懂PureComponent與Component的區(qū)別(現(xiàn)在也沒全懂),只是聽人說PureComponent更新的時(shí)候是淺比較,而Component是深比較。今天看了這部分,其實(shí)也沒懂,不過感覺后面再看看應(yīng)該就懂了。要搞清這里的PureComponet需要了解下js中繼承的實(shí)現(xiàn),大家可以參考《JavaScript高級程序設(shè)計(jì)》相關(guān)介紹,也可以看看理解js繼承的6種方式, 筆者看到這個(gè)PureComponet也是先復(fù)習(xí)了下才看的。不管你看沒看, 代碼先貼出來:
// PureComponent function ComponentDummy() {} ComponentDummy.prototype = Component.prototype; // 發(fā)現(xiàn)PureComponnet的構(gòu)造方法和Component是相同的 /** * Convenience component with default shallow equality check for sCU. */ function PureComponent(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; this.updater = updater || ReactNoopUpdateQueue; } const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy()); pureComponentPrototype.constructor = PureComponent; // Avoid an extra prototype jump for these methods. // 感覺不用加也可以, 只不過會(huì)多查找一次,但是不得不說細(xì)節(jié)考慮的真棒 Object.assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = true;
我畫了個(gè)圖來理解這個(gè)繼承。
首先是創(chuàng)建了一個(gè)ComponentDummy構(gòu)造函數(shù),他的原型指到Component的原型;然后創(chuàng)建了一個(gè)PureComponent, 加上了和Component一樣的屬性(這里為啥不用call)。PureComponent的原型指向ComponentDummy的實(shí)例;修改PureComponent原型的constructor屬性使其正確指向PureComponent的構(gòu)造函數(shù),并掛一個(gè)isPureReactComponent的屬性。為了減少向上去查找原型鏈次數(shù),用了一個(gè)assign直接將Component原型的東西拷貝到PureComponent的原型上(這里還是考慮的比較精細(xì)的)。
首先這個(gè)實(shí)現(xiàn)沒有啥問題,但是我有個(gè)疑問,大家可以留言指點(diǎn)下:
為什么要用繼承,注意到PureComponent的構(gòu)造函數(shù)和Component是一樣的,然后還有一個(gè)拷貝Component的原型到PureComponent的原型的操作,那這里有繼承的必要嗎?不都是重寫的嗎,感覺多此一舉。
下一篇預(yù)告 ReactElement源碼解析
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/103351.html
摘要:只涉及了,其他均沒有自己實(shí)現(xiàn)。這種組件的復(fù)用性是最強(qiáng)的。所以會(huì)新建,只繼承的原型,不包括,以此來節(jié)省內(nèi)存。 showImg(https://segmentfault.com/img/remote/1460000019783989); 一、React.Component() GitHub:https://github.com/AttackXiaoJinJin/reactExplain/...
摘要:自身的源碼也很簡單,節(jié)選如下上面的就是暴露給外部使用的。關(guān)于的源碼分析就到這里。這是內(nèi)部使用的一個(gè)工具集。不過也不是萬能的,特定情況下自己實(shí)現(xiàn)可能更高效。 TL;DR React 15.3.0 新增了一個(gè) PureComponent 類,以 ES2015 class 的方式方便地定義純組件 (pure component)。這篇文章分析了一下源碼實(shí)現(xiàn),并衍生探討了下 shallowCo...
摘要:本次分析的源碼采用的是的版本核心接口提供了處理的工具集我們先來看看做了什么事情即當(dāng)為空時(shí),返回不為時(shí)調(diào)用,最終返回一個(gè)數(shù)組這里說一下,可以通過傳入的對所有子組件進(jìn)行操作,具體使用方法看下圖參數(shù)通過配合的例子把父組件的賦值給每個(gè)子組件我們先不 本次分析的源碼采用的是16.4.1的版本 核心接口 showImg(https://segmentfault.com/img/bVbeT9f?w=...
摘要:往往純的單頁面應(yīng)用一般不會(huì)太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺(tái)應(yīng)用中我會(huì)將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動(dòng)端跨平臺(tái)App和桌面應(yīng)用的能力,Taro則賦...
摘要:往往純的單頁面應(yīng)用一般不會(huì)太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺(tái)應(yīng)用中我會(huì)將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動(dòng)端跨平臺(tái)App和桌面應(yīng)用的能力,Taro則賦...
閱讀 1816·2019-08-30 13:54
閱讀 2730·2019-08-29 17:27
閱讀 1116·2019-08-29 17:23
閱讀 3355·2019-08-29 15:20
閱讀 1231·2019-08-29 11:28
閱讀 1575·2019-08-26 10:39
閱讀 1321·2019-08-26 10:29
閱讀 646·2019-08-26 10:13