此文章是翻译How to develop apps bootstrapped with Create React App 官方文档
系列文章
- 如何开发由Create-React-App 引导的应用
- 如何开发由Create-React-App 引导的应用(一)
- 如何开发由Create-React-App 引导的应用(二)
- 如何开发由Create-React-App 引导的应用(四)
Integrating with an API Backend
这些教程将帮助您将应用程序与在另一个端口上运行的API后端集成,使用fetch()
来访问它。
Node
Ruby on Rails
Proxying API Requests in Development
注意:这个特性需要
react-scripts@0.2.3
版本以上。
人们通过提供与前端React 应用相同的主机和端口作为后端实现。
例如,应用部署后,生产设置可能看上去像这样:
1 | / - static server returns index.html with React app |
像这样的设置不是必须的。但是,如果你已经有了一个这样的设置,很方便写fetch('api/todos')
这样的请求而不必担心在开发过程中将它们重定向到另一个主机或端口。
在开发中,让开发服务器代理任何未知API 服务请求,添加一个proxy
域到你的package.json
,例如:
1 | "proxy": "http://localhost:4000" |
这样,当您在开发中fetch('/api/todos')
时,开发服务器将会认识到它不是一个静态资产,并会将您的请求代理到http://localhost:4000/api/todos
作为后备。 开发服务器将仅尝试发送没有text/html
accept header 的请求到代理。
方便的,这样可以避免在开发过程中发生CORS问题和像下面的错误消息:
1 | Fetch API cannot load http://localhost:4000/api/todos. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. |
请记住,proxy
仅在开发中有效(使用npm start
),由你确保像/api/todos
这样的URL指向正确的生产环境。 你不必使用/api
前缀。 任何无法识别没有text/html
accept header 的请求,将被重定向到指定的proxy
。
proxy
选项支持HTTP,HTTPS和WebSocket连接。
如果proxy
选项对你不够灵活,或者你可以:
- 在您的服务器上启用CORS(这是在Express 中的操作方法)。
- 使用环境变量将正确的服务器主机和端口注入到应用程序中。
Using HTTPS in Development
注意:这个特性需要
react-scripts@0.4.0
版本以上。
您可能需要开发服务器通过HTTPS提供页面。 一个特别的情况可能是有用的,当API服务器本身服务本身用于HTTPS时,使用the “proxy” feature来代理对API服务器的请求。
为此,请将HTTPS
环境变量设置为true
,然后像以往那样以npm start
启动开发服务器:
Windows(cmd.exe)
1 | set HTTPS=true&&npm start |
(注意:空格的缺失是有意的)
Linux, macOS(Bash)
1 | HTTPS=true npm start |
请注意,服务器将使用自签名证书,因此你的Web浏览器几乎肯定会在访问页面时显示警告。
Generating Dynamic <meta>
Tags on the Server
由于Create React App 不支持服务器渲染,你可能想知道如何使<meta>
标签动态化并反映当前URL。 为了解决这个问题,我们建议在HTML中添加占位符,如下所示:
1 | <!doctype html> |
然后,在服务器上,不管你使用的后端,你都可以将index.html
读入内存,并根据当前URL替换__OG_TITLE__
、__OG_DESCRIPTION__
以及任何其他具有值的占位符。 只需确保清理和转义内插的值,以便它们可以安全地嵌入到HTML中!
如果使用Node服务器,你甚至可以在客户端和服务器之间共享路由匹配逻辑。 但是在简单的情况下,复制它也可以正常工作。
Pre-Rendering into Static HTML Files
如果您使用静态主机提供商托管您的构建程序,则可以使用反应快照为应用程序中的每个路由或相对链接生成HTML页面。 然后,这些页面将在JavaScript软件包加载时无缝地变为活动状态或“水合”。
还有机会在静态托管之外使用它,在生成和缓存路由时将压力从服务器上取下。
预渲染的主要优点是,你可以使用HTML有效内容获取每个页面的核心内容,而不管你的JavaScript软件包是否成功下载。 这也增加了你的应用程序的每个路由将被搜索引擎抓取的可能性。
你可以阅读零配置预渲染(也成为snapshotting)。
Injecting Data from the Server into the Page
同上一节相似,你可以在HTML 中留一些插入全局变量的占位符,例如:
1 | <!document html> |
然后,在服务器上,您可以在发送响应之前将__SERVER_DATA__
替换为真实的JSON数据。 客户端代码可以读取window.SERVER_DATA
来使用它。 确保在清理JSON之后再发送到客户端,因为它使你的应用程序易受XSS攻击。
Running Test
注意:这个特性需要
react-scripts@0.3.0
版本以上。
阅读迁移指导如果在旧项目中使用
Create React App 使用Jest作为其测试运行器。 为了做好这个整合的准备,我们为Jest做了一个重大变革,所以如果你听到很多年前的坏事,请再试一次。
Jest是一个基于Node的运行器。 这意味着测试总是在Node环境中运行,而不是在真实的浏览器中运行。 这使我们能够实现加快迭代速度并防止怪异的行为。
虽然Jest提供像window
的浏览器全局变量,这多亏了jsdom,但它们只是同真正的浏览器行为相似。 Jest旨在用于你的逻辑和组件的单元测试,而不是DOM怪异。
如果需要,我们建议你使用单独的浏览器端到端的测试工具。 它们超出了Create React App的范围。
Filename Coventions
Jest将使用以下任何常见的命名规则来查找测试文件:
__tests__
文件夹中带有.js
后缀的文件。- 带有
.test.js
后缀的文件。 - 带有
.spec.js
后缀的文件。
.test.js
或.spec.js
文件(或__tests__
文件夹)可以位于src
顶级文件夹下的任意深度。
我们建议将测试文件(或__tests__
文件夹)放在正在测试的代码旁边,以使相对导入路径更短。 例如,如果App.test.js
和App.js
在同一个文件夹中,测试只需要从import App from './App'
,而不是长的相对路径。 Colocation还有助于在更大的项目中更快地找到测试。
Command Line Interface
当你运行npm test
时,Jest将以观察者模式启动。 每次保存文件时,都会重新运行测试,就像npm start
重新编译代码一样。
观察者包括交互式命令行界面,具有运行所有测试的能力,或专注于搜索模式。 它是这样设计的,以便你可以保持它开启并享受快速重新运行。 你可以从“Watch Usage” 笔记中了解每次运行后观察者打印的命令:
Version Control Integration
默认情况下,当您运行npm test
时,Jest将仅运行与上次提交后更改的文件相关的测试。 这是一个优化,旨在使你的测试运行快速,无论您有多少测试。 但是,它假定你不经常提交不通过测试的代码。
Jest将始终明确提到,它只运行与上次提交后更改的文件相关的测试。 你也可以在观察者模式按a
强制Jest运行所有测试。
Jest将始终在持续集成服务器上运行所有测试,或者该项目不在Git或Mercurial资源库中。
Writing Tests
要创建测试,请使用测试名称及其代码添加在it()
(或test()
)块。 你可以选择将它们包装在describe()
块中进行逻辑分组,但这不是必需的,也不是推荐的。
Jest提供了一个内置的expec()
全局函数来进行断言。 基本测试可能如下所示:
1 | import sum from './sum'; |
Jest支持的所有expect()
匹配器都在这里进行了广泛的记录。
你也可以使用[jest.fn()
和expect(fn).toBeCalled()
]创建“spies”或模拟函数。
Testing Components
有广泛的组件测试技术。 它们的范围从“冒烟测试(smoke test)”来验证组件渲染器没有错误抛出,到浅渲染来测试某些输出,到完全渲染来测试组件生命周期和状态更改。
不同的项目根据组件变化的频率及其包含的逻辑选择不同的测试权衡。 如果你尚未决定测试策略,我们建议你首先为组件创建简单的冒烟测试:
1 | import React from 'react'; |
该测试加载一个组件,并确保它在渲染过程中没有抛出错误。 这样的测试用很少的影响提供了很多价值,所以他们是伟大的起点,这个测试你可以在src/App.test.js
中找到。
当你遇到由更改组件导致的错误时,你将深入了解其中哪些部分在你的应用程序中值得测试。 这可能是引入更具体的测试来判断具体的预期输出或行为的好时机。
如果你想要脱离他们渲染的子组件来测试组件,我们建议使用Enzyme中的shallow() rendering API
。 你也可以写冒烟测试:
1 | npm install --save-dev enzyme react-addons-test-utils |
1 | import React from 'react'; |
与以前使用ReactDOM.render()
的冒烟测试不同,此测试仅渲染<App>
,而不会更深入。 例如,即使<App>
本身渲染的<Button>
抛出了错误,此测试也将通过。 浅渲染非常适合独立的单元测试,但你仍然可能需要创建一些完整的渲染测试,以确保组件正确集成。 Enzyme支持使用mount()
全渲染,还可以使用它来测试状态更改和组件生命周期。
你可以阅读Enzyme文档了解更多测试技术。 Enzyme文档使用Chai和Sinon作为断言,但你不必使用它们,因为Jest为spies提供了内置的expect()
和jest.fn()
。
以下是Enzyme文档中的一个例子,该文档声明了特定输出,使用Jest匹配器重写:
1 | import React from 'react'; |
所有Jest匹配器在这里被广泛记录。
不过,如下所述,你可以使用像Chai这样的第三方断言库。
此外,你可能会发现jest-enzyme有助于使用可读的匹配器简化你的测试。 以上contains
代码用jest-enzyme更简单。
1 | expect(wrapper).toContainReact(welcome) |
要使用Create React App设置jest-enzyme,请按照初始化测试环境的说明导入jest-enzyme
。
1 | npm install --save-dev jest-enzyme |
1 | // src/setupTests.js |
Using Third Party Assertino Libraries
我们建议你用expect()
的断言和jest.fn()
作为spies。 如果你遇到问题,请提交给Jest,我们会解决这些问题。 我们打算继续使他们更好的React,例如,美丽打印React元素作为JSX。
但是,如果你习惯于其他库,例如Chai和Sinon,或者如果你现有的代码使用了你想要移植的代码,那么你可以正常导入它们:
1 | import sinon from 'sinon'; |
然后在你的测试中像你通常那样使用它们。
Initializing Test Environment
注意:这个特性需要
react-scripts@0.4.0
版本以上。
在测试中,如果你的应用程序需要模拟浏览器API,或者在运行测试之前需要全局设置,请将src/setupTests.js
添加到你的项目中。 它将在运行测试之前自动执行。
例如:
1 | src/setupTests.js |
1 | const localStorageMoke = { |
Focusing and Excluding Tests
你可以用xit()
替换it()
以临时排除将要执行的测试。
同样,fit()
可以让你专注于特定的测试,而无需运行任何其他测试。
Coverage Reporting
Jest有一个集成的覆盖报告,与ES6工作良好,不需要配置。
运行npm test -- --coverage
(注意在中间的额外的--
)包括覆盖报告如下所示:
请注意,测试的运行速度要慢得多,因此建议你从正常的工作流程中分离运行它。
Continuous Integration
默认情况下,npm test
使用交互式CLI运行观察器。 但是,您可以强制运行测试一次,并通过设置一个名为CI
的环境变量来完成该过程。
当使用npm run build
创建应用程序的构建时,默认情况下不会检查linter警告。 像npm test
一样,你可以通过设置环境变量CI
来强制构建执行linter警告检查。 如果遇到任何警告,则构建失败。
流行的CI服务器默认已经设置了环境变量CI,但你也可以自己做这个:
On CI servers
Travis CI
- 按照Travis Getting started指南,将你的GitHub 仓库与Travis 同步。你可以需要在个人资料页面手动初始化某些设置。
- 在你的git 仓库中添加
.travis.yml
文件。1
2
3
4
5
6
7
8
9
10language: node_js
node_js:
- 4
- 6
cache:
directories:
- node_modules
scripts:
- npm test
- npm run build - 通过git push 触发第一次构建。
- 配置你的Travis CI 构建,如果需要。
On your own environment
Windows(cmd.exe)
1 | set CI=true&&npm test |
1 | set CI=true&&npm run build |
(注意:空格的缺失是有意的)
Linux, macOS(Bash)
1 | CI=true npm test |
1 | CI=true npm run build |
测试命令将强制Jest运行测试一次,而不启动观察器。
如果你发现自己在开发中经常遇到这种情况,请提出一个问题来告诉我们你的用例,因为我们希望让观察者有最佳体验,并且可以随时更改工作流程以适应更多的工作流程。
构建命令将检查linter警告,如果找到任何警告,则会失败。
Disabling jsdom
默认的,生成项目的package.json
像这样:
1 | // ... |
如果你知道没有一个测试依赖于jsdom,你可以安全地删除--env=jsdom
,你的测试运行得更快。
为了帮助您解决问题,以下是需要jsdom的API列表:
- 任何像
window
和document
的浏览器全局变量 ReactDOM.render()
TestUtils.renderIntoDocument()
(上述的一个简写)- Enzyme 中的
mount()
相反,下面APIjsdom 不是必须的
最后,对于snapshot testing jsdom 也不是必须的。
Snapshot Testing
Snapshot testing是Jest的一个功能,可自动生成组件的文本快照并将其保存在磁盘上,以便在UI输出更改时,你可以在不在组件输出上手动写入任何断言的情况下获得通知。 详细了解snapshot testing。
Editor Integration
如果你使用Visual Studio Code,则有一个Jest扩展可以与Create React App开箱即用。 这在使用文本编辑器时提供了很多类似IDE的功能:使用潜在的故障消息内联显示测试运行的状态,自动启动和停止观察器,并提供一键式快照更新。
Developing Components in Isolation
通常,在应用程序中,你有很多UI组件,并且它们每个都有许多不同的状态。 例如,一个简单的按钮组件可以具有以下状态:
- 带有文本标签。
- 用表情符号。
- 禁用模式。
通常,如果不运行示例应用程序或一些示例,很难看到这些状态,。
默认情况下,Create React App不包含这些工具,但你可以轻松地将React Storybook添加到你的项目中。 它是一个第三方工具,可让你开发组件,并与你的应用程序隔离,查看所有状态。
你也可以将Storybook部署为静态应用。 这样,你的团队中的每个人都可以查看和查看UI组件的不同状态,而无需启动后端服务器或在应用程序中创建一个帐户。
如何在你的应用中设置Storybook
首先,全局安装下面npm 包
1 | npm install -g getstorybook |
然后,在你的应用目录中运行下面命令:
1 | getstorybook |
之后,按照屏幕上的说明。
了解更多React Storybook:
- 屏幕录像:Getting Started with React Storybook
- GitHub Repo
- 文档
- React Storybook 的Snapshot Testing
Making a Progressive Web App
你可以按照此仓库中的步骤将你的React应用程序转换为Progressive Web App。
Deployment
npm run build
为你应用程序的生产构建创建一个build
目录。 设置你最喜欢的HTTP服务器,以便为你的站点的访问者提供index.html
,并且请求静态路径像/static/js/main.<hash>.js
获取/static/js/main.<hash>.js
文件的内容。
Static Server
对于使用Node的环境,处理这种情况的最简单的方法是安装serve并让其处理rest:
1 | npm install -g serve |
上面显示的最后一个命令将在端口5000上为你的静态站点提供服务。像许多serve的内部设置一样,端口可以使用-p
或--port
标志进行调整。
运行此命令以获取可用选项的完整列表:
1 | serve -h |
Other Solutions
你不一定需要静态服务器才能在生产中运行Create React App 项目。 它的工作也能很好集成到一个现有的动态项目中。
以下是使用Node和Express的编程示例:
1 | const express = require('express'); |
你的服务器软件的选择也不重要。 由于Create React App完全与平台无关,因此无需明确使用Node。
具有静态资源的build
文件夹是Create React App生成的唯一输出。
但是,如果你使用客户端路由,这还不够。 如果你希望在单页应用程序中支持像/todos/42
这样的URL,请阅读下一节。
Serving Apps with Client-Side Routing
如果在底层,你使用了HTML5 pushState
history API的路由器(例如,使用browserHistory
的React Router),则许多静态文件服务器将失败。 例如,如果你的React Router 中的route 使用/todos/42
,则开发服务器将正确响应localhost:3000/todos/42
,但是Express 服务器的生产版本将失败。
这是因为当/todos/42
作为新的页面加载时,服务器会查找build/todos/42
文件,但是找不到。 服务器需要配置为通过index.html
来响应/todos/42
的请求。 例如,我们可以修改我们上面的Express示例,为任何未知路径提供index.html
:
1 | app.use(express.static('./build')); |
如果你使用Apache,则需要在public
文件夹中创建一个.htaccess
文件,如下所示:
1 | Options -MultiViews |
运行npm run build
时,它将被复制到build
文件夹。
现在,对/todos/42
的请求将在开发和生产中都被正确处理。
Building for Relative Paths
默认情况下,Create React App会生成一个构建,假设你的应用程序是托管在服务器根目录。
要覆盖它,请指定package.json
中的homepage
,例如:
1 | "homepage": "http://mywebsite.com/relativepath" |
这将使Create React App正确地推断在生成的HTML文件中使用的根路径。
Serving the Same Build from Different Paths
注意:这个特性需要
react-scripts@0.9.0
版本以上。
如果你没有使用HTML5 pushState
history API,或者根本不使用客户端路由,则无需指定服务应用程序的URL。 相反,你可以把它放在你的package.json中:
1 | "homepage": ".", |
这将确保所有资源路径都相对于index.html
。 然后,你可以将你的应用程序从http://mywebsite.com
移动到http://mywebsite.com/relativepath
甚至是http://mywebsite.com/relative/path
,而无需重新构建它。
Azure
请参阅此博文,了解如何将React应用程序部署到Microsoft Azure。
Firebase
如果你尚未运行npm install -g firebase-tools
,请安装Firebase CLI。 注册一个Firebase帐户并创建一个新项目。 运行firebase login
并使用以前创建的Firebase帐户登录。
然后从项目的根目录运行firebase init
命令。 你需要选择Hosting:Configure and deploy Firebase Hosting sites,并选择你在上一步骤中创建的Firebase项目。 你将需要同意正在创建的database.rules.json
,选择build
作为公共目录,并通过回复y
来同意Configuire as a single-page app。
1 | === Project Setup |
现在,在使用npm run build
创建生产构建之后,可以通过运行firebase deploy
进行部署。
1 | === Deploying to 'example-app-fd690'... |
更多信息请看添加Firebase 到你的JavaScript 项目。
Github Pages
注意:这个特性需要
react-scripts@0.2.0
版本以上。
Step 1:Add homepage
to package.json
下面的步骤很重要!
如果你跳过它,你的应用程序将无法正确部署。
打开你的package.json
,添加homepage
域:
1 | "homepage": "https://myusername.github.io/my-app", |
Create React App 使用homepage
来确定构建的HTML文件中的根URL。
Step 2:
Install gh-pages
and add ‘deploy’ to ‘scripts’ in package.json
现在,每当运行npm run build
时,你将看到一个备忘单,其中包含如何部署到GitHub Pages的说明。
要发布在 https://myusername.github.io/my-app,请运行:
1 | npm install --save-dev gh-pages |
在package.json
中添加下面脚本:
1 | // ... |
predeploy
会在运行deploy
之前自动运行。
Step 3:Deploy the site by running npm rung deploy
然后运行:
1 | npm run deploy |
Step 4: Ensure your project’s settings use gh-pages
最后,确保GitHub项目设置中的GitHub Pages选项设置为使用gh-pages
分支:
Step 5: Optionally, configure the domain
你可以通过将CNAME文件添加到public/
文件夹来配置具有GitHub Pages的自定义域。
Notes on client-side routing
GitHub Pages 在底层不支持使用HTML5 pushState
history API的路由器(例如,使用browserHistory
的React Router)。 这是因为当一个网址载入新的页面时,如http://user.github.io/todomvc/todos/42,其中`/todos/42`是前端路由,GitHub Pages服务器返回404,因为它不知道/todos/42
任何事。 如果要将路由器添加到GitHub Pages上托管的项目,以下是一些解决方案:
- 你可以从使用HTML5 history API切换到使用哈希的路由。 如果你使用React Router,你可以切换到
hashHistory
实现此效果,但是该URL会更长更详细(例如,http://user.github.io/todomvc/#/todos/42?_k=yknaj
) 。 阅读有关React Router中不同历史记录实现的更多信息。 - 或者,你可以使用一个技巧来教导GitHub Pages通过使用特殊的重定向参数重定向到你的
index.html
页面来处理404.html。 在部署项目之前,你需要将一个包含重定向代码的404.html
文件添加到build
文件夹中,并且你需要添加将redirect参数处理的代码到index.html
。 你可以在本指南中找到此技术的详细说明。
Heroku
使用Heroku Buildpack for Create React App。
你可以在Deploying React with Zero Configuration 找到介绍。
Resolving Heroku Deployment Errors
有时npm run build
在本地工作,但在部署期间通过Heroku失败。 以下是最常见的情况。
“Module not found:Error:Cannot resolve ‘file’ or ‘directory’”
如果你得到这样的东西:
1 | remote: Failed to create a production build. Reason: |
这意味着你需要确保import
的文件或目录的文字大小符合你在文件系统或GitHub上看到的文件或目录。
这很重要,因为Linux(Heroku使用的操作系统)区分大小写。 所以MyDirectory
和mydirectory
是两个不同的目录,因此,尽管项目在本地构建成,但是区别在于破坏Heroku 远程上的import
语句。
“Could not find a required file.”
如果从包中排除或忽略必需的文件,你将看到类似于以下错误:
1 | remote: Could not find a required file. |
在这种情况下,请确保该文件位于正确的大小写,并且在本地.gitignore
或~/.gitignore_global
上不被忽略。
Modulus
请参阅Modulus博客文章,了解如何将你的react应用部署到Modulus。
Netlify
To do a manual deploy to Netlify’s CDN
1 | npm install netlify-cli |
选择build
作为部署路径。
To setup continuous delivery:
使用此设置,当你push a git或open a pull request 时,Netlify将构建和部署:
- 启动一个新的netlify项目
- 选择你的Git托管服务并选择你的仓库
- 单击
Build your site
Support for client-side routing:
要支持pushState
,请确保使用以下重写规则创建一个public/_redirects
文件:
1 | / * /index.html 200 |
构建项目时,Create React App会将public
文件夹内容放入构建输出。
Now
now提供零配置的单命令部署。
- now可以通过推荐的桌面工具或通过node 的
npm install -g now
。 - 通过运行
npm install --save serve
来安装serve
。 - 将此行添加到
package.json
中的scripts
中:1
"now-start": "serve build/",
- 在从你的项目目录中运行
now
。 你的输出中将会显示一个now.sh
URL,如下所示:构建完成后,将该URL粘贴到浏览器中,你将看到已部署的应用程序。1
> Ready! https://your-project-dirname-tpspyhtdtk.now.sh (copied to clipbord)
本文提供了详细信息。
S3 and CloudFront
请参阅此博文,介绍如何将你的React应用程序部署到Amazon Web Services S3和CloudFront。
Surge
如果你尚未运行npm install -g surge
,请安装Surge CLI。 运行surge
命令并登录或创建一个新帐户。
当询问项目路径时,请确保指定build
文件夹,例如:
1 | project path: /path/to/project/build |
请注意,为了支持使用HTML5 pushState
API的路由器,你可能需要将构建文件夹中的index.html
重新命名为200.html
,然后再部署到Surge。 这样可以确保每个URL都回退该文件。