React 之源码概览

马丁路德 等级 317 0 0

本节将对 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,如有侵权,请联系删除。

收藏
评论区

相关推荐

为什么 React 源码不用 TypeScript 来写?
周末的,看点轻松的吧,之前看过 React 的源码,比较好奇像 React 这样庞大的工程为什么没有用 TypeScript。 Facebook 工程师 Cat Chen 在知乎上(https://www.zhihu.com/question/378470381/answer/1079675543(https://www.zhihu.com/quest
Next.js页面渲染的优化方案
Next.js页面渲染的优化方案 在过去一年的工作中我所使用的js框架是Next.js,尽管这个框架在前后端同构方面有着绝佳的体验,但是当页面js文件过大以及preload过多的时候还是会出现页面跳转卡顿和渲染阻塞等比较糟糕的用户体验问题。由于我之前既不知道这个框架的工作原理,自然也就不知道如何去优化它。乘着农历春节前工地
《精通react/vue组件设计》之配合React Portals实现一个功能强大的抽屉(Drawer)组件
前言 本文是笔者写组件设计的第六篇文章,内容依次从易到难,今天会用到react的高级API React Portals,它也是很多复杂组件必用的方法之一. 通过组件的设计过程,大家会接触到一个完成健壮的组件设计思路和方法,也能在实现组件的过程逐渐对react/vue的高级知识和技巧有更深的理解和掌握,并且在企业实际工作做游刃有余. 之所以会写组件设计相关
《彻底掌握redux》之开发一个任务管理平台(上)
前言 redux是上手react开发的必经之路,也是目前react项目中使用的最流行状态管理库。虽然我们不使用redux也可以通过react的state和父子props进行基本的数据通信和项目开发,但是对于一个大型项目而言,往往考虑的更多的是代码结构和组件之间的通信,我们需要一种很优雅且有利于扩展的方式去开发我们的复杂系统,所以这种情况下使用redux是最佳
25、react入门教程
25、react入门教程 25、react入门教程 0. React介绍 0.1 什么是React? React(有时称为React.js 或ReactJS)是一
Create React App
Create React App Create React App Facebook开源了React前端框架(MIT Licence),也同时提供了React脚手架 createreactapp(ht
Hook 简介 – React
Hook 简介 _Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 import React, { useState } from 'react'; function Example() { // 声明一个新的叫做 “count” 的 sta
使用 State Hook – React
使用 State Hook_Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。中使用下面的例子介绍了 Hook:import React, { useState } from 'react';function Example() { //
React 之源码概览
本节将对 React 的源码架构,约定及其实现进行概述。如果您想 的开发,我们希望这份指南可以帮助你更加轻松地进行修改。我们并不推荐在 React 应用中遵循这些约定。有许多约定是历史原因,并且之后也许会有所修改。 项目根目录当克隆 之后,你们将看到项目根目录的信息: 包含元数据(比如 package.json)和 React 仓库中所有
React 之Virtual DOM 及内核
什么是 Virtual DOM?Virtual DOM 是一种编程概念。在这个概念里, UI 以一种理想化的,或者说“虚拟的”表现形式被保存于内存中,并通过如 ReactDOM 等类库使之与“真实的” DOM 同步。这一过程叫做。这种方式赋予了 React 声明式的 API:您告诉 React 希望让 UI 是什么状态,React 就确保 DOM 匹
Omi架构与React Fiber
写在前面 Omi框架(https://github.com/AlloyTeam/omi)在架构设计的时候就决定把update的控制权交给了开发者,视灵活性比生命还重要。不然的话,如果遇到React Fiber要解决的这类问题的话,就需要
React 灵魂 23 问,你能答对几个?
1、setState 是异步还是同步? 1. 合成事件中是异步 2. 钩子函数中的是异步 3. 原生事件中是同步 4. setTimeout中是同步 相关链接:你真的理解setState吗?: 2、聊聊 react@16.4 的生命周期相关连接:React 生命周期 我对 React v16.4 生命周期的
React Router 使用教程
React Router 使用教程作者: 日期: 真正学会 是一个漫长的过程。你会发现,它不是一个库,也不是一个框架,而是一个庞大的体系。想要发挥它的威力,整个技术栈都要配合它改造。你要学习一整套解决方案,从后端到前端,都是全新的做法。举例来说,React 不使用 HTML,而使用 JSX 。它打算抛弃 DOM,要求开发者
React - 认识生命周期
这周开始学习React的生命周期。React的生命周期从广义上分为三个阶段:挂载、渲染、卸载. 挂载卸载过程 constructor() componentWillMount() componentDidMount() componentWillUnmount() 更新过程 componentWillRece
面向初学者的 React 路由-React Router的完整指南!
因此,您正在尝试学习React.js。也许您甚至已经在其中构建了几个简单的项目。无论您是新开发人员还是有一定经验的人,都可能会发现自己必须开发具有不同页面和路线的Web应用程序。那就是React Router发挥作用的时候。什么是React Router?React Router提供了一种在React或React Native应用程序中实现路由的简便方法。入