create-react-app 核心思路分析

image.png

Create React App is an officially supported way to create single-page React applications. It offers a modern build setup with no configuration.

create react app 是 React 官方创建单页应用的方式,为了方便,下文皆简称 CRA。

它的核心思想我理解主要是:

脚手架核心功能中心化:使用 npx 保证每次用户使用的都是最新版本,方便功能的升级

模板去中心化:方便地进行模板管理,这样也允许用户自定义模板

脚手架逻辑和初始化代码逻辑分离:在 cra 中只执行了脚手架相关逻辑,而初始化代码的逻辑在 react-scripts 包里执行

本文主要就是通过源码分析对上述的理解进行阐述。

按照自己的理解,画了个流程图,大家可以带着该流程图去阅读源码(主要包含两个部分 create-react-app 和 react-scripts/init):

create-react-app 核心思路分析

如果图片不清晰可以微信搜索公众号 玩相机的程序员,回复 CRA 获取。

0. 用法

CRA 的用法很简单,两步:

安装:npm install -g create-react-app

使用:create-react-app my-app

这是常见的用法,会在全局环境下安装一个 CRA,在命令行中可以通过 create react app 直接使用。

现在更推荐的用法是使用 npx 来执行 create react app:

npx create-react-app my-app

这样确保每次执行 create-reat-app 使用的都是 npm 上最新的版本。

注:npx 是 npm 5.2+ 之后引入的功能,如需使用需要 check 一下本地的 npm 版本。

默认情况下,CRA 命令只需要传入 project-directory 即可,不需要额外的参数,更多用法查看:,就不展开了。

可以看一下官方的 Demo 感受一下:

create-react-app 核心思路分析

我们主要还是通过 CRA 的源码来了解一下它的思路。

1. 入口

本文中的 create-react-app 版本为 4.0.1。若阅读本文时存在 break change,可能就需要自己理解一下啦

按照正常逻辑,我们在 package.json 里找到了入口文件:

{ "bin": { "create-react-app": "./index.js" } }

index.js 里的逻辑比较简单,判断了一下 node 环境是否是 10 以上,就调用 init 了,所以核心还是在 init 方法里。

// index.js const { init } = require('./createReactApp'); init();

打开 createReactApp.js 文件一看,好家伙,1017 行代码(别慌,跟着我往下看,1000 行代码也分分钟看明白)

吐槽一下,虽然代码逻辑写得很清楚,但是为啥不拆几个模块呢?

找到 init 方法之后发现,其实就执行了一个 Promise:

// createReactApp.js function init() { checkForLatestVersion() .catch() .then(); }

注意这里是先 catch 再 then。

跟着我往下看呗 ~ 一步一步理清楚 CRA,你也能依葫芦画瓢造一个。

2. 检查版本

checkForLatestVersion 就做了一件事,获取 create-react-app 这个 npm 包的 latest 版本号。

如果你想获取某个 npm 包的版本号,可以通过开放接口 [https://registry.npmjs.org/-/package/{pkgName}/dist-tags](https://registry.npmjs.org/-/package/%7BpkgName%7D/dist-tags "https://registry.npmjs.org/-/package/{pkgName}/dist-tags") 获得,其返回值为:

{ "next": "4.0.0-next.117", "latest": "4.0.1", "canary": "3.3.0-next.38" }

如果你想获取某个 npm 包完整信息,可以通过开放接口 [https://registry.npmjs.org/{pkgName}](https://registry.npmjs.org/%7BpkgName%7D "https://registry.npmjs.org/{pkgName}") 获得,其返回值为:

{ "name": "create-react-app", # 包名 "dist-tags": {}, # 版本语义化标签 "versions": {}, # 所有版本信息 "readme": "", # README 内容(markdown 文本) "maintainers": [], "time": {}, # 每个版本的发布时间 "license": "", "readmeFilename": "README.md", "description": "", "homepage": "", # 主页 "keywords": [], # 关键词 "repository": {}, # 代码仓库 "bugs": {}, # 提 bug 链接 "users": {} }

回到源码,checkForLatestVersion().catch().then(),注意这里是先 catch 再 then,也就是说如果 checkForLatestVersion 里抛错误了,会被 catch 住,然后执行一些逻辑,再执行 then。

是的,Promise 的 catch 后面的 then 还是会执行。

2.1 Promise catch 后的 then

我们可以做个小实验:

function promise() { return new Promise((resolve, reject) => { setTimeout(() => { reject('Promise 失败了'); }, 1000); }); } promise() .then(res => { console.log(res); }) .catch(error => { console.log(error); // Promise 失败了 return `ErrorMessage: ${error}`; }) .then(res => { console.log(res); // ErrorMessage: Promise 失败了 });

原理也很简单,then 和 catch 返回的都是一个 promise,当然可以继续调用。

OK,checkForLatestVersion 以及之后的 catch 都是只做了一件事,获取 latest 版本号,如果没有就是 null。

这里拿到版本号之后也就判断一下当前使用的版本是否比 latest 版本低,如果是就推荐你把全局的 CRA 删了,使用 npx 来执行 CRA。

3. 核心方法 createApp

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfjyy.html