Vue history模式刷新页面404问题及解决

 更新时间:2024年12月09日 16:26:08   作者:梅坞茶坊  
文章介绍了Vue单页应用中出现404错误的原因,以及如何通过配置Nginx和使用Vue Router的hash模式来解决这个问题,同时,文章还简要解释了单页应用的概念及其优点和缺点,并讨论了Router的实现方式

为什么会出现404

我们先来看一下我们给到后端的dist文件

可以看到dist下只有一个 index.html 文件及一些静态资源,这个是因为Vue是单页应用(SPA),只有一个index.html作为入口文件,其它的路由都是通过JS来进行跳转

接着我们再来分析一下后端 nginx 的配置

server {
  // 监听80端口
  listen 80;
  // 定义你的站点名称
  server_name website.com;
  // 根据请求 URI 设置配置
  location / {
      // 站点根目录,这里为 vue 构建出来的 dist 目录
      root   /www/dist;
      // 站点初始页为index.html 或 index.htm
      index  index.html index.htm;
  }
}

我们现在可以根据 nginx 配置得出,当我们在地址栏输入 website.com 时,这时会打开我们 dist 目录下的 index.html 文件,然后我们在跳转路由进入到 website.com/login

关键在这里,当我们在 website.com/login 页执行刷新操作,nginx location 是没有相关配置的,所以就会出现 404 的情况

为什么hash模式下没有问题

router hash 模式我们都知道是用符号#表示的,如 website.com/#/login, hash 的值为 #/login

它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对服务端完全没有影响,因此改变 hash 不会重新加载页面

hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 website.com/#/login 只有 website.com 会被包含在请求中 ,因此对于服务端来说,即使没有配置location,也不会返回404错误

单页应用(SPA)概念

我们前面有提到单页应用,那什么是单页应用呢?

单页应用

单页应用(single-page application),缩写SPA 是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,而非传统的从服务器重新加载整个新页面。

这种方法避免了页面之间切换打断用户体验,使应用程序更像一个桌面应用程序。在单页应用中,所有必要的代码(HTML、JavaScript和CSS)都通过单个页面的加载而检索,或者根据需要(通常是为响应用户操作)动态装载适当的资源并添加到页面。

尽管可以用位置散列或HTML5历史API来提供应用程序中单独逻辑页面的感知和导航能力,但页面在过程中的任何时间点都不会重新加载,也不会将控制转移到其他页面

大白话来讲:

一个杯子,早上装的牛奶,中午装的是开水,晚上装的是茶,我们可以发现,变的始终是内容,而容器还是那个容器

当然,每种技术都有其利弊,单页应用也是如此

利:

  1. 无刷新体验,这个应该是最显著的有点,由于路由分发直接在浏览器端完成,页面是不刷新,对用户的响应非常及时,因此提升了用户体验
  2. 完全的前端组件化,前端开发不再以页面为单位,更多地采用组件化的思想,代码结构和组织方式更加规范化,便于修改和调整

弊:

  1. 首屏较长,要在一个页面上为用户提供产品的所有功能,在这个页面加载的时候,首先要加载大量的静态资源,这个加载时间相对比较长
  2. 不利于 SEO,单页页面,数据在前端渲染,就意味着没有 SEO,或者需要使用变通的方案

Router的实现

为了让大家加深大家对 Router 的理解,这里我们实现一个最简洁的 Router

  • hash 模式

核心通过监听url中的hash来进行路由跳转

// 定义 Router
class Router {
    constructor () {
        this.routes = {}; // 存放路由path及callback
        this.currentUrl = '';
        
        // 监听路由change调用相对应的路由回调
        window.addEventListener('load', this.refresh, false);
        window.addEventListener('hashchange', this.refresh, false);
    }
    
    route(path, callback){
        this.routes[path] = callback;
    }
    
    push(path) {
        this.routes[path] && this.routes[path]()
    }
}

// 使用 router
window.miniRouter = new Router();
miniRouter.route('/', () => console.log('page1'))
miniRouter.route('/page2', () => console.log('page2'))

miniRouter.push('/') // page1
miniRouter.push('/page2') // page2
  • history 模式

history 模式核心借用 HTML5 history api,api 提供了丰富的 router 相关属性

先了解一个几个相关的api

  • history.pushState 浏览器历史纪录添加记录
  • history.replaceState 修改浏览器历史纪录中当前纪录
  • history.popState 当 history 发生变化时触发
// 定义 Router
class Router {
    constructor () {
        this.routes = {};
        this.listerPopState()
    }
    
    init(path) {
        history.replaceState({path: path}, null, path);
        this.routes[path] && this.routes[path]();
    }
    
    route(path, callback){
        this.routes[path] = callback;
    }
    
    push(path) {
        history.pushState({path: path}, null, path);
        this.routes[path] && this.routes[path]();
    }
    
    listerPopState () {
        window.addEventListener('popstate' , e => {
            const path = e.state && e.state.path;
            this.routers[path] && this.routers[path]()
        })
    }
}

// 使用 Router

window.miniRouter = new Router();
miniRouter.route('/', ()=> console.log('page1'))
miniRouter.route('/page2', ()=> console.log('page2'))

// 跳转
miniRouter.push('/page2')  // page2

解决404

看到这里我相信大部分同学都能想到怎么解决问题了,

产生问题的本质是因为我们的路由是通过JS来执行视图切换的,

当我们进入到子路由时刷新页面,web容器没有相对应的页面此时会出现404

所以我们只需要配置将任意页面都重定向到 index.html,把路由交由前端处理

还是以 nginx 为例,更多版本的大家可以前往https://router.vuejs.org/zh/guide/essentials/history-mode.html 查看

location / {
  try_files $uri $uri/ /index.html;
}

这里有一个小细节,如果出现真的 404 页面了呢?比如 website.com/notfound

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

const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '*', component: NotFoundComponent }
  ]
})

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Vue3使用MD5加密实战案例(清晰明了)

    Vue3使用MD5加密实战案例(清晰明了)

    MD5是一种信息摘要算法(对称加密),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值,用来确保信息传输完整一致性,这篇文章主要给大家介绍了关于Vue3使用MD5加密的相关资料,需要的朋友可以参考下
    2023-05-05
  • vue点击按钮实现让页面的某一个元素全屏展示

    vue点击按钮实现让页面的某一个元素全屏展示

    这篇文章主要介绍了vue点击按钮实现让页面的某一个元素全屏展示,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • vue3+ts+Vuex中使用websocket协议方式

    vue3+ts+Vuex中使用websocket协议方式

    这篇文章主要介绍了vue3+ts+Vuex中使用websocket协议方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • 尤雨溪开发vue dev server理解vite原理

    尤雨溪开发vue dev server理解vite原理

    这篇文章主要为大家介绍了尤雨溪开发的玩具vite,vue-dev-server来理解vite原理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • 浅析Vue.js 中的条件渲染指令

    浅析Vue.js 中的条件渲染指令

    这篇文章主要介绍了Vue.js 中的条件渲染指令,Vue.js 中的条件渲染指令可以根据表达式的值,来决定在 DOM 中是渲染还是销毁元素或组件。本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-11-11
  • Vue-router结合transition实现app前进后退动画切换效果的实例

    Vue-router结合transition实现app前进后退动画切换效果的实例

    下面小编就为大家带来一篇Vue-router结合transition实现app前进后退动画切换效果的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • vue3 中使用vue img cutter 图片裁剪插件的方法

    vue3 中使用vue img cutter 图片裁剪插件的方法

    这篇文章主要介绍了vue3 中使用vue img cutter 图片裁剪插件的方法,首先安装依赖,构建 components/ImgCutter.vue 组件,需要的朋友可以参考下
    2024-05-05
  • Vue路由配置方法详细介绍

    Vue路由配置方法详细介绍

    Vue3和Vue2基本差不多,只不过需要将createRouter、createWebHistory从vue-router中引入,再进行使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-09-09
  • 基于 Vue.js 之 iView UI 框架非工程化实践记录(推荐)

    基于 Vue.js 之 iView UI 框架非工程化实践记录(推荐)

    为了快速体验 MVVM 模式,我选择了非工程化方式来起步,并选择使用 Vue.js,以及基于它构建的 iView UI 框架。本文给大家分享基于 Vue.js 之 iView UI 框架非工程化实践记录,需要的朋友参考下吧
    2017-11-11
  • Vue3自定义指令语法图文详解

    Vue3自定义指令语法图文详解

    这篇文章主要给大家介绍了Vue3自定义指令的相关资料,自定义指令是一种在Vue应用程序中扩展HTML标签的能力,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-09-09

最新评论