Featured image of post History和Hash路由有什么区别

History和Hash路由有什么区别

如何在Nginx中挂载History路由模式的Vue应用

相关阅读:

《HTML5 History 模式》

《url 中#(hash)的含义》

《vue:路由实现原理》

《Vue 项目打包部署总结》

Vue 中的路由实现原理

默认情况下,Vue 构建出的都是一个单页应用,即所谓的SPA,此类应用只有一个.html文件,通过 js 操作路由,实现一个网页,多种视图的效果。Vue 为我们提供了方便快速的构建页面路由的工具——VueRouter。

我们在配置路由的时候,会碰到两种路由模式,一种是Hash模式,一种是History模式,两者最直观的区别就是在Hash模式中,URL 会有一个"#“号在路由前面(比如助手的 app 页面就会有一个”#“号),而History模式则没有。

这是因为Hash模式采用 URL 的 hash(不是哈希表的那个 hash)来模拟一个完整的 URL。

我第一学期在完成助手前端部的仿制页面作业时,是这样写 a 标签的:

1
<a href="" class="menu">HTML/CSS</a>

当时学长跟我说,href标签不要留空,可以写一个”#“号代替。这是一个好习惯,虽然留空也没有报错,但事实上,留空时点击该标签时会刷新页面,而写了”#“后则只会跳转到页面顶部,但无论何时,都尽量不要让某个attribute值为空

"#"实际上是一个锚点,代表网页中的一个位置,其右面的字符,就是该位置的标识符。例如href="#app"将滚动到当前页面中id="app"的元素。正常情况下,当浏览器地址栏地址改变,浏览器会重新加载页面,而如果只是 hash 部分("#“后面)修改的话,则不会重新加载,这就是单页前端路由的原理,即允许根据不同的路由页面局部更新而不刷新整个页面。对于服务器来说,"#“后面的内容是会被自动忽略的,即 hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,所以对后端完全没有影响。

H5 新增了historypushState接口,同样也允许前端操作改变路由地址但是不触发页面刷新,history 模式即利用这一接口来实现。因此使用 history 模式可以去掉路由中的#号。

如何在 Nginx 中挂载 History 路由模式的 Vue 应用

除了外观和实现方式上的不同,HashHistory模式在部署时也有不同的要求

Hash模式下,仅 hash 符号之前的内容会被包含在请求中,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。

History模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如果后端缺少对 URL 的路由处理,将返回 404 错误。

当把单页应用部署到服务端后,由于我们构建的是一个单页应用,所以使用Hash路由模式的页面不会出现问题,而对于使用History模式的单页来说,在访问网页时的 URL 类似于http://xxx.com/vueapp/home这样的结构,这里vueapp是应用名称,在其之后使用/home对服务器而言相当于在vueapp目录下访问home子目录或者文件。正如之前所说的,history模式下调用pushState接口,可以实现“改变路由地址但是不触发页面刷新”,但是如果用户手动刷新后,服务器就会把当前路由当成一个子目录或者文件去寻址,那当然会报 404 错误。

为了解决这个问题,我们需要配置Nginx(或其他服务端),并在其中增加一个覆盖所有情况的候选资源,使得如果 URL 匹配不到任何静态资源,则返回同一个index.html页面,这个页面就是你 app 依赖的页面。这是为了让浏览器在任何情况下都能指向同一个网页,而不会在跳转时产生 404 错误。而相应的,这样子你的页面就不会再产生 404 错误界面了。

1
2
3
location /vueapp {
  try_files $uri $uri/ /vueapp/index.html;
}

这句配置的意思就是,拿到一个地址,先根据地址尝试找对应文件,找不到再试探地址对应的文件夹,再找不到就返回/vueapp/index.html。这样对于我们的单页应用而言,再刷新页面就不会出现 404 了

配置完重启下 Nginx 服务即可(CentOS)

1
$ service nginx restart

“给个警告,因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后再给出一个 404 页面。”

1
2
3
4
const router = new VueRouter({
  mode: "history",
  routes: [{ path: "*", component: NotFoundComponent }],
});

使用History模式的单页在部署时同样需要在项目的Vue.config.js下对publicPath进行路径的配置,将其设置为:

1
2
3
module.exports = {
  publicPath: process.env.NODE_ENV === "production" ? "/vueapp" : "/",
};

router/index.js下修改base路径:

1
2
3
4
5
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  base: "/vueapp/",
  routes,
});

这样一来在项目和服务端就都完成了配置,history模式的路由就可以正常显示内容了!

Built with Hugo
主题 StackJimmy 设计