目录
- 前文
- 记录背景
- 问题摸排:第一天
- 前端打包失败?
- 远程打包逻辑与本地打包逻辑不一致?
- 远程入口页被篡改?
- 再次排查本地webpack打包逻辑
- 问题摸排:第二天
- 确认本地与远程打包一致
- 排查远程nginx代理
- 定位问题 index.html 的所在
- 更改 index.html 中对依赖插件的引入
- 资源加载失败?
- 引入成功
- 目标:将 public 下的 js文件夹在打包的是拷贝到 static/js/ 目录下
- 下载 copy-webpack-plugin 插件
- 本地修改打包逻辑
- [copy-webpack-plugin] patterns must be an array?
- 问题解决
- 最后
前文
在写这篇文章之前,写过一篇名为【React 项目中如何使用 easyPlayer-pro.js】的文章,大家可能会有些好奇,为什么在这里会提到另外一篇文章。
他们是有很强的关联性的,这篇文章主要是记录解决问题的过程,而【React 项目中如何使用 easyPlayer-pro.js】这篇文章是最终的结果。
因为印象深刻,所以专门为此写下一篇,算是对此次相关问题的一些概括总结。
记录背景
因为一些原因,导致原本的视频播放插件不能继续支持项目需求,所以对视频播放插件进行更换,所以 easyPlayer.js
插件就承担起了本次视频播放的主角。
刚开始在开发环境中都挺顺利,但是当打包部署的时候就出现了一些问题,为解决这些问题花费了一些时间,解决过程有些波折,也是耗费很多心思,所以就打算给记录下来,算是一件小有感触的特殊事件。
问题摸排:第一天
因为有了 【React 项目中如何使用 easyPlayer-pro.js】 这篇文章,所以有些问题我就不继续在这里啰嗦,如有需要请前往查看。
前端打包失败?
因为本地运行正常,经过查看本地打包也正常,该有引入的文件也正常引入,该有的资源也有,但是为什么就不生效了,难道是远程打包失败了?
总结:正常引入,资源存在,没问题
远程打包逻辑与本地打包逻辑不一致?
本地没问题,那就远程有问题了?
如下操作:
-
查看打包资源地址,代码分支名称
-
查看远程打包命令
dir=`pwd` npm config set registry=https://registry.npmmirror.com/ rm -rf [文件夹名称] git clone -b [分支名] [仓库地址] mkdir -p output/bin/static cd [文件夹名称] npm config set unsafe-perm true npm cache clean -f rm -rf node_modules npm install npm run build:test || exit 1 rm -rf $dir/output/bin/static/* cp -rf build/* $dir/output/bin/static cd ..
因为项目的保密性,一些关键性的内容,就不展示了。
-
查看打包后的文件
总结:经查看,仓库地址,分支名称,运行命令,打包结果都没问题。
远程入口页被篡改?
查看线上入口文件,是否有我引入的文件以及一些特殊标识。
打开后简直懵逼了有没有,打包的入口页呢?资源去哪儿呢?添加的一些特殊标识呢?
难道说不是用这个入口文件,用了其他的吗,还是说打包根据环境变量对 webpack 有特殊配置?
再次排查本地webpack打包逻辑
- 检查根目录下
.env
打头的多个环境变量文件 - 检查
webpack
输出
总结:webpack 与 环境变量没问题,并且本地与远程打包命令这些都一致
其实当到这里的时候,已经不知道怎么处理了,先休息一下吧,同时也因为其他工作的原因,只能先暂时搁置。
问题摸排:第二天
晚上下班回去一路上想着问题到底出在哪里,所以第二天到了工位后继续查看问题,问题一天没处理,心里就不踏实。
确认本地与远程打包一致
利用 Git
使用命令 md5sum
将远程的 index.html
文件与 本地打包的index.html
文件做对比。看看有没有不一致。
md5Sum index.html
如下显示:
通过前面的值是否完全相等来判断两个文件是否一致。
总结:完全一致!
排查远程nginx代理
当问题走到这里的时候,我已经不怀疑我的工程代码了,我得从网络层面考虑考虑,是不是中间有第三方页面利用 nginx
做了代理跳转。
但是怎么看呢,我没有对应的权限,只能找运维的同事帮忙查看了。
当付出我心爱的零食后,总算请动了大佬,经查,代理没问题,没有指向其他的地址
。
总结:代理没问题。
定位问题 index.html 的所在
到这一步的时候,我就开始怀疑后端了,为啥要怀疑他们呢?
原因很简单,因为这个项目是前端和后端集中在一起部署的,不是单纯的前后端分开部署。
既然这样,那就拉后端同事一起看看,找了半天啥也没有找到,后端一口咬定是前端的问题,需要前端自行处理。
没办法,只能自己默默继续找问题。
一个上午就这样溜走了,吃过午饭后下午继续。
因为上午查看这个问题导致其他项目需求没有处理,下午只能是尽快处理完手中的活儿,接着看问题。
下午在写其他项目的时候,忽然想到既然线上入口有问题,那就直接到终端里面去查看入口文件长啥样。
这一看不要紧,一看就发现了问题,这个文件居然是存在的,为什么会存在呢?
经过一番排查,能够确定这个入口文件的来源了,一切问题都不再是问题。
是啥呢?
原来后端使用了一个模板引擎,在项目中配置了一个 idx.ftl
的文件,这个文件经过编译后就是测试环境线上的入口文件。
他长这样:
<#assign currentTime = "${.now?long?c}">
<!doctype html>
<html lang="en">
<head>
<meta name="thirdHomePage" content="${thirdHomePage}">
<meta name="AppPublic" content=".">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"/>
<meta charset="utf-8"/><meta name="AppPublic" content="不便展示">
<link rel="icon" href="./favicon.ico?v=1" type="image/x-icon">
<title>你的爱豆</title>
<link href="./static/css/main.css?v=${currentTime}" rel="stylesheet">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./static/js/commons.js"></script>
<script src="./static/js/main.js?v=${currentTime}"></script>
</body>
</html>
这一切的一切都说的通了,index.html 已定位。
更改 index.html 中对依赖插件的引入
针对引入文件丢失,标识丢失,只需要修改修改即可,如下:
<#assign currentTime = "${.now?long?c}">
<!doctype html>
<html lang="en">
<head>
<meta name="thirdHomePage" content="${thirdHomePage}">
<meta name="AppPublic" content=".">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"/>
<meta charset="utf-8"/><meta name="AppPublic" content="不便展示">
<link rel="icon" href="./favicon.ico?v=1" type="image/x-icon">
<title>你的爱豆</title>
<link href="./static/css/main.css?v=${currentTime}" rel="stylesheet">
</head>
<body>
<!-- 重要 -->
<script src="./js/easyplayer-pro.js"></script>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./static/js/commons.js"></script>
<script src="./static/js/main.js?v=${currentTime}"></script>
<!-- 重要 -->
<script>window.EasyPlayerPro = EasyPlayerPro</script>
</body>
</html>
代码中标示 **【重要】**的部分就是本次需要添加的内容。
到这里基本问题不大,还是需要做进一步的验证。
资源加载失败?
本以为部署后,就没问题了,谁知道,资源居然加载不出来,不知道文件为啥不能指向 index.html
同级目录下的js文件夹下,但是可以访问 ./static/ 目录下的资源。
最终没办法继续修改为跟 <script src="./static/js/commons.js"></script>
这个引入目录一致。
<#assign currentTime = "${.now?long?c}">
<!doctype html>
<html lang="en">
<head>
<meta name="thirdHomePage" content="${thirdHomePage}">
<meta name="AppPublic" content=".">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"/>
<meta charset="utf-8"/><meta name="AppPublic" content="不便展示">
<link rel="icon" href="./favicon.ico?v=1" type="image/x-icon">
<title>你的爱豆</title>
<link href="./static/css/main.css?v=${currentTime}" rel="stylesheet">
</head>
<body>
<!-- 重要:重新修改引入地址 -->
<script src="./static/js/easyPlayerPro/easyplayer-pro.js"></script>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./static/js/commons.js"></script>
<script src="./static/js/main.js?v=${currentTime}"></script>
<!-- 重要 -->
<script>window.EasyPlayerPro = EasyPlayerPro</script>
</body>
</html>
然后将对应的资源手动拷贝到 /static/js/easyPlayerPro/
目录下,再次重新打包部署。
成功了
惊不惊喜,太惊喜了。
引入成功
经过后端同事的重新部署,发现生效了,如下:
这都说明最重要的已经解决了,到此就算是前进了一大步。
可是这里虽然成功了,但是还是有问题,不能每次打包都要手动去拷贝一次资源吧,所以只能将webpack再拿出来改吧改吧。
目标:将 public 下的 js文件夹在打包的是拷贝到 static/js/ 目录下
看到这个标题,很多的朋友脑海中自动回显一个 webpack 插件: copy-webpack-plugin
,这个是专门用于拷贝文件的插件,用着也很好用,这下按照以往的思路直接上手修改就行了。
下载 copy-webpack-plugin 插件
webapck 4 地址
对于customize-cra版本为1.0.0和react版本为16.13.1的项目,copy-webpack-plugin 的版本并没有特定的要求,因为它与customize-cra和react版本不直接相关。copy-webpack-plugin是一个Webpack插件,用于复制文件和目录,它的使用并不依赖于React或customize-cra的具体版本。
截至我知识更新的时间点(2023年),你通常可以使用copy-webpack-plugin的任何版本,只要它与你使用的Webpack版本兼容。copy-webpack-plugin的最新版本应该与Webpack 4和Webpack 5都兼容,但如果你使用的是较旧的Webpack版本,你可能需要选择一个对应的copy-webpack-plugin版本。
对于Webpack 4(这可能是与react版本16.13.1一起使用的Webpack版本),copy-webpack-plugin的版本可以是5.x或更早的版本。以下是一个兼容性的例子:
npm install copy-webpack-plugin@^5.0.0 --save-dev
如果你使用的是Webpack 5,copy-webpack-plugin的版本应该是6.x或更新版本:
npm install copy-webpack-plugin@^6.0.0 --save-dev
由于你的customize-cra版本是1.0.0,这个版本是针对Webpack 4的。因此,建议安装copy-webpack-plugin的5.x版本,这样可以确保兼容性:
npm install copy-webpack-plugin@5 --save-dev
在安装了合适的copy-webpack-plugin版本之后,你就可以按照前面提供的方法在config-overrides.js中配置它了。
本地修改打包逻辑
问题得以解决,离成功又近了一步,再加上有了新思路,那心情是倍儿爽,内心有个声音告诉我:“这次一定能行”,经过一顿操作:
module.exports = {
webpack: override(
// 输出文件配置
appBuildOutput: (config) => {
// 其他配置...
config.plugins.push(
new CopyWebpackPlugin({
patterns: [
{
from: 'public/js/*.js', // 指定public目录下js文件夹中的所有.js文件
to: 'static/js/easyPlayerPro/[name].[ext]', // 指定目标路径,[name]和[ext]是文件名和扩展名的占位符
}
]
})
);
return config;
}
)
}
修改后再次执行 npm run build
,最终结果看这个篇幅也是显而易见的,居然报错:[copy-webpack-plugin] patterns must be an array
,惊不惊喜意不意外,当真是 【世事无常,大肠包小肠】。
以往的经验失效了,为啥呢?
[copy-webpack-plugin] patterns must be an array?
经过 GPT 哥的讲解:
如果你在使用
CopyWebpackPlugin
时遇到错误信息[copy-webpack-plugin] patterns must be an array
,这意味着你提供给CopyWebpackPlugin
的patterns
选项不是一个数组。这个选项是用来指定文件拷贝规则的,它必须是一个包含至少一个对象的数组。
下面是一个
"正确"
的CopyWebpackPlugin
配置示例,它指定了一个patterns
数组:
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
// ...其他配置
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: 'path/to/source/*', // 指定要复制的源文件或目录(支持glob语法)
to: 'path/to/destination/[name][ext]', // 指定目标路径,[name]和[ext]是占位符
// 可以添加更多配置选项,例如 noErrorOnMissing
noErrorOnMissing: true,
},
// 如果需要,可以在这里添加更多的pattern对象
],
}),
],
};
确保你的
patterns 属性是一个数组
,并且每个元素都是一个对象,它描述了拷贝的规则。如果你在config-overrides.js
中使用addWebpackPlugin
来添加CopyWebpackPlugin
,它应该看起来像下面这样:
const { override, addWebpackPlugin } = require('customize-cra');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = override(
addWebpackPlugin(
new CopyWebpackPlugin({
patterns: [
// 确保这里是一个数组,包含至少一个pattern对象
{
from: 'public/js/*.js',
to: 'static/js/[name][ext]',
},
],
})
)
);
如果
patterns
不是数组或者数组中的对象不符合预期格式,就会导致出现错误。检查你的配置,确保每个 pattern 对象都有正确的属性和格式。
这个看着跟我写的基本没多大区别,那就再找找度娘看看,得到的结论基本一致,所以能够想象得到是行不通的。
实在没办法,那就只能查看 copy-webpack-plugin
是如何传参的,所以去看看他的源码是怎么写的,以下是他的源码内容:
进去一眼就看到这个错误(途中画红线的位置),再接着看接收参数的地方,发现定义的类 CopyPlugin
接收两个参数:patterns 和 options,patterns是一个数组,options是一个对象。
要正确地使用这个类,需要确保传递给它的 patterns参数是一个数组
,而 options参数是一个对象
。
既然报错的根源已经找到,那就解决它,将参数给传正确。
问题解决
module.exports = {
webpack: override(
// 输出文件配置
appBuildOutput: (config) => {
// 其他配置...
config.plugins.push(
new CopyWebpackPlugin(
[
{
from: 'public/js/*.js', // 指定public目录下js文件夹中的所有.js文件
to: 'static/js/easyPlayerPro/[name].[ext]', // 指定目标路径,[name]和[ext]是文件名和扩展名的占位符
}
]
)
);
return config;
}
)
}
再次执行打包命令:npm run build,因为打包执行篇幅以及一些敏感信息,就不截全内容了,望大家理解。
到这里可以看到最后是成功打包,是否将 public 下的 js 文件成功的拷贝到 static/js/ 目录下,请看下面的截图:
到这一步,就算是前端打包工作内容部分结束了。可是就完全结束了吗,可以明确的说:还没有。还需要后端将 idx.ftl 文件中对 easyPlayer-pro.js 的引入地址再次修改一下。
经过后端同学的再次辛苦修改后,部署到测试环境,控制输入 window.EasyPlayerPro 是能够打印输出东西的,如下图所示:
最后
这次问题的排查,耗费了我两天半的时间,虽然中间不可能完全一心一意的将所有时间用来排查这个任务,但总体来说,时间从开始到解决确实花费了这么多,但好在是总算得以解决,心里又得一个石头总算是落下了。
写到最后,这篇文章写的有些繁杂且内容有些偏长,但总体来说,这算是我遇到这个问题进行排查的一些心路历程,希望对一些朋友有所帮助,该借助的力量一定要用起来。
看到这里的朋友,耽搁您一些宝贵的时间,借助您的 黄金手指 多多 点赞,搜藏,评论。