用过Vue单页面应用开发的,一定都知道Vue-router
这个路由组件,它支持hash
和history
两种模式。
vue-router
默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState
API 来完成 URL 跳转而无须重新加载页面。
const router = new VueRouter({
mode: 'history',
routes: [...]
})
当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id
,也好看!
不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id
就会返回 404,这就不好看了。
所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html
页面,这个页面就是你 app 依赖的页面。
今天我们的目的就是如何使用history
模式,让url地址更加简洁美观,为了更完整的演示,从头手把手演示一遍。
首先安装nodejs
,然后执行下面的npm命令创建vue3项目,跟着提示选择是或否即可完成项目的创建。本次创建的项目名称为vue-project
npm init vue@3
创建aspnetcore的webapi项目
如下图选择ASP.Net Core WebApi项目,项目名称为TestHistory
,目录选择和上面Vue项目同属一个文件夹下。
在vscode
打开前端项目,找到router配置
由于这里创建的是Vue3项目模板,模板自动配置好的vuerouter4,
其实下面这种是一样的。详情参考,
const router = new VueRouter({
mode: 'history',
routes: [...]
})
如果要使用hash
模式,则使用createWebHashHistory
函数创建。
如果我们的前端和后端用的是同一个域名,也就是部署在同一个目录下,则应该将前端编译后的Html页面使用aspnetcore
的静态资源进行托管,而不是直接放到根目录下。
后端首先要添加app.UseStaticFiles();
以支持静态资源托管,然后创建该中间件默认的静态资源文件目录wwwroot
。
将webapi项目发布到本地
vue项目执行npm run build
编译前端代码,将dist
目录下的文件拷贝到到webapi发布后的wwwroot
目录下,
IIS新建一个站点,这里使用8080
端口
别忘记安装Hosting Bundle
。
如果一切顺利,打开http://localhost:8080
你应该会看到这个页面。
点击about还会显示下面这个页面,而且地址是http://localhost:8080/about
,这不就是history
模式的效果吗!
什么都没做,效果就达到了?
别急的得意,在http://localhost:8080/about
这个地址下,刷新下网页试试。
卧槽,404了。
先解释下为什么会这样,当你访问http://localhost:8080
时由于iis默认是设置了默认文档
当找不到你请求的资源时,它会尝试检查目录下的默认文档是否存在,按先后顺序检查,发现存在index.html
所以就返回浏览器了,所以能够正常显示;当你点击about时,其实只是触发了页面的一个事件,页面有变化,url也变化了,但浏览器压根没刷新。当你手动刷新http://localhost:8080/about
时,就向后端发起这个地址的Get请求,很明显,我们没有写任何Controller
来匹配这个路由,wwwroot
目录下也不存在about/index.html
当然返回404了。
Vuerouter官方文档给出了部分后端服务器的配置方式 后端配置例子
这里只展示aspnetcore常用的服务器
location / {
try_files $uri $uri/ /index.html;
}
Internet Information Services (IIS)
- 安装 IIS UrlRewrite(opens new window)
- 在你的网站根目录中创建一个
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>
nginx的由于没有环境,没有测试,应该没问题,配置也比较简单。
iis的根据文档做一遍,UrlRewrite可以理解为是一个中间件,会对请求拦截,对符合规则的url进行路径重写,可行。
当然我要做的不是上面的任何一种,因为我们的站点可能会部署到各种各样的服务器,每次换服务器都需要不同的配置来实现,很繁琐,既然我们aspnetcore拥有强大的中间件系统,为什么不让aspnetcore来做这件事呢,不再依赖不同服务器的配置方案,实现一次编码,到处运行,在之前的文章中有介绍过如何处理404 《ASP.NETCore统一处理404错误都有哪些方式?》
那我们就在404的处理逻辑里实现其实就好了。
直接上代码
app.MapFallback(async (context) =>
{
var phpath = Path.Join(app.Environment.WebRootPath, context.Request.Path);
var name = Path.Combine(Path.GetDirectoryName(phpath)!, "index.html");
if (File.Exists(name))
{
context.Response.StatusCode = 200;
await context.Response.SendFileAsync(name);
}
});
1.当进入404处理逻辑时,首先拼接访问路径
2.检查访问的路径所属的文件夹下是否存在index.html
文件
3.当文件存在,则修改响应码,返回该文件。
4.不存在,什么也不干(这里其实可以做个友好提示页面)。
重新发布,测试,不管如何刷新,都能正常显示了。
Github上获取:https://github.com/SpringHgui/TestHistory