前端使用vite + vue-router@4,部署是用nginx
背景
vue-router的hash模式逃不掉url后面的#号,想要去掉#时,我们就得考虑使用histtory模式
vue-router@4创建history模式路由,大致代码如下👇
const router = createRouter({
history: createWebHistory(),
})
问题
前端通过history打包部署后会发现刷新404了,这个问题设计到hash和history的区别了
- hash模式 简单来说,它是基于浏览器的锚点实现的,#号后的url变化不会向服务器发送请求
- history模式 url的变化都会向服务器发送一个请求,如果服务器没有做处理,对应的url找不到对应的资源返回,那么就会页面404
前端打包文件中(一般情况下)只包含了一个index.html文件和其他如js、css等资源文件,所以当history模式的url变化时,在服务器上找不到与之对应的资源,就会404了。
说的有点抽象,给大家举个例子:
同一个页面的url,hash模式是http://www.abc.com/#/home,那么访问后到服务器时#号后面的内容是不会发送到服务器的,那么就是访问/根目录,/后面没有内容,就会默认访问index.html页面,打的包中正好有index.html文件,那么就一点问题也没有。但如果是history模式,此时的url为http://www.abc.com/home,那么这个url代表的意思就是(服务端没做其他操作的前提下)访问服务器根目录下的home目录下的index.html文件,但我们知道正常打的包根本就没有这个home目录,那就理所当然的404了
解决办法
vue-router官网上所说
要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的
index.html 相同的页面
在nginx做如下配置
location / {
try_files $uri $uri/ /index.html;
}
这里用到了try_files配置,简单说下它的作用:它首先匹配url有没有对应的资源(即上面的 u r i ),如果有则返回;如果没有,则继续查找 u r l 目录下(即上面的 uri),如果有则返回;如果没有,则继续查找url目录下(即上面的 uri),如果有则返回;如果没有,则继续查找url目录下(即上面的uri/)有没有默认的index.html文件,如果还没有,最后返回根目录下的index.html(即上面的/index.html)。其实在这里,我们想要的就是让它无脑返回根目录下的index.html(因为我们只有这个可访问文件)
这个时候,我们把打的包扔到nginx根目录下,并且加上如上配置,访问如http://www.abc.com/home页面就一切正常了。
至此部署到服务器的根目录就OK了~!
新的问题
如果部署在二级目录下,刷新依然会404,那么问题在哪呢?
把网页部署在/wj目录下(下文出现的所有newpage指的都是这个二级目录名称),并把前端打的包扔在了服务器上的/newpage目录下,期望访问http://www.abc.com/newpage/home能正常访问,那么按照经验,我们把nginx的配置修改成如下:
# nginx 修改后配置
location /newpage {
try_files $uri $uri/ /newpage/index.html;
}
nginx -s reload 后你会发现一个新问题
html文件其实正常返回了,但是资源文件都404了,检查资源文件的请求路径,发现根本没有请求到真实的资源地址,因为我们的资源文件都是在/newpage目录下,图中请求的是根目录下的资源文件,那当然没有了。
新的解决办法
聪明的小伙伴对vite的中的base进行修改 官网配置链接
所以我们将vite.config.js的配置修改成如下
// vite.config.js
{
base: '/newpage/'
// 其他代码省略
}
再重新打包部署,你会发现 ,资源找到了 ,所有的请求全部200 ,但是又有新的问题出现了,页面白屏,这百思不得其解。
仔细分析一下,我们访问的路径是带newpage的,但是vue-router定义的路由其实不带路径,此时聪明的你就又会想到vue-router也有一个配置 官网配置链接没错,所以我们修改对应配置如下
const router = createRouter({
history: createWebHistory('/newpage/')
})
重新打包上传,你会发现一切正常,非常完美,大功告成!🎇🎇🎇
不同环境的打包
不同环境的打包怎么办?手动改一次代码打包一次?不可能也不应该吧。
我们要能根据不同环境切换不同的base,这里采用的方式是使用 vite环境变量和模式来切换,具体方式如下:
创建 3 个.env文件,分别为.env.dev(测试环境), .env.development(开发环境), .env.production(生产环境)
# .env.dev
VITE_BASE_URL=/
# .env.development
VITE_BASE_URL=/newpage/
# .env.production
VITE_BASE_URL=/newpage/
定义了不同环境时的环境变量,那么只要在vue-router和vite config对应位置使用环境变量替换原来的固定值即可
const router = createRouter({
// 这里由原来的 '/newpage/' 固定值改为import.meta.env.VITE_BASE_URL替换
history: createWebHistory(import.meta.env.VITE_BASE_URL)
})
vite.config.js中略有不同,大致代码如下:
import { defineConfig, loadEnv } from 'vite'
export default ({ mode }) => {
// 使用loadEnv获取环境变量
const env = loadEnv(mode, process.cwd())
const BASE_URL = env.VITE_BASE_URL
return defineConfig({
base: BASE_URL, // 使用loadEnv获取VITE_BASE_URL替换原来的固定值 '/wj/'
// ...省略其他代码
})
}
修改package.json文件,自定义不同环境的打包命令,这里就不做介绍了
总结
总结一下history模式部署时需要改动的地方
- 如果是部署在根目录下,只需要改nginx配置即可,前端配置无需修改
- 如果是部署在二级目录下(如部署在/newpage目录),修改内容如下
- nginx配置
- vue-router配置
- vite.config配置
此致遍大功搞成了!!!