React - 源码
react 源码架构
源码目录结构
- fixtures:为代码贡献者提供的测试React
- packages:主要部分,包含Scheduler(调度器:排序优先级,优先级高的任务先进性处理),reconciler(协调器:找出哪些节点发生了变化,打上不同的tag)等
- scripts:react构建相关
packages主要包含模块
- react:核心Api(React.createElement、React.Component)
- render相关的:react-art(canvas、svg的渲染)、react-dom(浏览器环境)、react-native-renderer(原生相关)、react-noop-renderer(调试或fiber用)
- 试验型的包:react-server(ssr相关)、react-fetch(请求相关)、react-interactions(事件相关)、react-reconciler(构建节点)
- shared:公共方法和变量
- 辅助包:react-is(判断类型)、react-client(流相关)、react-fetch(数据请求)、react-refresh(热加载)、scheduler(调度器)、react-reconciler(构建节点)
jsx & 核心Api
virtual Dom
用js对象表示dom信息和结构,用类似react-dom等模块与真实dom同步,这个过程为协调(reconciler),这种方式可以声明式的渲染相应的ui状态,在react中以fiber树的形式存放组件树的相关信息,在更新时可以增量渲染相关dom,所以fiber也是virtual Dom实现的一部分
大量的dom操作慢,很小的更新都可能引起页面的重新排列,js对象优于内存中,处理更快,可以通过diff算法比较新老virtual Dom的差异,批量、异步、最小化的执行dom的变更,提高性能
可以跨平台,jsx–>ReactElement对象–>真实节点
jsx文件要声明
import React from 'react'
(react17 之后不用导入)是因为jsx是
ClassComponent的render函数
或者FunctionComponent
的返回值,表示组件内容,经过babel编译后,最后被编译成React.createElementReact.createElement
的源码做了几件事- 处理config,剩余的属性将添加到props
- 处理children
- 处理defaultProps
- 调用ReactElement返回jsx对象(virtual-dom)
1 | // ReactElement.js |
- 组件的类型:$$typeof,源码中有检查是否是合法Element的函数
ClassComponent
type是class本身,FunctionCoponent
type是这个function,创建的组件首字母必须大写,否则被当成普通节点,type是字符串1
2
3
4
5
6
7
8// ReactElement.js
export function isValidElement(object){
return (
typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE
)
}
// const REACT_ELEMENT_TYPE = Symbol.for('react.element');
- component
1 | // ReactBaseClasses.js |
模式入口函数
react有3中进入主体函数的入口
- legacy:
ReactDOM.render(<APP />,rootNode)
- blocking(实验):
ReactDOM.createBlockingRoot(rootNode).render(<APP />)
- concurrent:
ReactDOM.createRoot(rootNode).render(<APP />)
- legacy:
不同
- legacy常用,构建dom的过程是同步的,在render的
reconciler
中,如果diff过程很耗时,导致js一直阻塞高优先级别的任务(点击事件),页面会卡顿无法响应 - react未来模式:
concurrent Mode
,用时间片调度实现了异步可中断任务,根据设备性能不同,时间片长度也不一样,在每个时间片中,任务到了过期时间,主动让出线程给高优先级的任务(scheduler&lane模型)
- legacy常用,构建dom的过程是同步的,在render的
主要执行流程
createRootImpl
创建fiberRootNode、rootFiber
updateContainer
创建Update对象,保存在updateQueue环状链表
scheduleUpdateOnFiber
在Fiber上调度update,ensureRootIsScheduled
调度根节点performSyncWorkOnRoot | performConcurrentWorkOnRoot(render阶段)
- commitRoot(commit阶段)
legacy
- render -> legacyRenderSubtreeIntoContainer -> createRootImpl -> createFiberRoot -> createHostRootFiber -> legacyRenderSubtreeIntoContainer(调用updateContainer) —> -> scheduleUpdateOnFiber(调用performSYncWorkOnRoot)进入render 和commit阶段
1 | function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback){ |
1 |
|
1 | function updateContainer(element, container,parentComponent, callback){ |
concurrent
1 | function ensureRootIsScheduled(root,currentTime){ |
- 不同点
- 传参不同:createRootImpl第二个参数,一个是LegacyRoot 一个是ConcurrentRoot
- requestUpdateLane 获取的lane的优先级不同
- 函数scheduleUpdateOnFiber中根据不同优先级进入不同分支:legacy模式进入performSyncWorkOnRoot, concurrent模式会异步调度performConcurrentWorkOnRoot