- 为啥我写的Vue应用在开发阶段都没问题,部署到服务端之后访问不了除了/的页面呢
- 为啥我写的SPA页面的路由用hash模式都没问题,改成history模式就问题百出呢
- 啥是前端路由啥是后端路由,要怎么配后端才能支持我的前端路由呢
1、什么是路由
在Web开发过程中,经常会遇到『路由』的概念。那么,到底什么是路由?简单来说,路由就是URL到函数的映射。
访问的URL会映射到相应的函数里(这个函数是广义的,可以是前端的函数也可以是后端的函数),然后由相应的函数来决定返回给这个URL什么东西。路由就是在做一个匹配的工作。
2、前端路由
前端路由——顾名思义,页面跳转的URL规则匹配由前端来控制。而前端路由主要是有两种显示方式:
- 带有hash的前端路由,优点是兼容性高。缺点是URL带有#号不好看
- 不带hash的前端路由,优点是URL不带#号,好看。缺点是既需要浏览器支持也需要后端服务器支持
前端路由应用最广泛的例子就是当今的SPA的web项目。不管是Vue、React还是Angular的页面工程,都离不开相应配套的router工具。前端路由带来的最明显的好处就是,地址栏URL的跳转不会白屏了——这也得益于前端渲染带来的好处。
3、前端路由与前端渲染
讲前端路由就不能不说前端渲染。我以Vue项目为例。如果你是用官方的vue-cli搭配webpack模板构建的项目,你有没有想过你的浏览器拿到的html是什么样的?是你页面长的那样有button有form的样子么?我想不是的。在生产模式下,你看看构建出来的index.html长什么样:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="xxxx.xxx.js"></script>
<script type="text/javascript" src="yyyy.yyy.js"></script>
<script type="text/javascript" src="zzzz.zzz.js"></script>
</body>
</html>
可以看到,这个其实就是你的浏览器从服务端拿到的html。这里面空荡荡的只有一个<div id="app"></div>
这个入口的div以及下面配套的一系列js
文件。所以你看到的页面其实是通过那些js渲染出来的。这也是我们常说的前端渲染。
前端渲染把渲染的任务交给了浏览器,通过客户端的算力来解决页面的构建,这个很大程度上缓解了服务端的压力。而且配合前端路由,无缝的页面切换体验自然是对用户友好的。不过带来的坏处就是对SEO不友好,毕竟搜索引擎的爬虫只能爬到上面那样的html,对浏览器的版本也会有相应的要求。
image.png
需要明确的是,只要在浏览器地址栏输入URL再回车,是一定会去后端服务器请求一次的
。而如果是在页面里通过点击按钮等操作,利用router库的api来进行的URL更新是不会去后端服务器请求的。
Hash模式
hash模式利用的是浏览器不会对#号后面的路径对服务端发起路由请求。
也就是在浏览器里输入如下这两个地址:http://localhost/#/user/1和http://localhost/其实到服务端都是去请求http://localhost这个页面的内容。
而前端的router库通过捕捉#号后面的参数、地址,来告诉前端库(比如Vue)渲染对应的页面。这样,不管是我们在浏览器的地址栏输入,或者是页面里通过router的api进行的跳转,都是一样的跳转逻辑。所以这个模式是不需要后端配置其他逻辑的,只要给前端返回http://localhost对应的html,剩下具体是哪个页面,就由前端路由去判断便可。
History模式
不带#
号的路由,也就是我们通常能见到的URL形式。router库要实现这个功能一般都是通过HTML5提供的history这个api。比如history.pushState()
可以向浏览器地址栏push一个URL,而这个URL是不会向后端发起请求的!通过这个特性,便能很方便地实现漂亮的URL。不过需要注意的是,这个api对于IE9及其以下版本浏览器是不支持的,IE10开始支持,所以对于浏览器版本是有要求的。vue-router会检测浏览器版本,当无法启用history模式的时候会自动降级为hash模式。
上面说了,你在页面里的跳转,通常是通过router的api去进行的跳转,router的api调用的通常是history.pushState()
这个api,所以跟后端没什么关系。但是一旦你从浏览器地址栏里输入一个地址,比如http://localhost/user/1
,这个URL是会向后端发起一个get请求的。后端路由表里如果没有配置相应的路由,那么自然就会返回一个404了!这也就是很多朋友在生产模式遇到404页面的原因。
那么很多人会问了,那为什么我在开发模式下没问题呢?那是因为vue-cli
在开发模式下帮你启动的那个express
开发服务器帮你做了这方面的配置。理论上在开发模式下本来也是需要配置服务端的,只不过vue-cli
都帮你配置好了,所以你就不用手动配置了。
那么该如何配置呢?其实在生产模式下配置也很简单,参考vue-router给出的配置例子。一个原则就是,在所有后端路由规则的最后,配置一个规则,如果前面其他路由规则都不匹配的情况下,就执行这个规则——把构建好的那个index.html
返回给前端。这样就解决了后端路由抛出的404的问题了,因为只要你输入了http://localhost/user/1
这地址,那么由于后端其他路由都不匹配,那么就会返回给浏览器index.html
。
浏览器拿到这个html之后,router库就开始工作,开始获取地址栏的URL信息,然后再告诉前端库(比如Vue)渲染对应的页面。到这一步就跟hash模式是类似的了。
当然,由于后端无法抛出404的页面错误,404的URL规则自然是交给前端路由来决定了。你可以自己在前端路由里决定什么URL都不匹配的404页面应该显示什么。
通俗点说,route就是你访问一个页面的地址。router就是由一群地址组成的东西。
参考https://www.cnblogs.com/songyao666/p/11470030.html#router=1
https://zhuanlan.zhihu.com/p/138480612
首先我们来学习三个单词(route,routes,router):
route:首先它是个单数,译为路由,即我们可以理解为单个路由或者某一个路由;
routes:它是个复数,表示多个的集合才能为复数;即我们可以理解为多个路由的集合,JS中表示多种不同状态的集合的形式只有数组和对象两种,事实上官方定义routes是一个数组;所以我们记住了,routes表示多个数组的集合;
router:译为路由器,上面都是路由,这个是路由器,我们可以理解为一个容器包含上述两个或者说它是一个管理者,负责管理上述两个;举个常见的场景的例子:当用户在页面上点击按钮的时候,这个时候router就会去routes中去查找route,就是说路由器会去路由集合中找对应的路由;
router.js
//引入vue
import Vue from 'vue';
//引入vue-router
import VueRouter from 'vue-router';
//第三方库需要use一下才能用
Vue.use(VueRouter)
//引用page1页面
import page1 from './page1.vue';
//引用page2页面
import page2 from './page2.vue';
//定义routes路由的集合,数组类型
const routes=[
//单个路由均为对象类型,path代表的是路径,component代表组件
{path:'/page1',component:page1},
{path:"/page2",component:page2}
]
//实例化VueRouter并将routes添加进去
const router=new VueRouter({
//ES6简写,等于routes:routes
routes
});
//抛出这个这个实例对象方便外部读取以及访问
export default router
这里我们再修改一下main.js
import Vue from 'vue'
import App from './App'
//引用router.js
import router from './router.js'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
//一定要注入到vue的实例对象上
router,
components: { App },
template: '<App/>'
})
修改App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<div>
//router-link定义页面中点击触发部分
<router-link to="/page1">Page1</router-link>
<router-link to="/page2">Page2</router-link>
</div>
//router-view定义页面中显示部分
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
网友评论