react源碼解析5.jsx&核心api
視訊課程(高效學習):進入課程
課程目錄:
1.開篇介紹和面試題
2.react的設計理念
3.react源碼架構
4.源碼目錄結構和調試
5.jsx&核心api
6.legacy和concurrent模式入口函數
7.Fiber架構
8.render階段
9.diff算法
10.commit階段
11.生命周期
12.狀态更新流程
13.hooks源碼
14.手寫hooks
15.scheduler&Lane
16.concurrent模式
17.context
18事件系統
19.手寫迷你版react
20.總結&第一章的面試題解答
21.demo
virtual Dom是什麼
一句話概括就是,用js對象表示dom資訊和結構,更新時重新渲染更新後的對象對應的dom,這個對象就是React.createElement()的傳回結果
virtual Dom是一種程式設計方式,它以對象的形式儲存在記憶體中,它描述了我們dom的必要資訊,并且用類似react-dom等子產品與真實dom同步,這一過程也叫協調(reconciler),這種方式可以聲明式的渲染相應的ui狀态,讓我們從dom操作中解放出來,在react中是以fiber樹的形式存放元件樹的相關資訊,在更新時可以增量渲染相關dom,是以fiber也是virtual Dom實作的一部分
為什麼要用virtual Dom
大量的dom操作慢,很小的更新都有可能引起頁面的重新排列,js對象優于在記憶體中,處理起來更快,可以通過diff算法比較新老virtual Dom的差異,并且批量、異步、最小化的執行dom的變更,以提高性能
另外就是可以跨平台,jsx --> ReactElement對象 --> 真實節點,有中間層的存在,就可以在操作真實節點之前進行對應的處理,處理的結果反映到真實節點上,這個真實節點可以是浏覽器環境,也可以是Native環境
virtual Dom真的快嗎?其實virtual Dom隻是在更新的時候快,在應用初始的時候不一定快
react源碼5.1
const div = document.createElement('div');
let str = ''
for(let k in div){
str+=','+k
}
console.log(str)
複制
react源碼5.2
jsx&createElement
jsx可以聲明式的描述視圖,提升開發效率,通過babel可以轉換成React.createElement()的文法糖,也是js文法的擴充。
jsx是ClassComponent的render函數或者FunctionComponent的傳回值,可以用來表示元件的内容,在經過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表示的是元件的類型,例如在源碼中有一個檢查是否是合法Element的函數,就是根object.$$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建立的,則type是這個function,源碼中用ClassComponent.prototype.isReactComponent來差別二者。注意class或者function建立的元件一定要首字母大寫,不然後被當成普通節點,type就是字元串。
jsx對象上沒有優先級、狀态、effectTag等标記,這些标記在Fiber對象上,在mount時Fiber根據jsx對象來建構,在update時根據最新狀态的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,這個函數在下一章講解,這裡重點關注ReactDom.render()使用時候的三個參數。
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函數中主要在目前執行個體上挂載了props、context、refs、updater等,是以在元件的執行個體上能拿到這些,而更新主要的承載結構就是updater, 主要關注isReactComponent,它用來表示這個元件是類元件
總結:jsx是React.createElement的文法糖,jsx通過babel轉化成React.createElement函數,React.createElement執行之後傳回jsx對象,也叫virtual-dom,Fiber會根據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};
複制