React 之源码概览

马丁路德
• 阅读 1296

本节将对 React 的源码架构,约定及其实现进行概述。

如果您想参与 React 的开发,我们希望这份指南可以帮助你更加轻松地进行修改。

我们并不推荐在 React 应用中遵循这些约定。有许多约定是历史原因,并且之后也许会有所修改。

项目根目录

当克隆 React 仓库之后,你们将看到项目根目录的信息:

  • packages 包含元数据(比如 package.json)和 React 仓库中所有 package 的源码(子目录 src)。如果你需要修改源代码, 那么每个包的 src 子目录是你最需要花费精力的地方。
  • fixtures 包含一些给贡献者准备的小型 React 测试项目。
  • build 是 React 的输出目录。源码仓库中并没有这个目录,但是它会在你克隆 React 并且第一次构建它之后出现。

文档位于 React 仓库之外的一个独立仓库中

还有一些其他的顶层目录,但是它们几乎都是工具类的,并且在贡献代码时基本不会涉及。

共置测试

我们没有单元测试的顶层目录。而是将它们放置在所需测试文件的相同目录下的 __tests__ 的目录之中。

比如,一个用于 setInnerHTML.js 的测试文件,会存放在 __tests__/setInnerHTML-test.js,就在它同级目录下。

warning 和 invariant

React 代码库直接使用 console.error 来展示 warnings:

if (__DEV__) {
  console.error('Something is wrong.');
}

warning 仅在开发环境中启用。在生产环境中,他们会被完全剔除掉。如果你需要在生产环境禁止执行某些代码,请使用 invariant 模块代替 warning

var invariant = require('invariant');

invariant(
  2 + 2 === 4,
  'You shall not pass!'
);

invariant 判别条件为 false 时,会将 invariant 的信息作为错误抛出

“Invariant” 用于声明 “这个条件应总为 true”。你可以把它当成一种断言。

保持开发和生产环境的行为相似是十分重要的,因此 invariant 在开发和生产环境下都会抛出错误。不同点在于在生产环境中这些错误信息会被自动替换成错误代码,这样可以让输出库文件变得更小。

开发环境与生产环境

你可以在代码库中使用 __DEV__ 这个伪全局变量,用于管理开发环境中需运行的代码块

这在编译阶段会被内联,在 CommonJS 构建中,转化成 process.env.NODE_ENV !== 'production' 这样的判断。

对于独立构建来说,在没有 minify 的构建中,它会变成 true,同时在 minify 的构建中,检测到的 if 代码块会被完全剔除。

if (__DEV__) {
  // 仅在开发环境下执行的代码
}

Flow

我们最近将 Flow 引入源码,用于类型检查。在许可证头部的注释中,标记为 @flow 注释的文件是已经经过类型检查的。

我们接受添加 Flow 注释到现有代码。Flow 注释看上去像这样:

ReactRef.detachRefs = function(
  instance: ReactInstance,
  element: ReactElement | string | number | null | false,
): void {
  // ...
}

如果可以的话,新代码应尽量使用 Flow 注释。 你可以运行 yarn flow,用 Flow 本地检查你的代码。

Multiple Packages

React 采用 monorepo 的管理方式。仓库中包含多个独立的包,以便于更改可以一起联调,并且问题只会出现在同一地方。

React Core

React “Core” 中包含所有全局 React API,比如:

  • React.createElement()
  • React.Component
  • React.Children

React 核心只包含定义组件必要的 API。它不包含协调算法或者其他平台特定的代码。它同时适用于 React DOM 和 React Native 组件。

React 核心代码在源码的 packages/react 目录中。在 npm 上发布为 react 包。相应的独立浏览器构建版本称为 react.js,它会导出一个称为 React 的全局对象。

渲染器

React 最初只是服务于 DOM,但是这之后被改编成也能同时支持原生平台的 React Native。因此,在 React 内部机制中引入了“渲染器”这个概念。

渲染器用于管理一棵 React 树,使其根据底层平台进行不同的调用。

渲染器同样位于 packages/ 目录下:

另外一个官方支持的渲染器的是 react-art。它曾经是一个独立的 GitHub 仓库,但是现在我们将此加入了主源代码树。

注意:

严格说来,react-native-renderer 实现了 React 和 React Native 的连接。真正渲染 Native 视图的平台特定代码及组件都存储在 React Native 仓库中。

reconcilers

即便 React DOM 和 React Native 渲染器的区别很大,但也需要共享一些逻辑。特别是协调算法需要尽可能相似,这样可以让声明式渲染,自定义组件,state,生命周期方法和 refs 等特性,保持跨平台工作一致。

为了解决这个问题,不同的渲染器彼此共享一些代码。我们称 React 的这一部分为 “reconciler”。当处理类似于 setState() 这样的更新时,reconciler 会调用树中组件上的 render(),然后决定是否进行挂载,更新或是卸载操作。

Reconciler 没有单独的包,因为他们暂时没有公共 API。相反,它们被如 React DOM 和 React Native 的渲染器排除在外。

Stack reconciler

“stack” reconciler 是 React 15 及更早的解决方案。虽然我们已经停止了对它的使用, 但是这在下一章节有详细的文档。

Fiber reconciler

“fiber” reconciler 是一个新尝试,致力于解决 stack reconciler 中固有的问题,同时解决一些历史遗留问题。Fiber 从 React 16 开始变成了默认的 reconciler。

它的主要目标是:

  • 能够把可中断的任务切片处理。
  • 能够调整优先级,重置并复用任务。
  • 能够在父元素与子元素之间交错处理,以支持 React 中的布局。
  • 能够在 render() 中返回多个元素。
  • 更好地支持错误边界。

你可以在这里这里,深入了解 React Fiber 架构。虽然这已经在 React 16 中启用了,但是 async 特性还没有默认开启。

源代码在 packages/react-reconciler 目录下。

事件系统

React 在原生事件基础上进行了封装,以抹平浏览器间差异。其源码在 packages/react-dom/src/events 目录下。

本文转自 https://react.docschina.org/docs/codebase-overview.html,如有侵权,请联系删除。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这