☰
guess 项目中的 Create React App 样板工程:react-app Fixture 开发与构建实践指南
2026/10/8 14:23:35 网站建设 项目流程
  • 前端
  • 开发工具

【免费下载链接】guess

🔮 Libraries & tools for enabling Machine Learning driven user-experiences on the web

项目地址:https://gitcode.com/gh_mirrors/gu/guess
点击查看免费下载

本篇技术指南以 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

项目地址:https://gitcode.com/gh_mirrors/gu/guess
点击查看免费下载

相关推荐

上一篇:fs-extra 的 pathExists / pathExistsSync:基于 fs.access 的路径存在性检测指南
下一篇:Humanizer 数值量级扩展方法:用 Tens、Hundreds、Thousands、Millions、Billions 写出可读的数值常量

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询