- 前端
- 开发工具
【免费下载链接】guess
🔮 Libraries & tools for enabling Machine Learning driven user-experiences on the web
本篇技术指南以 packages/guess-parser/test/fixtures/react-app/README.md 为主体,剖析 guess 仓库中作为路由解析测试样例的 Create React App 样板工程:从目录结构、可用脚本、环境变量,到代码分割、测试、部署的完整实操流程。读完本文,你将理解 react-app fixture 的工程全貌,掌握 CRA 工程的日常开发与生产发布要点,并知晓它与 guess-parser 中 React JSX 路由解析能力的配合关系。
在 guess 仓库中,react-app 是一个由 Create React App(CRA)脚手架生成的 React 示例工程,被 guess-parser 用作静态路由解析的测试夹具(fixture)。它的package.json依赖react ^16.3.2、react-router ^4.2.0、react-loadable ^5.3.1与react-scripts 1.1.4,脚本则直接沿用 CRA 的四件套:start、build、test、eject。因此,这份 README 实际就是 CRA 官方模板文档,它完整定义了该 fixture 的构建、测试与部署约定,也直接决定了 guess-parser 能从中解析出哪些路由。
工程结构与构建入口
CRA 对工程目录有强制约定:public/index.html是页面模板,src/index.js是 JavaScript 入口,这两个文件必须存在且保持确切文件名;其余文件可以删除或重命名。典型结构如下:
my-app/ README.md node_modules/ package.json public/ index.html favicon.ico src/ App.css App.js App.test.js index.css index.js logo.svg对照 react-app 的 src 目录:src/index.js通过ReactDOM.render(<App />, document.getElementById('root'))挂载应用,src/App.jsx定义路由与页面骨架,public/index.html则提供挂载节点。文档特别强调:
- Webpack 只处理
src内的文件,所有 JS 与 CSS 必须放在src下,否则 Webpack 无法感知;把文件放进src子目录能获得更快的增量重建。 - 只有
public内的文件可被public/index.html直接引用;src之外的顶层目录(如文档目录)不会进入生产构建。
这一约束正是 guess-parser 能够稳定解析路由的前提:路由代码全部集中于src之下,解析器只需遍历src目录即可捕获完整的<Route>声明(见下文“路由解析的源码印证”)。
核心可用脚本
在 react-app 目录下可运行四个脚本(见 package.json):
| 脚本 | 作用 |
|---|---|
npm start | 启动开发模式,默认打开http://localhost:3000,代码改动自动刷新页面,lint 错误同时显示在终端与控制台 |
npm test | 启动 Jest 交互式监听测试模式 |
npm run build | 生产构建,输出到build目录;React 以生产模式打包、代码被压缩且文件名带内容哈希,可直接部署 |
npm run eject | 一次性操作,将 Webpack、Babel、ESLint 等配置与传递依赖复制进工程,从此自行维护全部构建配置(单向、不可回退) |
其中eject是被文档反复提及的“最后手段”:CRA 的精选特性集足以支撑中小型部署,若只是调整少数选项,更推荐使用环境变量或直接 forkreact-scripts(见“替代 Eject 的方案”一节),而不是把整个构建体系摊开到自己手里维护。
支持的浏览器、语言特性与 Polyfill
该模板默认使用最新版 React,并支持“最新 JavaScript 标准的超集”:除 ES6 语法外,还支持指数运算符(ES2016)、async/await(ES2017)、对象 rest/spread 属性(stage 3)、动态import()(stage 3)、类字段与静态属性(stage 3),以及 JSX 和 Flow 语法。
文档特别强调内置 polyfill 只有三个:
Object.assign()—— 由object-assign提供;Promise—— 由promise提供(完整实现 Promises/A+);fetch()—— 由whatwg-fetch提供。
这意味着若使用Array.from()、Symbol等需要运行时支持的 ES6+ 特性,必须自行引入 polyfill 或确认目标浏览器已支持;for...of等语法经 Babel 编译后也可能依赖 ES6 运行时,必要时可用 Babel REPL 确认编译产物。
目录与编辑器开发体验
语法高亮与 Lint
- 语法高亮:按 Babel 官方编辑器配置文档为对应编辑器(Sublime Text、Atom、VS Code 等)启用 JSX/Babel 高亮。
- 编辑器内 Lint(
react-scripts@0.2.0+,npm 3+):在工程根目录新建.eslintrc:{ "extends": "react-app" }即可让编辑器插件报告 lint 警告。注意
.eslintrc的改动只影响编辑器集成,不影响终端与浏览器内的 lint 输出——CRA 有意只提供一套最小规则集。若想统一代码风格,文档建议使用 Prettier 而非扩展 ESLint 风格规则。
编辑器内调试(VS Code / WebStorm)
VS Code 需要安装 Chrome Debugger 扩展,并在.vscode/launch.json中加入:
{ "version": "0.2.0", "configurations": [{ "name": "Chrome", "type": "chrome", "request": "launch", "url": "http://localhost:3000", "webRoot": "${workspaceRoot}/src", "sourceMapPathOverrides": { "webpack:///src/*": "${webRoot}/*" } }] }若通过HOST/PORT环境变量调整过服务地址,需同步修改url。随后npm start启动应用,按F5即可在编辑器内断点调试。WebStorm 则在Run -> Edit Configurations...中新增JavaScript Debug,URL 填http://localhost:3000,macOS 按^D、Windows/Linux 按F9启动调试。
Prettier 自动格式化
安装husky lint-staged prettier(或yarn add三件套),在package.json的scripts中新增"precommit": "lint-staged",并添加lint-staged字段:
"lint-staged": { "src/**/*.{js,jsx,json,css}": [ "prettier --single-quote --write", "git add" ] }此后每次提交前 Prettier 会自动格式化暂存文件;首次全量格式化可运行./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx,json,css}"。
日常开发操作清单
安装依赖与导入组件
安装任意依赖(如 React Router)使用:
npm install --save react-router # 或 yarn add react-router项目经 Babel 支持 ES6 模块。文档建议:模块只导出单个对象(如一个组件)时用export default配import Button from './Button';工具模块导出多个函数时用命名导出,导入时记得加花括号。
代码分割(Code Splitting)
CRA 通过动态import()支持代码分割——import()返回 Promise,解析为模块的命名空间对象。例如App.js中点击按钮后再加载moduleA.js:
import React, { Component } from 'react'; class App extends Component { handleClick = () => { import('./moduleA') .then(({ moduleA }) => { // Use moduleA }) .catch(err => { // Handle failure }); }; render() { return ( <div> <button onClick={this.handleClick}>Load</button> </div> ); } } export default App;moduleA.js及其独有依赖会被打成独立 chunk,用户点击后才加载;同样支持async/await写法。
这正是 react-app fixture 的核心用途:它的路由组件大量使用动态 import。在 src/App.jsx 中,/intro与/main路由分别通过AsyncComponent(() => import('./intro/Intro'))和AsyncComponent(() => import('./main/Main'))懒加载;src/main/Main.jsx 中/main/parent也走AsyncComponent(() => import('./parent/Parent')),而/main/kid则直接引入静态组件Kid。AsyncComponent由 src/LazyRoute.jsx 基于react-loadable封装,负责渲染加载中/出错/完成三态。这种“静态路由 + 懒加载组件”的混合形态,正是 guess-parser 用来验证“既能识别路由路径、又能标记哪些路由是懒加载”的典型样本。
样式与静态资源
- 样式:在 JS 中
import './Button.css'声明依赖,Webpack 在开发模式下热更新样式,生产构建时将所有 CSS 合并压缩为单个文件。 - CSS 后处理:Autoprefixer 自动压缩并添加厂商前缀(如
display: flex会生成-webkit-box/-ms-flexbox前缀版本)。 - CSS 预处理器:以 Sass 为例,安装
node-sass-chokidar后新增脚本:"build-css": "node-sass-chokidar src/ -o src/", "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive"借助
--include-path可免相对路径导入(@import 'styles/_colors.scss'、@import 'nprogress/nprogress')。用npm-run-all -p watch-css start-js可并行跑 Sass 监听与 dev server。文档选用node-sass-chokidar是因为node-sass --watch在虚拟机/Docker 下有性能问题、存在无限编译与文件监听失效的缺陷。 - 图片/字体/文件:在 JS 中
import logo from './logo.png',导入结果即最终 URL(小于 10,000 字节的 bmp/gif/jpg/jpeg/png 会内联为 data URI,SVG 除外);CSS 中url(./logo.png)同样会被 Webpack 解析。生产文件名由内容哈希生成,内容变化会自动换名,无需担心缓存。
public文件夹的使用
public下的文件不会被 Webpack 处理,而是原样复制进build。引用它们需使用%PUBLIC_URL%(HTML 中)或process.env.PUBLIC_URL(JS 中):
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;其代价是:文件不做后处理/压缩、缺失文件不会在编译期报错(用户会得到 404)、文件名不含哈希(变更需手动改名或加查询参数)。文档建议仅在少数场景使用:需要特定文件名(如manifest.webmanifest)、海量图片需动态引用路径、引入无法被 Webpack 打包的小脚本或第三方库<script>。若在 HTML 中声明全局变量,JS 侧应显式const $ = window.$;读取,避免 lint 报错。
环境变量
环境变量在构建时注入(CRA 产出静态包,无法在运行时读取)。除内置的NODE_ENV外,自定义变量必须以REACT_APP_开头,否则会被忽略(防止意外暴露机器上同名私钥)。npm start/npm test/npm run build下NODE_ENV分别恒为development/test/production,且不可手动覆盖。
代码中通过process.env.REACT_APP_SECRET_CODE读取,HTML 中通过%REACT_APP_WEBSITE_NAME%占位。临时变量按平台设置:
:: Windows cmd.exe set "REACT_APP_SECRET_CODE=abcdef" && npm start# Windows Powershell ($env:REACT_APP_SECRET_CODE = "abcdef") -and (npm start)# Linux/macOS Bash REACT_APP_SECRET_CODE=abcdef npm start永久变量写入工程根目录的.env(建议纳入版本控制,排除.env*.local)。.env文件族按优先级加载:npm start为.env.development.local、.env.development、.env.local、.env;npm run build为.env.production.local、.env.production、.env.local、.env;npm test为.env.test.local、.env.test、.env(注意无.env.local)。.env内还支持变量展开(REACT_APP_VERSION=$npm_package_version)。
开发期代理 API
前后端同源部署时,可在package.json加"proxy": "http://localhost:4000",开发服务器会把不带text/html的未知请求转发到该地址,绕开 CORS。若默认代理不够灵活,可用对象形式并支持正则匹配路径:
"proxy": { "/api": { "target": "<url>", "ws": true }, "/foo": { "target": "<url_2>", "ssl": true, "pathRewrite": { "^/foo": "/foo/beta" } }, "/bar/[^/]*[.]html": { "target": "<url_3>" } }对象形式的代理对所有匹配请求(含text/html)一律生效。WebSocket 代理需"ws": true,且 Socket.io 只能代理到 Socket.io 服务器。启用代理后若遇 “Invalid Host header”(DNS 重绑定防护),可设置HOST或(不推荐地)在.env.development.local中写DANGEROUSLY_DISABLE_HOST_CHECK=true。
开发期 HTTPS
设置HTTPS=true后npm start(set HTTPS=true&&npm start或HTTPS=true npm start),服务器会使用自签名证书,浏览器会提示警告;此特性常用于配合代理访问 HTTPS 的 API 服务器。
服务端注入与预渲染
- 动态 meta 标签:CRA 不支持服务端渲染,可在
index.html中留占位符(如<meta property="og:title" content="__OG_TITLE__">),服务器读取 HTML 后按当前 URL 替换(务必转义、消毒插入值)。 - 预渲染静态 HTML:用
react-snapshot或react-snap为每个路由生成 HTML 页面,JS bundle 加载后自动“水合”(hydrate)。收益是核心内容随 HTML 到达,且更易被搜索引擎收录。 - 服务端注入数据:在 HTML 中留
window.SERVER_DATA = __SERVER_DATA__;,服务器发送响应前替换为真实 JSON;务必先消毒 JSON 再下发,否则存在 XSS 风险。
测试体系:Jest + jsdom
CRA 使用 Jest 作为测试运行器,测试在 Node 环境(配合 jsdom 模拟浏览器全局)运行,速度快、不易抖动,定位为逻辑与组件单测,而非 DOM 行为测试;端到端测试建议另用专用工具。
命名约定与交互式 CLI
Jest 会识别三种命名约定:__tests__目录下的.js文件、*.test.js、*.spec.js,可位于src下任意深度。文档建议把测试文件与被测代码放同一目录,缩短相对导入路径。npm test进入 watch 模式,保存文件即重跑,交互式 CLI 支持按搜索模式聚焦测试;watch 模式下默认只跑自上次提交以来变更文件相关的测试,按a可强制全量运行(CI 或非 git 仓库中则默认全量)。
编写测试
用内置expect()断言、jest.fn()做间谍。组件“冒烟测试”示例:
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; it('renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render(<App />, div); });隔离渲染可用 Enzyme:安装enzyme enzyme-adapter-react-16 react-test-renderer,并在src/setupTests.js中configure({ adapter: new Adapter() }),随后可用shallow(<App />)(浅渲染,不深入子组件)与mount()(完整渲染,适合测状态与生命周期)。src/setupTests.js还会在每轮测试前自动执行,可在此注入全局 mock(如localStorage)。xit()排除测试、fit()聚焦测试;npm test -- --coverage产出覆盖率报告,package.json的jest字段可覆盖collectCoverageFrom、coverageReporters、coverageThreshold、snapshotSerializers。
CI 与调试
设置CI=true后:npm test只跑一轮并退出(不再进入 watcher),npm run build将 lint 警告视为构建失败。Travis CI 的.travis.yml示例:
language: node_js node_js: - 6 cache: directories: - node_modules script: - npm run build - npm test若测试不依赖 jsdom,可去掉test脚本的--env=jsdom以提速;window/document、ReactDOM.render()、mount()需要 jsdom,而TestUtils.createRenderer()、shallow()与快照测试不需要。Chrome 调试测试需 Node 8+,添加"test:debug": "react-scripts --inspect-brk test --runInBand --env=jsdom"脚本后打开about:inspect附加调试器;VS Code 则可用launch.json中type: node+runtimeExecutable: react-scripts的配置直接调试。
生产构建与部署
npm run build产出build目录:静态服务器把index.html交给访问者、把/static/js/main.<hash>.js等路径映射到对应文件。最简方案是用serve:
npm install -g serve serve -s build默认端口 5000(可用-p/--port调整,serve -h查看全部选项);也可集成进 Express:
const express = require('express'); const path = require('path'); const app = express(); app.use(express.static(path.join(__dirname, 'build'))); app.get('/', function (req, res) { res.sendFile(path.join(__dirname, 'build', 'index.html')); }); app.listen(9000);客户端路由(pushState)的服务器配置
使用 React RouterbrowserHistory这类 HTML5pushState路由时,静态服务器对/todos/42这类前端路由会 404,必须把未知路径全部回退到index.html。Express 改为app.get('/*', ...);Apache 在public放.htaccess:
Options -MultiViews RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.html [QSA,L]生产构建启用 service worker 后,浏览器也会自动以缓存的index.html兜底导航请求(可通过 eject 后调整navigateFallback、navigateFallbackWhitelist配置),并建议把public/manifest.json的start_url改为"."以配合客户端路由。
相对路径构建
默认构建假定应用托管在服务器根路径。指定"homepage": "http://mywebsite.com/relativepath"可让 CRA 推断正确的资源根路径;react-router@^4用户还可给<BrowserRouter basename="/calendar">设置 basename。若不用 pushState 客户端路由,可设"homepage": ".",使所有资源路径相对index.html,同一构建即可迁移到任意子路径。
各平台部署速览
- Firebase:
firebase init选 Hosting,public 目录填build、勾选单页应用;务必在firebase.json的hosting.headers中给/service-worker.js配Cache-Control: no-cache,否则首轮部署后难以看到更新;随后firebase deploy。 - GitHub Pages:
package.json加"homepage": "https://myusername.github.io/my-app",安装gh-pages并添加"predeploy": "npm run build"、"deploy": "gh-pages -d build",仓库设置中把 Pages 源切换为gh-pages分支。用户页需把部署推送到 master(gh-pages -b master -d build)并让源分支不用 master。GitHub Pages 不支持 pushState 路由,可改用 hashHistory 或借助404.html重定向技巧。 - Heroku:使用 Heroku Buildpack for Create React App;若构建失败,注意 Linux 文件系统大小写敏感导致的 import 路径问题,以及被
.gitignore排除的必需文件。 - Netlify:手动部署
netlify deploy选build;持续交付把构建命令设为yarn build、发布目录设为build;pushState 支持需在public/_redirects写/* /index.html 200。 - Now(Zeit):
npm run build后进入build目录执行now --name your-project-name。 - S3/CloudFront、Surge、Azure:均有对应接入方式;Surge 支持 pushState 时可将
index.html改名为200.html作为兜底。
高级配置环境变量
下表(来自原文档的 Advanced Configuration 一节)列出关键环境变量及其生效范围:
| 变量 | 开发 | 生产 | 说明 |
|---|---|---|---|
BROWSER | ✓ | ✗ | 默认打开系统浏览器(macOS 偏好 Chrome);设为none禁用,或指定一个.js脚本自定义启动方式 |
HOST | ✓ | ✗ | 默认绑定localhost,可指定其他主机 |
PORT | ✓ | ✗ | 默认尝试 3000 端口(占用时提示下一个可用端口) |
HTTPS | ✓ | ✗ | 设为true以 HTTPS 模式运行开发服务器 |
PUBLIC_URL | ✗ | ✓ | 强制资源按给定 URL(含主机名)原样引用,适合 CDN 托管 |
CI | ◐ | ✓ | 构建时把警告视为失败;测试运行器退出监听模式(多数 CI 默认已设) |
REACT_EDITOR | ✓ | ✗ | 覆盖崩溃 overlay 点击堆栈时自动检测的编辑器;none禁用 |
CHOKIDAR_USEPOLLING | ✓ | ✗ | 设为true启用轮询监听(虚拟机内必要) |
GENERATE_SOURCEMAP | ✗ | ✓ | 设为false关闭生产 source map,缓解小内存机器 OOM |
NODE_PATH | ✓ | ✓ | 同 Node.jsNODE_PATH,但仅允许相对目录,可用于模拟 monorepo(如NODE_PATH=src) |
常见故障排查
npm start不感知变更:从 Dropbox 目录移出工程;编辑器“safe write”特性需关闭;项目路径含括号时移动(Webpack watcher 缺陷);Linux/macOS 上调大 watcher 限额;虚拟机内加CHOKIDAR_USEPOLLING=true。npm test在 macOS Sierra 挂起:多为 Watchman 问题,重装 Watchman 4.7.0+(brew reinstall watchman),或直接卸载。npm run build提前退出:内存不足(含云环境无 swap),建议加 swap 或本地构建。- Moment.js 区域缺失:默认仅含英文 locale,需显式
import 'moment/locale/fr';后moment.locale('fr')切换。 npm run build压缩失败:部分第三方包未预编译为 ES5,可在依赖仓库提 issue、fork 修复,或把小型依赖直接拷入src/当作应用代码处理。
与 guess-parser 路由解析的源码印证
react-app fixture 的价值远超“一个普通 CRA 示例”——它是 guess-parser 验证React JSX 路由解析的测试样例。react-jsx.spec.ts 直接调用parseReactJSXRoutes('packages/guess-parser/test/fixtures/react-app/src'),断言解析结果恰为五个路由:/、/intro、/main、/main/kid、/main/parent,且非懒加载路由的路径为/main/kid。
从源码结构看,这一结果与上文读到的组件声明一一对应:
- src/App.jsx 声明了
Redirect from="/" to="/intro"、/intro、/main三个顶层路由; - src/main/Main.jsx 声明了子路由
/main/kid与/main/parent; Route的component属性要么是静态组件(如Kid),要么是AsyncComponent(() => import(...))动态加载(如/intro、/main、/main/parent),解析器据此标记lazy属性。
而 src/react/react-jsx.ts 的实现揭示了底层机制:parseRoutes读取 fixture 的src下全部文件后,以JsxEmit.React+allowJs: true的 TypeScript 编译选项解析 JSX 文件。也就是说,react-app 的 README 所描述的“动态import()代码分割”与“React Router 路由声明”,正是 guess-parser 静态扫描 JSX 代码、重建站点路由图的输入样本;该 fixture 的目录组织(路由集中在src)也与 README 中“Webpack 只处理src内文件”的约定互为表里。
替代 Eject 的方案
eject 后需长期自行维护配置,成本高。文档建议:与其 eject,不如forkreact-scripts及所需依赖包按需修改,这样多个项目可共享同一份定制脚本。若只是调整个别构建/环境选项,优先使用上文“高级配置”中的环境变量,而非把整个构建管线收入囊中。
结语
这份 CRA 模板 README 既是 react-app fixture 的开发运维手册,也是理解 guess-parser React 路由解析能力的工程背景。从目录约定、四件套脚本、环境变量与代理,到 Jest 测试、预渲染与多平台部署,它覆盖了一个 CRA 工程从脚手架到上线的完整生命周期;而在 guess 仓库中,它更承担着“路由解析测试样本”的角色——README 描述的代码分割模式与路由声明方式,正是 guess-parser 解析器逐文件扫描src、产出路由图与懒加载标记的真实数据来源。
- 前端
- 开发工具
【免费下载链接】guess
🔮 Libraries & tools for enabling Machine Learning driven user-experiences on the web
相关推荐
PhoneInfoga 电话号码核验使用教程:一条命令跨 5 个数据源交叉核对号码,还能本地开 Web 界面
PhoneInfoga 电话号码核验使用教程:一条命令跨 5 个数据源交叉核对号码,还能本地开 Web 界面 PhoneInfoga 是一个开源的电话号码信息收
网络安全后端awesome-compose 实战:React 前端(Create React App)开发、测试与构建全指南
awesome compose 实战:React 前端(Create React App)开发、测试与构建全指南 导读 本指南以 awesome compose
示例工程Create React App 项目集成 React Styleguidist:组件开发与实时样式指南的完整实操
Create React App 项目集成 React Styleguidist:组件开发与实时样式指南的完整实操 本篇指南以 React Styleguidi
开发工具前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考