
create-react-app 的 public 文件夹深度解析PUBLIC_URL 静态资源逃生通道的用法与实现机制【免费下载链接】create-react-appSet up a modern web app by running one command.项目地址: https://gitcode.com/gh_mirrors/cr/create-react-apppublic文件夹是 Create React AppCRA项目中最容易“用错”的目录它既是唯一可以直接修改的index.html的所在地也是绕过 webpack 模块系统投递静态资源的“逃生通道escape hatch”。本篇以官方文档 Using the Public Folder 为主体逐条还原其中的规则与示例并结合 react-scripts 源码讲清楚%PUBLIC_URL%与process.env.PUBLIC_URL在构建时究竟是如何被计算和替换的以及开发服务器与生产构建分别如何处理public目录下的文件。读完本文你将能够正确决定哪些文件该放public、哪些文件该改用import并理解 CRA 在任意部署路径非根 URL、客户端路由下资源地址依然正确的底层原因。public 文件夹的角色可改的 HTML 与不经过 webpack 的静态文件官方文档using-the-public-folder.md指出该功能自react-scripts0.5.0起可用。public文件夹承担两类职责存放可自定义的 HTML 文件。你可以直接编辑public/index.html例如设置页面标题和 meta 标签参见 title-and-meta-tags.md。文档强调编译产物对应的script标签会在构建过程中自动注入到 HTML 中你不需要也不应该手动写。存放不走模块系统的其他静态资源。把文件放进public它不会被 webpack 处理而是原样复制进build文件夹。要引用这些资源必须使用PUBLIC_URL这个环境变量。从源码可以印证“原样复制”这一行为生产构建脚本在 build.js 中直接调用fs.copySync(paths.appPublic, paths.appBuild, {...})把public整体拷入build而public/index.html本身则作为 webpack 的模板参与构建见 webpack.config.js 中template: paths.appHtml的配置其中appHtml在 paths.js 中被解析为resolveApp(public/index.html)。也就是说public目录在 CRA 中是“双通道”的index.html走 HtmlWebpackPlugin 模板通道因此会被注入 script 标签、被变量插值、在 production 下被压缩而目录里的其他文件走文件系统直接拷贝通道因此不会被处理、不会压缩、文件名不带内容哈希。为什么不推荐把资源放 publicimport 通道的三大好处文档明确指出通常情况下推荐在 JavaScript 中import资源参见 添加样式表 与 添加图片和字体因为模块系统提供以下好处脚本和样式表会被压缩、打包到一起避免额外的网络请求文件缺失会在编译期报错而不是让用户遭遇 404产物文件名包含内容哈希content hash无需担心浏览器缓存旧版本。而放进public的资源则恰好是这三点的反面文档列出了使用这种逃生通道必须接受的代价public里的文件不会被后处理或压缩文件缺失在编译期不会被发现用户访问时会直接 404产物文件名不包含内容哈希每次文件变更后你需要自行加查询参数或改文件名来破除缓存。因此public的定位是 workaround而不是默认路径。在 index.html 中使用 %PUBLIC_URL%在index.html中public下的资源通过%PUBLIC_URL%前缀引用官方示例link relicon href%PUBLIC_URL%/favicon.ico /有两条硬性规则需要注意只有public文件夹内的文件才能通过%PUBLIC_URL%前缀访问。如果你想引用src或node_modules里的文件必须先把它复制到public——文档把这视为一种“显式声明该文件属于构建产物”的意图表达运行npm run build时CRA 会把%PUBLIC_URL%替换为正确的绝对路径这样即使项目使用了客户端路由client-side routing或部署在非根 URL 下资源引用依然有效。%PUBLIC_URL%的替换发生在构建期由 InterpolateHtmlPlugin 完成。这个插件挂在 HtmlWebpackPlugin 的afterTemplateExecution钩子上对 HTML 做全局正则字符串替换data.html data.html.replace( new RegExp(% escapeStringRegexp(key) %, g), value );它在 webpack.config.js 中以new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw)的形式注册。也就是说凡是env.raw中的键NODE_ENV、PUBLIC_URL、WDS_SOCKET_*、FAST_REFRESH以及所有REACT_APP_*变量都可以通过%键名%的形式写进index.html%PUBLIC_URL%只是其中最常用的一个。在 JavaScript 中使用 process.env.PUBLIC_URL在 JS 代码里等价能力是process.env.PUBLIC_URL官方示例render() { // Note: this is an escape hatch and should be used sparingly! // Normally we recommend using import for getting asset URLs // as described in “Adding Images and Fonts” above this section. return img src{process.env.PUBLIC_URL /img/logo.png} /; }文档特意标注这应当“sparingly”节制地使用。其注入机制在 env.js 的getClientEnvironment(publicUrl)函数中PUBLIC_URL: publicUrl被放入环境对象随后连同NODE_ENV和所有REACT_APP_*变量一起被JSON.stringify最终通过 webpack 的DefinePluginnew webpack.DefinePlugin(env.stringified)在编译期做文本替换把process.env.PUBLIC_URL换成具体的字符串字面量。因此它是构建时注入而非运行时读取——修改部署路径后必须重新构建才能生效。PUBLIC_URL 的取值来源从源码看计算优先级%PUBLIC_URL%与process.env.PUBLIC_URL拿到的是同一个值其计算逻辑集中在 paths.jsconst publicUrlOrPath getPublicUrlOrPath( process.env.NODE_ENV development, require(resolveApp(package.json)).homepage, process.env.PUBLIC_URL );核心解析函数是 getPublicUrlOrPath.js取值优先级为环境变量PUBLIC_URL.env文件或 shell 中设置若以.开头相对路径如.development 下会规范为/production 下则按原样保留以启用相对资源路径服务于不使用 pushState 客户端路由的应用若是带域名的完整 URLdevelopment 下取pathnameproduction 下原样使用package.json的homepage字段规则类似取 URL 的pathname部分默认值/前两者都未设置时public URL 就是根路径。另外注意 webpack.config.js 中有一处细节传给getClientEnvironment的值是paths.publicUrlOrPath.slice(0, -1)即刻意去掉了末尾斜杠源码注释解释%PUBLIC_URL%/xyz比%PUBLIC_URL%xyz更好看所以在 HTML 里书写时仍需手动补上/。开发服务器的行为与生产构建一致地“以 public 为静态根”webpackDevServer.config.js 配置了static: { directory: paths.appPublic }源码注释也明确写道——“在index.html中你可以用%PUBLIC_URL%获取public文件夹的 URL在 JavaScript 代码中你可以用process.env.PUBLIC_URL访问它”。因此开发期与构建期的行为是统一的资源引用无需区分环境。什么时候该用 public 文件夹文档给出的推荐立场是样式表、图片、字体仍应通过 JavaScriptimport引入public文件夹适用于以下较少见的情形你需要在构建产物中得到指定文件名的文件例如 PWA 的manifest.webmanifest你有成千上万张图片需要动态拼接路径来引用你想在打包代码之外引入一个类似pace.js的小型独立脚本某些库与 webpack 不兼容你只能以script标签方式引入。文档同时提醒如果你在index.html中加入了声明全局变量的script需要继续了解如何安全地引用这些变量参见 Using Global Variables。小结一张决策速查表场景正确做法修改页面标题、meta 标签编辑public/index.html参见 title-and-meta-tags.md引入样式表、图片、字体在 JS 中import让 webpack 处理参见 adding-a-stylesheet.md、adding-images-fonts-and-files.md需要构建产物中的固定文件名如 manifest放入public用%PUBLIC_URL%/process.env.PUBLIC_URL引用需要动态路径引用大量图片、引入 webpack 不兼容的脚本放入public调整部署根路径设置PUBLIC_URL环境变量或homepage字段重新构建一句话总结public是“HTML 可编辑 资源直通构建目录”的逃生通道%PUBLIC_URL%由 InterpolateHtmlPlugin 在构建期替换、process.env.PUBLIC_URL由 DefinePlugin 注入两者同源于getPublicUrlOrPath对PUBLIC_URL环境变量、homepage字段和默认值/的优先级解析——理解这条链路就能在任意部署路径下正确地组织 CRA 的静态资源。【免费下载链接】create-react-appSet up a modern web app by running one command.项目地址: https://gitcode.com/gh_mirrors/cr/create-react-app创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考