今天看了react源碼,僅以記錄。 1:monorepo (react 的代碼管理方式) 與multirepo 相對。 monorepo是單代碼倉庫, 是把所有相關項目都集中在一個代碼倉庫中,每個module獨立發佈,每個module都有自己的依賴項(package.json),能夠作為獨立的npm ...
今天看了react源碼,僅以記錄。
1:monorepo (react 的代碼管理方式)
與multirepo 相對。 monorepo是單代碼倉庫, 是把所有相關項目都集中在一個代碼倉庫中,每個module獨立發佈,每個module都有自己的依賴項(package.json
),能夠作為獨立的npm package發佈,只是源碼放在一起維護。
下圖是典型monorepo目錄圖,react為例
2: setState
✋ setState "react/src/ReactBaseClasses.js"
* @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
*/
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');
};
可以看到,幹了兩件事情:第一:調用方法invariant(), 第二:調用 this.updater.enqueueSetState
✋ invariant() "shared/invariant" 讓我們先去看看第一個方法幹嘛了!
export default function invariant(condition, format, a, b, c, d, e, f) {
validateFormat(format);
if (!condition) { // 導出了一個方法,可以看出如果condition為false的話, 拋出錯誤
let error;
if (format === undefined) {
error = new Error(
'Minified exception occurred; use the non-minified dev environment ' +
'for the full error message and additional helpful warnings.',
);
} else {
const args = [a, b, c, d, e, f];
let argIndex = 0;
error = new Error(
format.replace(/%s/g, function() {
return args[argIndex++];
}),
);
error.name = 'Invariant Violation';
}
error.framesToPop = 1; // we don't care about invariant's own frame
throw error;
}
}
得出,這個方法就是判斷partialState 的type, 不正確的話拋錯誤。 Facebook工程師把這個拋錯誤封裝了成了invariant函數,嗯,以後工作中可以這樣做!
✋ this.updater.enqueueSetState 讓我們再去看看第二個是幹嘛了!
首先,this.updater 什麼鬼:
import ReactNoopUpdateQueue from './ReactNoopUpdateQueue';
/**
* Base class helpers for the updating state of a component.
*/
function Component(props, context, updater) {//在這個文件中導出了兩個構造函數Component和PureComponent
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.
this.updater = updater || ReactNoopUpdateQueue; // 這裡是this.updater, 這個標黃的 是default updater
}
✋ ReactNoopUpdateQueue "react/src/ReactNoopUpdateQueue.js" 導出的一個對象,裡面有好幾個方法,其中一個就是setState用到的this.updater.enqueueSetState(this, partialState, callback, 'setState'):
enqueueSetState: function(
publicInstance, // 實例this
partialState, // 新的state
callback, // 回調
callerName,
) {
warnNoop(publicInstance, 'setState');
},
// 啊 這個裡面是default updater
✋ enqueueSetState定義 "react-dom/src/server/ReactPartialRenderer.js" 實際的updater從哪裡來的呢?哎,我是暫時沒找到,但是,我知道這個實際的updater肯定有enqueueSetState方法,那我就全局搜索一下,找到enqueueSetState的定義:
let updater = { //這裡是updater對象,裡面各種方法
isMounted: function(publicInstance) {
return false;
},
enqueueForceUpdate: function(publicInstance) {
if (queue === null) {
warnNoop(publicInstance, 'forceUpdate');
return null;
}
},
enqueueReplaceState: function(publicInstance, completeState) {
replace = true;
queue = [completeState];
},
enqueueSetState: function(publicInstance, currentPartialState) {
if (queue === null) { // 這是一個錯誤情況,下麵代碼中warnNoop()方法就是在dev環境中給出空操作警告的
warnNoop(publicInstance, 'setState');
return null;
}
queue.push(currentPartialState); // 把現在要更新的state push到了一個queue中
},
};
// 接下來代碼解決了我上面找不到實際updater的疑問!
let inst;
if (shouldConstruct(Component)) {
inst = new Component(element.props, publicContext, updater); // 在new的時候,就把上面真實的updater傳進去啦!!!
.......後面還有好多,不過與我這一期無關了.
✋ queue 這是react提升性能的關鍵。並不是每次調用setState react都立馬去更新了,而是每次調用setState, react只是push到了待更新的queue中! 下麵是對這個queue的處理!
if (queue.length) {
let oldQueue = queue;
let oldReplace = replace;
queue = null;
replace = false;
if (oldReplace && oldQueue.length === 1) { // 隊列裡面,只有一個,直接更換。
inst.state = oldQueue[0];
} else { // 隊列裡面有好幾個,先進行合併,再更新
let nextState = oldReplace ? oldQueue[0] : inst.state;
let dontMutate = true;
for (let i = oldReplace ? 1 : 0; i < oldQueue.length; i++) {
let partial = oldQueue[i];
let partialState =
typeof partial === 'function'
? partial.call(inst, nextState, element.props, publicContext)
: partial;
if (partialState != null) { // 這裡合併啦,新的是nextState
if (dontMutate) {
dontMutate = false;
nextState = Object.assign({}, nextState, partialState);
} else {
Object.assign(nextState, partialState);
}
}
}
inst.state = nextState; //最後賦值給實例
}
} else {
queue = null;
}