摘要:首先是創建了一個構造函數,他的原型指到的原型然后創建了一個加上了和一樣的屬性這里為啥不用。的原型指向的實例修改原型的屬性使其正確指向的構造函數,并掛一個的屬性。
每次都信誓旦旦的給自己立下要好好學習react源碼的flag,結果都是因為某個地方卡住了,或是其他原因沒看多少就放棄了。這次又給自己立個flag-堅持看完react源碼。為了敦促自己,特開設這樣一個專欄來記錄自己的學習歷程,這意味著這個專欄的文章質量并不高,你可以拿來參考參考,切莫全信,我不想誤人子弟,后面要是學有所成再考慮產出些好點的文章。 要是發現文章中有什么不當之處,歡迎批評交流。我看的源碼版本是16.8.2。我是用在源碼加注釋的方法學習的,放在github上。
為了看react源碼,我查找了不少資料,這里推薦兩個參考資料,個人覺得寫得不錯。
慕課網一個課的電子書,他有個源碼解析的視頻教程,應該不錯,不過我沒買。
一個知乎專欄,寫得很清晰,只不過是15.6.2的, 在react16里面一些方法找不到了。
Component, PureComponent是我們最常用的東西,我們經常繼承他們來創建組件。因此,我選擇從這幾個最最常用的東西入手開始欣賞React源碼。他們都位于packages/react目錄下,入口在index.js,index.js里邊導出的其實是src下的React.js里的東西,在React.js中可以看到React暴露的API。在React.js中可以找到上面說述的Component,PureComponent和ReactElement相關線索。
ComponentComponent和PureComponent都位于/packages/react/src/ReactBaseClasses.js。
這兩個東西都是構造函數,或者稱為類。
Component的構造函數長成如下這樣:
/** * Base class helpers for the updating state of a component. */ // 經常去繼承他,原來這個構造行數是這樣的 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. // 這個new的時候需要注意updater是哪里來的, 這個updater與setState應該有很大關系 this.updater = updater || ReactNoopUpdateQueue; }
這并沒有什么神奇的,他接收三個參數,掛到this上。具體是這三個參數是啥,我目前也是不清楚的,因為我們平時使用都是extends他而并沒有new他,new的過程應該是框架去做的,這個得到后面再做分析。后面分析時需要注意updater,感覺這里會是一個重點,他有一個默認值,ReactNoopUpdateQueue,去看了下他的代碼,他是一個對象,掛了一些方法,這里也就不展開了,我也沒太細看。
Component的原型上掛了一些方法和屬性,isReactComponent屬性,setState方法,forceUpdate方法,代碼如下:
// 通常isXxx都是boolean類型的,這里比較奇怪,后面需要關注下 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 */ // 原來我們平時調用的setState就這么幾行啊,但是看他是調用的updater的enqueueSetState, // 相關實現應該在那里邊了, 可以updater這個東西很厲害 Component.prototype.setState = function(partialState, callback) { // 這里是個參數校驗,校驗不通過的話會給提示信息,并拋出異常 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 */ // 很少用到這個方法啊, 但他和setState一樣都是Component原型上的方法 Component.prototype.forceUpdate = function(callback) { this.updater.enqueueForceUpdate(this, callback, "forceUpdate"); };
其實Component的原型上掛載的東西也沒什么神奇的,其中非常重要的是updater的enqueueSetState,enqueueForceUpdate方法,進一步說明了updater是后面分析的重點。
接下來的一段代碼是用來在開發模式下標記廢棄的api的,在開發模式下回給寫提示,代碼如下:
// 這里是標識一些廢棄的api, 開發模式會報出來提醒開發這注意 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__這個東西我沒找到是在哪里掛到全局的(知道的同學可以留言指點下),但是看變量名可以推測他是開發模式標識,這個提示我們在做一些給別人用的東西時,接口協議約定十分重要,一旦約定就不能輕易變更,確實需要變更時需要通知調用方調整。回頭來,這里標識廢棄了isMounted,replaceState兩個方法,其實他們被挪到了updater里邊。
PureComponent開始用React時老大Rewview我的代碼時經常寫評論,“你這個Component可以改成PureComponent”,當時一直不懂PureComponent與Component的區別(現在也沒全懂),只是聽人說PureComponent更新的時候是淺比較,而Component是深比較。今天看了這部分,其實也沒懂,不過感覺后面再看看應該就懂了。要搞清這里的PureComponet需要了解下js中繼承的實現,大家可以參考《JavaScript高級程序設計》相關介紹,也可以看看理解js繼承的6種方式, 筆者看到這個PureComponet也是先復習了下才看的。不管你看沒看, 代碼先貼出來:
// PureComponent function ComponentDummy() {} ComponentDummy.prototype = Component.prototype; // 發現PureComponnet的構造方法和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. // 感覺不用加也可以, 只不過會多查找一次,但是不得不說細節考慮的真棒 Object.assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = true;
我畫了個圖來理解這個繼承。
首先是創建了一個ComponentDummy構造函數,他的原型指到Component的原型;然后創建了一個PureComponent, 加上了和Component一樣的屬性(這里為啥不用call)。PureComponent的原型指向ComponentDummy的實例;修改PureComponent原型的constructor屬性使其正確指向PureComponent的構造函數,并掛一個isPureReactComponent的屬性。為了減少向上去查找原型鏈次數,用了一個assign直接將Component原型的東西拷貝到PureComponent的原型上(這里還是考慮的比較精細的)。
首先這個實現沒有啥問題,但是我有個疑問,大家可以留言指點下:
為什么要用繼承,注意到PureComponent的構造函數和Component是一樣的,然后還有一個拷貝Component的原型到PureComponent的原型的操作,那這里有繼承的必要嗎?不都是重寫的嗎,感覺多此一舉。
下一篇預告 ReactElement源碼解析
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103351.html
摘要:只涉及了,其他均沒有自己實現。這種組件的復用性是最強的。所以會新建,只繼承的原型,不包括,以此來節省內存。 showImg(https://segmentfault.com/img/remote/1460000019783989); 一、React.Component() GitHub:https://github.com/AttackXiaoJinJin/reactExplain/...
摘要:自身的源碼也很簡單,節選如下上面的就是暴露給外部使用的。關于的源碼分析就到這里。這是內部使用的一個工具集。不過也不是萬能的,特定情況下自己實現可能更高效。 TL;DR React 15.3.0 新增了一個 PureComponent 類,以 ES2015 class 的方式方便地定義純組件 (pure component)。這篇文章分析了一下源碼實現,并衍生探討了下 shallowCo...
摘要:本次分析的源碼采用的是的版本核心接口提供了處理的工具集我們先來看看做了什么事情即當為空時,返回不為時調用,最終返回一個數組這里說一下,可以通過傳入的對所有子組件進行操作,具體使用方法看下圖參數通過配合的例子把父組件的賦值給每個子組件我們先不 本次分析的源碼采用的是16.4.1的版本 核心接口 showImg(https://segmentfault.com/img/bVbeT9f?w=...
摘要:往往純的單頁面應用一般不會太復雜,所以這里不引入和等等,在后面復雜的跨平臺應用中我會將那些技術一擁而上。構建極度復雜,超大數據的應用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應用而生,Electron和React-native賦予了它構建移動端跨平臺App和桌面應用的能力,Taro則賦...
摘要:往往純的單頁面應用一般不會太復雜,所以這里不引入和等等,在后面復雜的跨平臺應用中我會將那些技術一擁而上。構建極度復雜,超大數據的應用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應用而生,Electron和React-native賦予了它構建移動端跨平臺App和桌面應用的能力,Taro則賦...
閱讀 1809·2019-08-30 13:54
閱讀 2725·2019-08-29 17:27
閱讀 1109·2019-08-29 17:23
閱讀 3350·2019-08-29 15:20
閱讀 1225·2019-08-29 11:28
閱讀 1566·2019-08-26 10:39
閱讀 1315·2019-08-26 10:29
閱讀 639·2019-08-26 10:13