头图

深入理解 React 源码,带你从零实现 React v18 的核心功能,构建自己的 React 库。

电子书地址:https://2xiao.github.io/leetcode-js/react

源代码地址:https://github.com/2xiao/my-react

React 是由卓越工程师们在数年时间内精心打造的库,其中必定蕴含了许多值得借鉴的经验和智慧。

如果你渴望更进一步,不仅仅停留在 API 的使用层面,而是追求更深入前端技术的探索,那么掌握 React 源码将成为你技能提升的极佳途径。

本系列文章遵循 React 源码的核心思想,通俗易懂的解析 React 源码,带你从零实现 React v18 的核心功能,学完本系列,你将有这些收获:

  • 面试加分:框架底层原理是面试必问环节,熟悉 React 源码会为你的面试加分,也会为你拿下 offer 增加不少筹码;
  • 提升开发效率:熟悉 React 源码之后,会对 React 的运行流程有新的认识,让你在日常的开发中,对性能优化、使用技巧和 bug 解决更加得心应手;
  • 巩固基础知识:学习本书也顺便巩固了数据结构和算法,如 reconciler 中使用了 fiber、update、链表等数据结构,diff 算法要考虑怎样降低对比复杂度;

本系列文章的特色:

  • 教程详细,代码开源,带你构建自己的 React 库;
  • 功能全面,可跑通官方测试用例;
  • 按 Git Tag 划分迭代步骤,记录每个功能的实现过程;

1. 选择项目结构

选择 Multi-repo 还是 Mono-repo?

  • Multi-repo 每个库都有独立的仓库,逻辑清晰,但协同管理会更繁琐;
  • Mono-repo 可以方便的协同管理不同独立的库的生命周期,但是会有更高的操作复杂度。

很多大型项目都使用 Mono-repo 结构管理,比如 Vue,Bable,我们也选择 Mono-repo。

2. 选择 Mono-repo 工具

简单工具:

  • npm workspace
  • Yarn workspace
  • pnpm workspace

专业工具:

  • nx
  • bit
  • turborepo
  • rush
  • nx
  • lerna

我们选择 pnpm,因为它相比其它打包工具,依赖安装更快,更规范(处理幽灵依赖问题)。

安装 pnpm

npm install -g pnpm

初始化pnpm

pnpm init

在根目录新增 pnpm-workspace.yaml文件,并初始化,写入下面的代码:

// pnpm-workspace.yaml

packages:
  - 'packages/*'

pnpm-workspace.yaml文件定义了工作空间的根目录,也就是说,packages 目录下的文件就是 Mono-repo 的子项目。

3. 配置代码规范

代码规范检查与修复,使用 eslint

安装:

pnpm i eslint -D -w

新增.gitignore文件,忽略node_modules目录下的文件:

node_modules/*

初始化 eslint:

npx eslint --init

选项选择如下:

√ How would you like to use ESLint?
· To check syntax and find problems

√ What type of modules does your project use?
· JavaScript modules (import/export)

√ Which framework does your project use?
· none

√ Does your project use TypeScript?
· Yes

√ Where does your code run?
· node

√ What format do you want your config file to be in?
· JSON

√ Would you like to install them now?
· Yes

√ Which package manager do you want to use?
· pnpm

如果报错,再执行下面的代码,手动安装这两个包和 typescript:

pnpm i -D -w @typescript-eslint/eslint-plugin, @typescript-eslint/parser, typescript

此时,自动生成了一个.eslintrc.json文件,它是对 eslint 的一些配置,将.eslintrc.json文件配置如下:

// .eslintrc.json

{
    "env": {
        "browser": true,
        "es2021": true,
        "node": true,
        "jest": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "prettier",
        "plugin:prettier/recommended"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": "latest",
        "sourceType": "module"
    },
    "plugins": ["@typescript-eslint", "prettier"],
    "rules": {
        "prettier/prettier": "error",
        "no-case-declarations": "off",
        "no-constant-condition": "off",
        "@typescript-eslint/ban-ts-comment": "off",
        "@typescript-eslint/no-unused-vars": "off",
        "@typescript-eslint/no-var-requires": "off",
        "no-unused-vars": "off"
    }
}

安装 ts 的eslint插件:

pnpm i @typescript-eslint/eslint-plugin -D -w

安装代码风格检查 prettier

pnpm i prettier -D -w

新建.prettierrc.json配置文件,添加配置:

// .prettierrc.json

{
    "printWidth": 80,
    "tabWidth": 2,
    "useTabs": true,
    "singleQuote": true,
    "semi": true,
    "trailingComma": "none",
    "bracketSpacing": true
}

prettier集成到eslint中,避免它和eslint冲突,其中:

  • eslint-config-prettier:覆盖eslint本身的规则配置
  • eslint-plugin-prettier:用prettier来接管修复代码,即eslint --fix
pnpm i eslint-config-prettier eslint-plugin-prettier -D -w

package.json"scripts" 中增加 lint 对应的执行脚本:

"lint": "eslint --ext .ts,.jsx,.tsx --fix --quiet ./packages"

4. 配置 commit 规范检查

安装husky,用于拦截 commit 命令:

pnpm i husky -D -w

初始化huaky

npx husky install

将刚才实现的格式化命令pnpm lint纳入 commit 时husky将执行的脚本:

npx husky add .husky/pre-commit "pnpm lint"

使用commitlint堆 git 提交信息进行检查:

pnpm i commitlint @commitlint/cli @commitlint/config-conventional -D -w

新建配置文件.commitlintrc.js,指定规范集:

// .commitlintrc.js

module.exports = {
    extends: ['@commitlint/config-conventional']
};

commitlint集成到husky中:

npx husky add .husky/commit-msg "npx --no-install commitlint -e $HUSKY_GIT_PARAMS"

这样之后所有的 commit 都必须符合规范集的格式:提交的类型: 摘要信息,即<type>: <subject>,例如:git commit -m "feat: project init"

常见的 type 值如下:

  • feat: 新功能
  • fix: 修复
  • docs: 文档变更
  • style: 代码格式
  • refactor: 重构
  • perf: 性能优化
  • test: 增加测试
  • revert: 回退
  • build: 打包
  • chore: 构建过程或辅助工具的变动

5. 配置 typescript

新建配置文件tsconfig.json,并添加以下配置:

// tsconfig.json

{
    "compileOnSave": true,
    "include": ["./packages/**/*"],
    "compilerOptions": {
        "target": "ESNext",
        "useDefineForClassFields": true,
        "module": "ESNext",
        "lib": ["ESNext", "DOM"],
        "moduleResolution": "Node",
        "strict": true,
        "sourceMap": true,
        "resolveJsonModule": true,
        "isolatedModules": true,
        "esModuleInterop": true,
        "noEmit": true,
        "noUnusedLocals": false,
        "noUnusedParameters": false,
        "noImplicitReturns": false,
        "skipLibCheck": true,
        "baseUrl": "./packages",
        "paths": {
            "hostConfig": ["./react-reconciler/src/hostConfig.ts"]
        }
    }
}

6. 选择打包工具

有一个比较权威的网站https://bundlers.tooling.report/,比较了不同的打包工具的区别。

可以看到webpack是比较大而全的,但是我们要开发的是一个库,而不是业务代码,希望工具尽可能简洁,打包产物可读性高,所以选择rollup

安装rollup:

pnpm i rollup -D -w

新建文件夹scripts/rollup,用于放所有的打包脚本。


至此,我们的项目框架就搭建完成了。

相关代码可在 git tag v1.1 查看,地址:https://github.com/2xiao/my-react/tree/v1.1

版权声明:本系列文章是基于 《从 0 实现 React18》 整理创作的,视频教程的作者是 @卡颂


TNTWEB
3.8k 声望8.5k 粉丝

腾讯新闻前端团队