react源碼層深入刨析babel解析jsx實(shí)現(xiàn)
經(jīng)過(guò)多年的發(fā)展,React已經(jīng)更新了大版本16、17、18,本系列主要講的是 version:17.0.2,在講這個(gè)版本之前,我們先看一看在babel的編譯下,每個(gè)大版本之下會(huì)有什么樣的變化。
jsx
<div className='box'>
<h1 className='title' style={{'color':'red'}}>React源碼解析</h1>
<ul>
<li>第一章</li>
<li>第二章</li>
<li>第三章</li>
<li>第四章</li>
</ul>
</div>
v16.x及以前版本

v17及之后版本

所以各位看到了,在v16及以前我們babel進(jìn)行jsx解析編譯的是根據(jù)@babel/babel-preset-react-app解析成React.createElement進(jìn)行包裹的,而v17以及之后的版本,官網(wǎng)早就說(shuō)明,對(duì)jsx的轉(zhuǎn)換用react/jsx-runtime,而不再依賴(lài)React.createElement了,看到這里我想各位對(duì)不同版本的babel解析jsx已經(jīng)有了眉目了,早已經(jīng)迫不及待想去看看jsx-runtime和createElement到底是如何玩的,那么進(jìn)入源碼
在babel解析后的v17產(chǎn)物中我們可以看得到 var _jsxRuntime = require("react/jsx-runtime");那么我們追本溯源可以找到在packages/react/src/jsx/ReactJSX.js里面的jsxs是怎么來(lái)的
// packages/react/src/jsx/ReactJSX.js
import {REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
import {
jsxWithValidationStatic,
jsxWithValidationDynamic,
jsxWithValidation,
} from './ReactJSXElementValidator';
import {jsx as jsxProd} from './ReactJSXElement';
const jsx = __DEV__ ? jsxWithValidationDynamic : jsxProd;
const jsxs = __DEV__ ? jsxWithValidationStatic : jsxProd;
const jsxDEV = __DEV__ ? jsxWithValidation : undefined;
export {REACT_FRAGMENT_TYPE as Fragment, jsx, jsxs, jsxDEV};在非dev環(huán)境下我們繼續(xù)去找jsProd
export function jsx(type, config, maybeKey) {
let propName;
//標(biāo)簽上的屬性集合
const props = {};
//單獨(dú)處理key ref
let key = null;
let ref = null;
if (maybeKey !== undefined) {
key = '' + maybeKey;
}
if (hasValidKey(config)) {
// 處理合法的key
key = '' + config.key;
}
if (hasValidRef(config)) {
// 處理合法的ref
ref = config.ref;
}
// 把屬性加到props中
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
// 處理默認(rèn)props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement(
type,
key,
ref,
undefined,
undefined,
ReactCurrentOwner.current,
props
)
}ReactElement
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// 表示是否為ReactElement
$$typeof: REACT_ELEMENT_TYPE,
// 元素自身屬性
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner,
};
if (__DEV__) {
element._store = {};
// 開(kāi)發(fā)環(huán)境下將_store、_self、_source屬性變?yōu)椴豢擅杜e
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false,
});
Object.defineProperty(element, '_self', {
configurable: false,
enumerable: false,
writable: false,
value: self,
});
Object.defineProperty(element, '_source', {
configurable: false,
enumerable: false,
writable: false,
value: source,
});
// 凍結(jié)props、element防止被手動(dòng)修改
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}
return element;
};這上面便是v17及之后版本的jsx-runtime所做的事情。那么這里再去看一下v16中的createElement所做的事情吧。
相關(guān)參考視頻講解:進(jìn)入學(xué)習(xí)
React.createElement
// packages/react/src/ReactElement.js
export function createElement(type, config, children) {
let propName;
// 記錄標(biāo)簽上的屬性集合
const props = {};
//單獨(dú)處理key ref
let key = null;
let ref = null;
let self = null;
let source = null;
// 當(dāng)config部位null的時(shí)候,表示標(biāo)簽上有屬性,加到props里面去
if (config != null) {
// 合法的ref才做處理
if (hasValidRef(config)) {
ref = config.ref;
if (__DEV__) {
warnIfStringRefCannotBeAutoConverted(config);
}
}
if (hasValidKey(config)) {
// 有合法的key才做處理
key = '' + config.key;
}
// 記錄信息用于debug
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// 處理self,source,key,ref以外的屬性,加入props中
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// 處理子節(jié)點(diǎn)
const childrenLength = arguments.length - 2;
// 單標(biāo)簽子節(jié)點(diǎn)
if (childrenLength === 1) {
props.children = children;
//嵌套子節(jié)點(diǎn)
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
//開(kāi)發(fā)環(huán)境凍結(jié),childArray防止被修改
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
// 處理默認(rèn)props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
// dev環(huán)境下,key 與 ref不掛到props中去
if (key || ref) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
// 調(diào)用返回
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}由React.createElement源碼得知,他做了如下事情
- 解析
config參數(shù)中是否有合法的key、ref屬性,并處理,并將其他的屬性?huà)斓?code>props上。 - 解析函數(shù)的第三參數(shù),并分情況將第三參數(shù)掛到
props.children上。 - 對(duì)默認(rèn)props進(jìn)行處理,如果存在該屬性則直接掛載到props上,不存在則要添加上。
- 開(kāi)發(fā)環(huán)境下將
_store、_self、_source設(shè)置為不可枚舉狀態(tài),為后期的diff比較作優(yōu)化,提高比較性能。 - 將
type、key、ref、props等屬性通過(guò)調(diào)用ReactElement函數(shù)創(chuàng)建虛擬dom。
ReactElement
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner,
};
if (__DEV__) {
// The validation flag is currently mutative. We put it on
// an external backing store so that we can freeze the whole object.
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
element._store = {};
// To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false,
});
// self and source are DEV only properties.
Object.defineProperty(element, '_self', {
configurable: false,
enumerable: false,
writable: false,
value: self,
});
// Two elements created in two different places should be considered
// equal for testing purposes and therefore we hide it from enumeration.
Object.defineProperty(element, '_source', {
configurable: false,
enumerable: false,
writable: false,
value: source,
});
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}
return element;
};仔細(xì)瞧一瞧,這個(gè)其實(shí)跟jsxs調(diào)用的ReactElement實(shí)現(xiàn)的差不多的功能,但是為什么要寫(xiě)兩遍?仔細(xì)看來(lái),在兩個(gè)版本的ReactElement中,傳入的參數(shù)不一致,在開(kāi)發(fā)環(huán)境下,分別對(duì)其做劫持不可枚舉狀態(tài),僅此而已
React.Component
寫(xiě)慣了hooks組件,但是Class組件也別忘了喲,因?yàn)樵?code>React17里面Class組件也是沒(méi)有被抹去的,所以既然是源碼解析,那么我們也要來(lái)看一看這個(gè)Component到底干了啥。
// packages/react/src/ReactBaseClasses.js
function Component(props, context, updater) {
// 接受各種參數(shù),掛到this上
this.props = props;
this.context = context;
this.refs = emptyObject;
// updater ??
this.updater = updater || ReactNoopUpdateQueue;
}
// 原型上掛載了isReactComponent用來(lái)區(qū)分函數(shù)組件與類(lèi)組件
Component.prototype.isReactComponent = {};
//原型上掛載了setState方法用來(lái)觸發(fā)更新
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.',
);
// 調(diào)用updater上的enqueueSetState方法???
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
// 原型上掛載了強(qiáng)制更新的方法
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};從源碼上可以得知,React.Component 主要做了以下幾件事情:
- 將
props, context, updater掛載到this上,props,context一目了然,后面的updater位觸發(fā)器,上面掛了很多方法,我們后面再談。 - 在
Component原型鏈上添加isReactComponent對(duì)象,用于區(qū)分函數(shù)組件還是類(lèi)組件。 - 在
Component原型鏈上添加setState方法,觸發(fā)更新。 - 在
Component原型鏈上添加forceUpdate方法,強(qiáng)制更新。
總結(jié)
不管是類(lèi)組件還是函數(shù)組件,最終我們寫(xiě)的jsx都被babel轉(zhuǎn)化成了可識(shí)別的元素,其中我們也看了ReactElement,createElement,Component等內(nèi)部實(shí)現(xiàn),了解到了作為ReactElement他是怎么被創(chuàng)建的,但是遠(yuǎn)遠(yuǎn)沒(méi)有完,因?yàn)槲覀冎牢覀冊(cè)趯?xiě)React的時(shí)候,會(huì)在后面帶上一個(gè)ReactDOM.render(<Element/>, 'root'),沒(méi)錯(cuò)我們下一章節(jié)就要去探索一下ReactDOM.render方法了。
到此這篇關(guān)于react源碼層深入刨析babel解析jsx實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)react babel解析jsx內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決React初始化加載組件會(huì)渲染兩次的問(wèn)題
這篇文章主要介紹了解決React初始化加載組件會(huì)渲染兩次的問(wèn)題,文中有出現(xiàn)這種現(xiàn)象的原因及解決方法,感興趣的同學(xué)跟著小編一起來(lái)看看吧2023-08-08
React?Native?中實(shí)現(xiàn)倒計(jì)時(shí)功能
這篇文章主要介紹了React?Native中實(shí)現(xiàn)倒計(jì)時(shí)功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
React Router6.x路由表封裝的兩種寫(xiě)法
本文主要介紹了React Router6.x路由表封裝的兩種寫(xiě)法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
webpack 2的react開(kāi)發(fā)配置實(shí)例代碼
本篇文章主要介紹了webpack 2的react開(kāi)發(fā)配置實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07
Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能實(shí)例詳解
今天是使用?Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能,前端我們使用?Reactjs?+?Axios?來(lái)搭建前端上傳文件應(yīng)用,后端我們使用?Node.js?+?Express?+?Multer?+?Mongodb?來(lái)搭建后端上傳文件處理應(yīng)用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2022-06-06
react實(shí)現(xiàn)頁(yè)面水印效果的全過(guò)程
大家常常關(guān)注的是網(wǎng)站圖片增加水印,而很少關(guān)注頁(yè)面水印,其實(shí)這個(gè)需求也是比較常見(jiàn)的,比如公文系統(tǒng)、合同系統(tǒng)等,這篇文章主要給大家介紹了關(guān)于react實(shí)現(xiàn)頁(yè)面水印效果的相關(guān)資料,需要的朋友可以參考下2021-09-09
axios請(qǐng)求響應(yīng)數(shù)據(jù)加解密封裝實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了axios請(qǐng)求響應(yīng)數(shù)據(jù)加解密封裝實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03

