react源碼解析5.jsx&核心api
視頻課程(高效學習):進入課程
課程目錄:
6.legacy和concurrent模式入口函數(shù)
virtual Dom是什么
一句話概括就是,用js對象表示dom信息和結構锄禽,更新時重新渲染更新后的對象對應的dom矾湃,這個對象就是React.createElement()的返回結果
virtual Dom是一種編程方式登钥,它以對象的形式保存在內(nèi)存中寝志,它描述了我們dom的必要信息憨降,并且用類似react-dom等模塊與真實dom同步描焰,這一過程也叫協(xié)調(reconciler)瘫证,這種方式可以聲明式的渲染相應的ui狀態(tài),讓我們從dom操作中解放出來狐赡,在react中是以fiber樹的形式存放組件樹的相關信息撞鹉,在更新時可以增量渲染相關dom疟丙,所以fiber也是virtual Dom實現(xiàn)的一部分
為什么要用virtual Dom
大量的dom操作慢,很小的更新都有可能引起頁面的重新排列鸟雏,js對象優(yōu)于在內(nèi)存中享郊,處理起來更快,可以通過diff算法比較新老virtual Dom的差異孝鹊,并且批量炊琉、異步、最小化的執(zhí)行dom的變更又活,以提高性能
另外就是可以跨平臺苔咪,jsx --> ReactElement對象 --> 真實節(jié)點,有中間層的存在柳骄,就可以在操作真實節(jié)點之前進行對應的處理团赏,處理的結果反映到真實節(jié)點上,這個真實節(jié)點可以是瀏覽器環(huán)境耐薯,也可以是Native環(huán)境
virtual Dom真的快嗎舔清?其實virtual Dom只是在更新的時候快,在應用初始的時候不一定快
const div = document.createElement('div');
let str = ''
for(let k in div){
str+=','+k
}
console.log(str)
jsx&createElement
jsx可以聲明式的描述視圖曲初,提升開發(fā)效率体谒,通過babel可以轉換成React.createElement()的語法糖,也是js語法的擴展臼婆。
jsx是ClassComponent的render函數(shù)或者FunctionComponent的返回值抒痒,可以用來表示組件的內(nèi)容,在經(jīng)過babel編譯之后颁褂,最后會被編譯成React.createElement
故响,這就是為什么jsx文件要聲明import React from 'react'
的原因(react17之后不用導入),你可以在 <a name="https://www.babeljs.cn/repl">babel編譯jsx</a> 站點查看jsx被編譯后的結果
`React.createElement`的源碼中做了如下幾件事
- 處理config痢虹,把除了保留屬性外的其他config賦值給props
- 把children處理后賦值給props.children
- 處理defaultProps
- 調用ReactElement返回一個jsx對象(virtual-dom)
//ReactElement.js
export function createElement(type, config, children) {
let propName;
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
//處理config被去,把除了保留屬性外的其他config賦值給props
//...
}
const childrenLength = arguments.length - 2;
//把children處理后賦值給props.children
//...
//處理defaultProps
//...
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE,//表示是ReactElement類型
type: type,//class或function
key: key,//key
ref: ref,//ref屬性
props: props,//props
_owner: owner,
};
return element;
};
typeof === REACT_ELEMENT_TYPE來判斷的
//ReactElement.js
export function isValidElement(object) {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}
如果組件是ClassComponent則type是class本身,如果組件是FunctionComponent創(chuàng)建的糜值,則type是這個function丰捷,源碼中用ClassComponent.prototype.isReactComponent來區(qū)別二者。注意class或者function創(chuàng)建的組件一定要首字母大寫寂汇,不然后被當成普通節(jié)點病往,type就是字符串。
jsx對象上沒有優(yōu)先級骄瓣、狀態(tài)停巷、effectTag等標記,這些標記在Fiber對象上,在mount時Fiber根據(jù)jsx對象來構建畔勤,在update時根據(jù)最新狀態(tài)的jsx和current Fiber對比蕾各,形成新的workInProgress Fiber,最后workInProgress Fiber切換成current Fiber庆揪。
render
//ReactDOMLegacy.js
export function render(
element: React$Element<any>,//jsx對象
container: Container,//掛載dom
callback: ?Function,//回調
) {
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
可以看到render所做的事也就是調用legacyRenderSubtreeIntoContainer式曲,這個函數(shù)在下一章講解,這里重點關注ReactDom.render()使用時候的三個參數(shù)缸榛。
component
//ReactBaseClasses.js
function Component(props, context, updater) {
this.props = props;//props屬性
this.context = context;//當前的context
this.refs = emptyObject;//ref掛載的對象
this.updater = updater || ReactNoopUpdateQueue;//更新的對像
}
Component.prototype.isReactComponent = {};//表示是classComponent
component函數(shù)中主要在當前實例上掛載了props吝羞、context、refs内颗、updater等钧排,所以在組件的實例上能拿到這些,而更新主要的承載結構就是updater均澳, 主要關注isReactComponent恨溜,它用來表示這個組件是類組件
總結:jsx是React.createElement的語法糖,jsx通過babel轉化成React.createElement函數(shù)负懦,React.createElement執(zhí)行之后返回jsx對象筒捺,也叫virtual-dom,F(xiàn)iber會根據(jù)jsx對象和current Fiber進行對比形成workInProgress Fiber
pureComponent也很簡單纸厉,和component差不多系吭,他會進行原型繼承,然后賦值isPureReactComponent
function PureComponent(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;
export {Component, PureComponent};