Vue项目布署后,刷新页面404的真正原因找到了
从一篇日记说起我是一个小前端,我有写日记的习惯2020年10月17天气 晴今天天气不错,心情也跟着好起来了辛辛苦苦加班两个星期终于完成了产品需求,到了要上线的时候了,嘴里也不知不觉哼起...
从一篇日记说起
我是一个小前端,我有写日记的习惯
2020年10月17 天气 晴
今天天气不错,心情也跟着好起来了
辛辛苦苦加班两个星期终于完成了产品需求,到了要上线的时候了,嘴里也不知不觉哼起了“劳资今晚不加班”的小曲
一顿操作猛如虎,终端执行 npm run build
一把梭,将构建产出的 dist 文件夹扔给了发际线愈发上移的后端同学,让其帮忙布署到服务器上
不一会,后端同学说项目布署好了,并扔回给了我一个线上链接
怀着激动的心,用颤抖的手慌忙点开,我没有放过页面上的每一个细节,毕竟都是用自己的头发换回来的,页面的上每一个按钮我都要复点一次以确保它们没有问题,几分钟后,我的脸上慢慢洋溢出来了笑容,因为我认为这是一次完美的“线上行动”
赶紧平复了一下心情,在公司项目里 @BOSS,自豪的表示项目已完美上线,BOSS 们可以验收
~ Wow! 平常高冷女神范的运营小姐姐给我发消息了,哈哈,一定是要夸我写的这个系统太好用了吧,点开
赶紧自己试了试,只要一刷新还真是这样,今天又得加班了!
完
为什么会出现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来提供应用程序中单独逻辑页面的感知和导航能力,但页面在过程中的任何时间点都不会重新加载,也不会将控制转移到其他页面
大白话来讲:
一个杯子,早上装的牛奶,中午装的是开水,晚上装的是茶,我们可以发现,变的始终是内容,而容器还是那个容器
当然,每种技术都有其利弊,单页应用也是如此
利:
无刷新体验,这个应该是最显著的有点,由于路由分发直接在浏览器端完成,页面是不刷新,对用户的响应非常及时,因此提升了用户体验
完全的前端组件化,前端开发不再以页面为单位,更多地采用组件化的思想,代码结构和组织方式更加规范化,便于修改和调整
弊:
首屏较长,要在一个页面上为用户提供产品的所有功能,在这个页面加载的时候,首先要加载大量的静态资源,这个加载时间相对比较长
不利于 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 }
]
})
更多推荐
所有评论(0)