0%

create-react-app核心源码解读

create-react-app是大家常用的用来创建react项目的脚手架,它的设计理念和实现思路值的我们学习。我研究了一下create-react-app源码,并把它的核心功能模块梳理出来。

下面是这篇文章的主要内容:

  1. 简单介绍create-react-app的使用

  2. 介绍create-react-app的流程,从全局上看create-react-app是怎么创建react项目的

  3. 详细的分析create-react-app的3个核心模块的实现

  4. 总结

    create-react-app快速入门

  5. 使用create-react-app创建项目my-app:

1
npx create-react-app my-app
  1. 进入 my-app 文件夹,执行 npm start 启动项目

默认阅读这篇文章的同学都是接触过create-react-app的人,所以不对如何使用create-react-app进行深入介绍,如果想了解详细情况请阅读官网文档(👉 create-react-app)。

必备知识

为了更好的了解create-react-app内部的实现原理,我们需要掌握以下这几个知识点:

1. monorepo管理

概念

Monorepo 是管理项目代码的一个方案,即在一个项目仓库(repo)中管理多个模块/包(package)。Monorepo的优势在于一个仓库维护多个模块,能够统一工作流,代码共享。

create-react-app使用Monorepo方案在packages下维护了11个包。这些包互相之间有一定的联系,放在一个仓库中维护方便代码管理。此思路也值的我们在工作中学习运用。

1
2
3
4
5
6
7
8
9
10
11
12
.
├── babel-plugin-named-asset-import
├── babel-preset-react-app
├── confusing-browser-globals
├── cra-template
├── cra-template-typescript
├── create-react-app
├── eslint-config-react-app
├── react-app-polyfill
├── react-dev-utils
├── react-error-overlay
└── react-scripts

使用

我们可以用lerna或yarn workspace实现Monorepo方案,此处介绍lerna构建基础Monorepo仓库过程:

  1. 进入项目目录,创建一个 lerna 管理的仓库
1
lerna init
  1. 增加一个 packages
1
lerna create my-package
  1. 发布包。提示输入新版本并更新所有在 github 和 npm的包。
1
lerna publish
  1. 把packages下所有包的依赖安装到根 node_modules。
1
2
lerna bootstrap

详细使用文档请查看:👉 lerna官网

2. node必备模块

commander

commander 是一个完整的node.js命令行解决方案,封装了获取命令行指令

  • .version方法可以设置版本,其默认选项为-V和–version
  • 通过.arguments可以为最顶层命令指定参数,对子命令而言,参数都包括在command调用之中了。尖括表示必填(eg. ),而方括号(eg. [optional])则代表选填。
  • 通过.usage选项可以修改帮助信息的首行提示

如下demo表示,运行create-react-app myApp op1,其中myApp是必须要写的,op1可不写

1
2
3
4
5
6
7
8
9
10
const chalk = require('chalk');
const {Command} = require('commander');
new Command('create-react-app')
.version('1.0.0')
.arguments(' [optional]')
.usage(`${chalk.green('')} [optional]`)
.action((must,optional,...args) => {
console.log(must,optional,args);
})
.parse(process.argv);

cross-spawn

  • cross-spawn是node的spawn和spawnSync的跨平台解决方案

  • inherit表示将相应的stdio流传给父进程或从父进程传入

1
2
3
4
5
6
7
const spawn = require('cross-spawn');
const child = spawn('node', ['script.js','one','two','three'], { stdio: 'inherit' });
child.on('close',()=>{
console.log('child is done!');
});
const result = spawn.sync('node', ['script.js','one','two','three'], { stdio: 'inherit' });
console.log(result);

create-react-app各模块介绍

create-react-app的实现过程可以用下面的流程图👇表示,最重要的是 create-react-appreact-scriptscra-template这三个模块。

img

梳理一下流程:

  1. 命令行输入npx create-react-app my-app

  2. 调用create-react-app模块,

    • 创建my-app文件夹

    • 写入package.json

    • 安装react, react-dom, cra-template, react-scripts 这四个模块

    • 调用react-scripts的init.js

  3. 调用react-scripts的init.js

    • 根据cra-template/template.jsonmy-app/package.json合并出新的package.json
    • 复制cra-template/template里的内容到my-app下
    • 安装项目依赖
    • 移除cra-template
  4. 得到目标文件夹 my-app。其中package.json的scripts脚本命令调用了react-scripts模块bin/react-scripts文件。

下面重点介绍 create-react-appreact-scriptscra-template这三个模块的具体实现。

create-react-app核心模块实现

create-react-app

1. 主要功能

create-react-app包是入口,用户在命令行输入npx create-react-app my-app会执行

  • 和用户交互,获取项目名 my-app
  • 创建my-app文件夹,安装react, react-dom, cra-template, react-scripts 这四个模块
  • 调用react-scripts/init.js

2. 核心代码

此处根据源码整理的逻辑图,方便大家阅读,

3. 简化版实现

为了便于理解,将上述逻辑简化了一下,实现了个简易版。项目地址:

可以通过npm i min-create-react-app -g 试用此模块。

react-scripts

1. 主要功能

  • 复制cra-template到目标文件夹
  • 提供webpack的功能

2. 实现思路

2.1 复制cra-template到目标文件夹

2.2 提供scripts命令:

img

关键代码:

  1. package.json中bin字段指向./bin/react-scripts.js说明命令行中执行react-scripts xxx 命令会执行此文件。

img

  1. ./bin/react-scripts.js 中第27行和31行说明实际上执行的是对应的build.js、eject.js、start.js和test.js这四个文件。

img

  1. react-scripts start命令
    1. 设置process.env.NODE_ENV = ‘development’
    1. 获取webpack配置文件config/webpackDevServer.config.js
    1. 调用react-dev-utils/WebpackDevServerUtils/createCompiler生成compiler
    1. 调用/config/webpackDevServer.config.js生成serverConfig
    1. 启动WebpackDevServer服务
    1. 启动浏览器,打开项目页面
  1. build命令
    1. 设置process.env.NODE_ENV = ‘production’;
    1. 获取webpack配置文件
    1. 清空build目录
    1. 拷贝public目录下的文件到build目录
    1. 创建compiler并调用run方法进行编译

react的webpack配置文件做了很多优化配置,值的我们学习:github.com/facebook/cr…

cra-template

1. 目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.
├── README.md
├── package.json
├── template
│ ├── README.md
│ ├── gitignore
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── robots.txt
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
└── template.json

2. 主要功能

cra-template放的是react基础项目模板,会被拷贝到目标文件夹成为基础项目文件。

  • public中存放静态资源
  • src中存放.js和.css文件
  • template.json中有此模板依赖的package,react-scripts在复制模板到目标文件夹时会将template.json和原package.json文件合并生成新的package.json

总结

至此,create-react-app的核心代码已经介绍完毕。

通过这篇文章,我们了解到以下几点:

  1. create-react-app 采用Monorepo方案,在一个仓库里管理create-react-app,react-scripts和cra-template等多个包,实现工作流和代码共享;

  2. create-react-app 项目中,create-react-app包是入口,实现了读取命令行中的项目名,创建项目文件夹,安装react, react-dom, cra-template, react-scripts 这四个模块,最后调用react-scripts的init.js

  3. react-scripts提供两块功能,一是复制cra-template到目标文件夹,二是提供webpack的功能

  4. cra-template放的是react基础项目模板,会被拷贝到目标文件夹成为基础项目文件

希望这篇文章能够对你有所帮助。

参考

facebook/create-react-app

create-react-app官网文档

基于 Lerna 管理 packages 的 Monorepo 项目最佳实践

使用 MonoRepo 管理前端项目