一、背景
自从运维同事强烈要求前端的环境要使用多套的,参考文章
【项目】参考若依的前端框架去多环境
于是一番捣鼓与改造之后,看似已经顺利了
但运维说,前端还是有问题,需要他帮我改下,于是改了如下内容
什么嘛,为啥要帮我加上web?然后说还剩下最后一个问题,留给我慢慢改(自行解决)。
内心崩溃,为啥不一起改了呢??于是让我们来分析到底问题出在了哪里???
二、异常截图
2.1 页面:沙箱环境的前端页面显示了白屏
2.1 F12查看 控制台输出
F12一查看,错误的内容提示如下红色的部分,提示的意思是说以下JS的资源找不到
放大一点点看
这些文件都是哪里产生的呢?404就是提示找不到文件路径的报错嘛明显是~
前端大神说这个 web 目录,webpack 不会自动帮你生成的,需要自己在服务器创建 web 目录
然后把所有的资源文件,放在 web 目录下,其实是一个道理嘛。详细是以下代码进行dist的移动工作的
运维的很明白,因为加了web目录文件,前端打包完的dist文件是放在web目录下的。
因此,需要 解决的问题是 把这些JS等资源文件都要移动到 web路径,但是应该怎么实现呢?
三、问题排查与修复
3.1 定位错误代码, 查看configureWebpack
首先怀疑的是就是这个内容,发现以下的写法,我也看不懂啊。
这一段是对输出output进行配置,对文件名进行加version之类的吧?
version是一个变量,变量值的话,在前面有定义过
const version = new Date().getTime() + '-' + process.env.VERSION
原来写这一块的前端同事反馈,这个是因为某个项目出现过缓存没有及时更新的问题,于是需要加上这个配置
但是这里明显是有问题,从输出来看,web前缀,并没有帮我加上?那问题究竟出在哪?
一轮百度以及请教了前端大神之后发现,以下的写法确实是有问题的~大家的通用做法是这样的。
参考以下配置,我们开始了调试之旅
output: {
filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].js',
publicPath: publishPath.publishPath + publishPath.prefix +'/'
},
3.1.1 尝试1:加上前缀/web (无效)
试着加上前缀
output: {
filename: `/web/js/[name].${version}.js`,
chunkFilename: `/web/js/[name].${version}.js`
},
3.1.2 尝试2: 增加process.env.VUE_APP_CONTEXT_PATH前缀
output: {
filename: `${process.env.VUE_APP_CONTEXT_PATH}js/[name].${version}.js`,
chunkFilename: `${process.env.VUE_APP_CONTEXT_PATH}js/[name].${version}.js`
}
3.1.3 尝试3:注释掉output(无效)
//output: {
// filename: `${process.env.VUE_APP_CONTEXT_PATH}js/[name].${version}.js`,
// chunkFilename: `${process.env.VUE_APP_CONTEXT_PATH}js/[name].${version}.js`
//}
3.1.4 尝试4: 删除version (无效)
output: {
filename: `/web/js/[name].js`,
chunkFilename: `/web/js/[name].js`
},
3.1.5 尝试5:增加publishPath和path (无效)
output: {
filename: `/web/js/[name].js`,
chunkFilename: `/web/js/[name].js`
publishPath: 'web/',
path: path.resolve(__dirname, "dist"), // string
},
3.1.6 经过多轮尝试与请教后,最后的正确版本是
const isDev = process.env.ENV === 'development'
output: {
filename: isDev ? `[name].js` : `[name].[chunkhash].js`,
chunkFilename: isDev ? `[name].js` :`[name].[chunkhash].js`,
},
看到这,你肯定会有以下的疑问?
-
(1)为啥要加上isDev
参考一下文章:
【异常】Cannot use [chunkhash] or [contenthash] for chunk in ‘[name].[chunkhash].js‘ -
(2)chunkhash的作用是啥?
后面的文章会讲到, 反正最后是配置了这个,这个是请教了前端大佬给的建议 -
(3)为什么去掉了publishPath?
因为vue对webpack进行重写了,publishPath提前写了。
3.2 尝试在本地进行prod脚本的构建
听前端大神的,可以先排除一下自己编译出来的文件有没有问题,那就本地先build起来啊!
本地能build,服务器也肯定能build的啊,然后看一下生成的 dist 目录里有啥先。于是现在对本地进行调试吧 ,本地build一下先,但是本地遇到了不能build 情况, 如果本地都不能 build,那么部署是不会成功的
3.2.1 configuration.output.chunkFilen iame: A relative path is expected.
本地的构建过程中,得到过这个错误提示
什么相对路径的问题?看来还不能乱配置 斜杠啊。
- configuration.output.chunkFilename: A relative path is expected. However, the provided value "/web/js/[name].1673275185922-1.0.0.js" is an absolute path!
-> The filename of non-entry chunks as relative path inside the `output.path` directory.
WebpackOptionsValidationError: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
3.2.2 npm run build:prod的编写
以下这个是不会进行构建的npm run prod
,
正确的应该是npm run build:prod
,因为里面必须要是有build
注意:要有类似于以下的输出,才算是真正的build了哦!
build后,会出现这个dist文件。
打开的index文件都带有web前缀。难怪找不到文件。。
什么情况,这个build:prod 的script竟然会写错了?
这个问题运维也没有发现,其实在Jenkins的构建日志中也是可以看到是没有输出结果的。
因为Jenkins只保留最近几次的构建,历史的已经很难看到了,无法截图输出了。
3.2.3 在开发环境的也试着加入web前缀
发现资源文件还是没有找,最后,要将dev的脚本改成如下,才能够正常访问业务。
四、 看看还有没有其他改动点?
4.1 终于不会白屏啦
终于,通过以上修改,至少不会白屏了。。。终于剩下最后一个问题了啦,这个问题好解决,之前解决过嘿嘿。
4.2 参考别的项目的配置
运维反馈,这个问题是因为环境配置里的 VUE_APP_BASE_API 没生效
说罢给了以下截图,让我参考
4.3 你确定你的Nginx配置没有问题?
于是先怀疑,Nginx的反向代理没有配置或者是配置有错?运维说, 不可能!
4.4 提示了不同的错误码了哦
于是我继续排查,试着加上了web作为前缀,发现还是不行,还是提示错误
不过!!!现在提示502了???有进展!
4.5 代码重定向的逻辑排查
从异常来看,是 直接进行了代码重定向
那是那段逻辑进行了重定向呢??于是,便利了代码后锁定以下代码
让我们详细看看,这一段代码逻辑
import { showSpin, hideSpin } from '@/utils/baseTool'
import axios from 'axios'
import store from '../store'
import { getToken, removeToken } from '../utils/cookie'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: '/api/',
// 超时
timeout: 100000,
headers: {
get: {
'Content-Type': 'application/json;charset=utf-8'
}
}
})
// request拦截器
service.interceptors.request.use(
config => {
// 登录界面
if (config.url.indexOf('/login') !== -1) {
config.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'
config.headers.Accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9'
} else if (getToken()) {
config.headers.Authorization = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
showSpin()
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
res => {
hideSpin()
const code = res.data.resultCode
if (code) {
if (code === 401) {
store.dispatch('LogOut').then(() => {
location.href = '/#/login' // 为了重新实例化vue-router对象 避免bug
})
} else if (code === 1005) { // 用户未登录
removeToken()
location.href = '/#/login'
} else if (code !== 0) {
const errorMsg = res.data.resultDesc
return Promise.reject(new Error(errorMsg))
}
} else {
return res.data
}
},
error => {
hideSpin()
return Promise.reject(error)
}
)
export default service
在创建 创建axios实例的时候,使用了baseURL: '/api/',
这是不是就是我要的呢?我希望在所有的请求前面都加上/web的前缀 啊。
4.6 代码逻辑调整
于是做了如下代码的调整
(1)定义前缀变量
const prefix = process.env.VUE_APP_CONTEXT_PATH
(2)baseURL的修改
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 100000,
headers: {
get: {
'Content-Type': 'application/json;charset=utf-8'
}
}
})
(3)使用前缀变量
if (code === 401) {
store.dispatch('LogOut').then(() => {
location.href = prefix + '#/login' // 为了重新实例化vue-router对象 避免bug
})
} else if (code === 1005) { // 用户未登录
removeToken()
location.href = prefix + '#/login'
} else if (code !== 0) {
const errorMsg = res.data.resultDesc
return Promise.reject(new Error(errorMsg))
}
5、验证
最后,终于是可以进入了,总结为啥会有这此问题吧,
(1)对npm的构建不熟悉,导致了scripts脚本都会写错。
(2)对webpack不熟悉,不懂构建webpack是如何控制构建结果的。