create-react-app核心源码解读

2,554 阅读6分钟

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快速入门

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

npx create-react-app my-app

2. 进入 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个包。这些包互相之间有一定的联系,放在一个仓库中维护方便代码管理。此思路也值的我们在工作中学习运用。

.
├── 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 管理的仓库

lerna init

2. 增加一个 packages

lerna create my-package

3. 发布包。提示输入新版本并更新所有在 github 和 npm的包。

lerna publish

4. 把packages下所有包的依赖安装到根 node_modules。

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可不写

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流传给父进程或从父进程传入
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这三个模块。

梳理一下流程:

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命令: 

关键代码:

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

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

3. react-scripts start命令

  • 1.设置process.env.NODE_ENV = 'development'
  • 2.获取webpack配置文件config/webpackDevServer.config.js
  • 3.调用react-dev-utils/WebpackDevServerUtils/createCompiler生成compiler
  • 4. 调用/config/webpackDevServer.config.js生成serverConfig
  • 4.启动WebpackDevServer服务
  • 5.启动浏览器,打开项目页面

4. build命令

  • 1.设置process.env.NODE_ENV = 'production';
  • 2.获取webpack配置文件
  • 3.清空build目录
  • 4.拷贝public目录下的文件到build目录
  • 5.创建compiler并调用run方法进行编译

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

cra-template

1. 目录结构

.
├── 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 管理前端项目