如何在Create React App构建阶段为react-scripts生成的script标签添加async属性
当然可以!针对Create React App(基于react-scripts构建)自动注入的主JS脚本添加async属性,有几种实用的方案,不用直接eject也能搞定,下面给你详细拆解:
方案1:用react-app-rewired自定义Webpack配置(推荐,无需eject)
这个方法不用暴露react-scripts的核心配置,还能保留后续版本更新的能力:
- 首先安装依赖:
npm install --save-dev react-app-rewired customize-cra
- 在项目根目录新建
config-overrides.js文件,写入以下配置:
const { override } = require('customize-cra'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = override((config) => { // 找到webpack配置中的HtmlWebpackPlugin实例 const htmlPlugin = config.plugins.find(plugin => plugin instanceof HtmlWebpackPlugin); if (htmlPlugin) { // 开启async脚本加载模式,所有注入的脚本都会带上async属性 htmlPlugin.options.scriptLoading = 'async'; // 如果你只想给主脚本(比如main.xxx.js)加async,可以用更精细化的处理: // htmlPlugin.options.scripts = htmlPlugin.options.scripts?.map(script => { // if (script.src?.includes('main.')) { // return { ...script, attributes: { async: true } }; // } // return script; // }); } return config; });
- 修改
package.json中的scripts命令,替换成react-app-rewired:
"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-scripts eject" }
- 执行
npm run build后,生成的index.html里的主脚本就会自动带上async属性了。
方案2:已eject项目的修改方式(不推荐)
如果你已经执行过npm run eject暴露了配置,可以直接修改webpack配置:
- 打开项目根目录下的
config/webpack.config.js,找到HtmlWebpackPlugin的配置块,添加scriptLoading: 'async'选项:
new HtmlWebpackPlugin( Object.assign( {}, { inject: true, scriptLoading: 'async', // 新增这一行 template: paths.appHtml, }, isEnvProduction ? { minify: { // 原有压缩配置... }, } : undefined ) ),
- 保存后重新构建,脚本就会带上async属性。不过要注意,eject后无法再接收react-scripts的官方更新,所以除非必要,不建议用这个方法。
方案3:Post-build脚本修改生成的HTML
如果你不想碰Webpack配置,可以在构建完成后用脚本批量修改生成的index.html:
- 先安装处理HTML的工具:
npm install --save-dev cheerio
- 根目录新建
postbuild.js文件:
const fs = require('fs'); const cheerio = require('cheerio'); // 指向build后的index.html路径 const htmlPath = './build/index.html'; const htmlContent = fs.readFileSync(htmlPath, 'utf8'); const $ = cheerio.load(htmlContent); // 给所有包含"main."的脚本标签添加async属性 $('script[src*="main."]').attr('async', ''); // 把修改后的内容写回文件 fs.writeFileSync(htmlPath, $.html());
- 修改
package.json的build命令,让构建完成后自动执行这个脚本:
"scripts": { "build": "react-scripts build && node postbuild.js", // 其他脚本... }
这个方法简单直接,但只对生产构建生效,开发环境的动态注入脚本不会被修改。
注意事项
async和defer的区别:如果你的脚本有依赖顺序要求(比如第三方库先加载,React再初始化),可以考虑用defer代替async——defer会在DOM加载完成后按脚本顺序执行,而async是脚本加载完成就立即执行,可能打乱顺序。- 测试兼容性:虽然现代浏览器都支持这两个属性,但如果需要兼容旧版浏览器,要提前做好测试。
- React 18+适配:React 18的
createRoot初始化逻辑对异步加载脚本的兼容性很好,但还是要测试一下页面初始化是否正常。
内容的提问来源于stack exchange,提问作者Aviral Bajpai
相关产品推荐
相关产品推荐

