React16源码分析(1):react项目架构/文件目录/包结构解读
Author:[email protected] Date:
选取react的 16.7.0版本作为示例,解读源码
https://github.com/facebook/react/tree/master/packages 分析react的整体结构。
react结构剖析
React源码结构树:
React16的架构分为三层:
Scheduler(调度器):调度任务的优先级,高优任务优先进入Reconciler
Reconciler(协调器):负责找出变化的组件
Renderer(渲染器): renderers一直是React代码的核心部分,它包含了大部分功能的实现。
renderers源码目录:
任务调度(Scheduler)阶段有两个性能的优化点
将任务队列的内部数据结构转换成最小二叉堆的形式以提升队列的性能(在最小堆中我们能够以最快的速度找到最小的那个值,因为那个值一定在堆的顶部,有效减少整个数据结构的查找时间)。
使用周期更短的postMessage循环的方式而不是使用requestAnimationFrame这种与帧边界对齐的方式(这种优化方案指得是在将任务进行延迟后恢复执行的阶段,前后两种方案都是宏任务,但是宏任务也有顺序之分,postMessage的优先级比requestAnimationFrame高,这也就意味着延迟任务能够更快速地恢复并执行)。
React的核心
包含所有全局 React API,如:
React.createElement
React.Component
React.Children
这些API是全平台通用的,它不包含ReactDOM、ReactNative等平台特定的代码。
packages目录
scheduler文件夹 : Scheduler(调度器)的实现。
shared文件夹 :源码中其他模块公用的方法和全局变量,比如在shared/ReactSymbols.js中保存React不同组件类型的定义。
legacy-events文件夹:React的合成事件机制。
Renderer相关的文件夹:渲染相关
- react-art
- react-dom # 注意这同时是DOM和SSR(服务端渲染)的入口
- react-native-renderer
- react-noop-renderer # 用于debug fiber(后面会介绍fiber)
- react-test-renderer
试验性包的文件夹:React将自己流程中的一部分抽离出来,形成可以独立使用的包,由于他们是试验性质的,所以不被建议在生产环境使用。包括如下文件夹:
- react-server # 创建自定义SSR流
- react-client # 创建自定义的流
- react-fetch # 用于数据请求
- react-interactions # 用于测试交互相关的内部特性,比如React的事件模型
- react-reconciler # Reconciler的实现,你可以用他构建自己的Renderer
辅助包的文件夹:React将一些辅助功能形成单独的包。包括如下文件夹:
- react-is # 用于测试组件是否是某类型
- react-client # 创建自定义的流
- react-fetch # 用于数据请求
- react-refresh # “热重载”的React官方实现
react-reconciler文件夹:我们需要重点关注react-reconciler,在接下来源码学习中80%的代码量都来自这个包。
react的 16.7.0 node_modules/react
react 入口
进入react 目录,index.js
if (process.env.NODE_ENV === 'production') { module.exports = require('./cjs/react.production.min.js'); } else { module.exports = require('./cjs/react.development.js'); }
如果不是生产环境,就走react.development.js。纯代码也就一千多行的样子
看下这个JS,首先通过symbol 定义各种类型
// TODO: this is special because it gets imported during build. var ReactVersion = '16.7.0'; // The Symbol used to tag the ReactElement-like types. If there is no native Symbol // nor polyfill, then a plain number is used for performance. var hasSymbol = typeof Symbol === 'function' && Symbol.for; var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7; var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca; var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb; //……
到243行,队列更新相关代码
function warnNoop(publicInstance, callerName) { { var _constructor = publicInstance.constructor; var componentName = _constructor && (_constructor.displayName || _constructor.name) || 'ReactClass'; var warningKey = componentName + '.' + callerName; if (didWarnStateUpdateForUnmountedComponent[warningKey]) { return; } warningWithoutStack$1(false, "Can't call %s on a component that is not yet mounted. " + 'This is a no-op, but it might indicate a bug in your application. ' + 'Instead, assign to `this.state` directly or define a `state = {};` ' + 'class property with the desired state in the %s component.', callerName, componentName); didWarnStateUpdateForUnmountedComponent[warningKey] = true; } } /** * This is the abstract API for an update queue. */ var ReactNoopUpdateQueue = {}
376行,组件相关代码
react虚拟DOM实现的核心
import React, {Component} from 'react'; export default class App extends Component { constructor(props) {} render() { return (<div>demo<div>)} } ReactDOM.render(<App />, document.getElementById('root'));
App组件的render方法返回的是一段HTML结构,在普通的函数中这种写法是不支持的,所以我们一般需要相应的插件来在背后支撑,在React中为了支持这种jsx语法提供了一个Babel预置工具包@babel/preset-react,其中这个preset又包含了两个比较核心的插件:
@babel/plugin-syntax-jsx:这个插件的作用就是为了让Babel编译器能够正确解析出jsx语法。
@babel/plugin-transform-react-jsx:在解析完jsx语法后,因为其本质上是一段HTML结构,因此为了让JS引擎能够正确识别,我们就需要通过该插件将jsx语法编译转换为另外一种形式。在默认情况下,会使用React.createElement来进行转换,当然我们也可以在.babelrc文件中来进行手动设置。
转换后
render() { return React.createElement("div", { className: "content" }, React.createElement("header", null, "React learning"), React.createElement(List, { data: this.state.data })); }
可以看到jsx语法最终被转换成由React.createElement方法组成的嵌套调用链,可能你之前已经了解过这个API,或者接触过一些伪代码实现,这里我们就基于源码,深入源码内部来看看其背后为我们做了哪些事情。
从1810行,看react的整体结构
var React = { Children: { map: mapChildren, forEach: forEachChildren, count: countChildren, toArray: toArray, only: onlyChild }, createRef: createRef, Component: Component, PureComponent: PureComponent, createContext: createContext, forwardRef: forwardRef, lazy: lazy, memo: memo, Fragment: REACT_FRAGMENT_TYPE, StrictMode: REACT_STRICT_MODE_TYPE, Suspense: REACT_SUSPENSE_TYPE, createElement: createElementWithValidation, cloneElement: cloneElementWithValidation, createFactory: createFactoryWithValidation, isValidElement: isValidElement, version: ReactVersion, unstable_ConcurrentMode: REACT_CONCURRENT_MODE_TYPE, unstable_Profiler: REACT_PROFILER_TYPE, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals }; // Note: some APIs are added with feature flags. // Make sure that stable builds for open source // don't modify the React object to avoid deopts. // Also let's not expose their names in stable builds. if (enableStableConcurrentModeAPIs) { React.ConcurrentMode = REACT_CONCURRENT_MODE_TYPE; React.Profiler = REACT_PROFILER_TYPE; React.unstable_ConcurrentMode = undefined; React.unstable_Profiler = undefined; } // 一些有用的React Hooks方法 if (enableHooks) { React.useCallback = useCallback; React.useContext = useContext; React.useEffect = useEffect; React.useImperativeMethods = useImperativeMethods; React.useLayoutEffect = useLayoutEffect; React.useMemo = useMemo; React.useReducer = useReducer; React.useRef = useRef; React.useState = useState; }
这里我们专门来讲:《React16源码分析(2):react.development.js源码注释》
参考文章:
React技术揭秘1-4 源码的文件目录 https://juejin.im/post/5ef9b00a6fb9a07e894a9e04
React学习 -- React源码 https://blog.csdn.net/u014328357/article/details/73201116
React16源码解读:开篇带你搞懂几个面试考点 https://www.cnblogs.com/tangshiwei/p/12100306.html
转载本站文章《React16源码分析(1):react项目架构/文件目录/包结构解读》,
请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/jsBase/2016_0520_7834.html
延伸阅读:
- React16源码分析(2):react.development.js源码注释
- grunt使用yeoman自动化构建react项目……
- browser.js什么鬼?作用是什么
- React on ES6+:react.component vs react.createclass的异同
- React项目中常见的技术坑与优化及Component Generator
- react更新组件componentWillReceiveProp里面setState无效,未触发渲染
- can't resolve 'redux-thunk' in *** 项目不能跑起
- React+redux组件最简单的计算器!
- react+redux渲染页面空白,原来是大小写惹的祸害
- react:Uncaught TypeError: Cannot read property
- react组件中bind(this)写在哪里好?
- react-dom.min.js:15 Uncaught (in promise) Error: Minified React error
- react hook context 管理全局状态
- TypeScript写React组件默认属性问题
- React Query与SWR尚能饭否?React Query还真香!
- React 同构实践与思考
- React代数效应学习笔记
- ReactHook详解:memo/useMemo/useCallback等钩子细讲
- jsx动态class写法:vue3与react+classname库
- react异步数据如ajax请求应该放在哪个生命周期?
- React 源码剖析系列—生命周期的管理艺术—有限状态机
- React 源码剖析系列 - 解密 setState
- React16源码分析(0):对象池/合成事件/事务机制等概念科普
- React Fiber实现原理分析