1. 背景
单页面应用的vue.js项目,设置router模式为history。对a标签写href属性进行子页面跳转。本地可以跳转但刷新了页面,在服务器上则显示为404。
2. 解决方案
首先,说明下vue-router的默认hash模式——使用URL的hash来模拟一个完整的URL,当URL改变时,页面不会重新加载。但是这种hash很丑,也不符合对URL的使用习惯。所以,需要使用另外一个叫history模式来实现URL跳转而无须重新加载页面。
const router = new VueRouter({
mode: 'history',
routes: [...]
})
本地测试都没有问题,但是发到服务器上面直接访问子面面就返回404了。按照官网的说法:
要在服务器端增加一个覆盖所有情况的候选资源:如果URL匹配不到任何静态资源,则应该返回同一个index.html页面,这个页面就是你app依赖的页面。
所以,我们需要进行后端配置如下:
(参考官网 https://router.vuejs.org/zh-cn/essentials/history-mode.html)
(1)Apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
(2)nginx
location / {
try_files $uri $uri/ /index.html;
}
(3)原生Node.js
const http = require('http')
const fs = require('fs')
const httpPort = 80
http.createServer((req, res) => {
fs.readFile('index.htm', 'utf-8', (err, content) => {
if (err) {
console.log('We cannot open 'index.htm' file.')
}
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8'
})
res.end(content)
})
}).listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort)
})
(4)IIS
[1] 安装 IIS UrlRewrite
[2] 在网站根目录中创建一个web.config文件,如下
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Handle History Mode and custom 404/500" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
(5)Caddy
rewrite {
regexp .*
to {path} /
}
3. 警告
如上进行后端配置之后,即便页面确实应该报404,服务器就不会再返回404错误页面,因为所有路径都会返回到index.html。为了避免这样的情况,需要在vue应用里面覆盖所有的路由情况,给出一个404页面。
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '*', component: NotFoundComponent }
]
})