后端路由简介
路由这个概念最先是后端出现的。在以前用模板引擎开发页面时,经常会看到这样
http://www.xxx.com/login
大致流程可以看成这样:
- 浏览器发出请求
- 服务器监听到80端口(或443)有请求过来,并解析url路径
- 根据服务器的路由配置,返回相应信息(可以是 html 字串,也可以是 json 数据,图片等)
- 浏览器根据数据包的 Content-Type 来决定如何解析数据
简单来说路由就是用来跟后端服务器进行交互的一种方式,通过不同的路径,来请求不同的资源,请求不同的页面是路由的其中一种功能。
前端路由
1. hash 模式
随着 ajax 的流行,异步数据请求交互运行在不刷新浏览器的情况下进行。而异步交互体验的更高级版本就是 SPA —— 单页应用。单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。
类似于服务端路由,前端路由实现起来其实也很简单,就是匹配不同的 url 路径,进行解析,然后动态的渲染出区域 html 内容。但是这样存在一个问题,就是 url 每次变化的时候,都会造成页面的刷新。那解决问题的思路便是在改变 url 的情况下,保证页面的不刷新。在 2014 年之前,大家是通过 hash 来实现路由,url hash 就是类似于:
http://www.xxx.com/#/login
这种 #。后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发hashchange
这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听hashchange
来实现更新页面部分内容的操作:
function matchAndUpdate () {
// todo 匹配 hash 做 dom 更新操作
}
window.addEventListener('hashchange', matchAndUpdate)
2. history 模式
14年后,因为HTML5标准发布。多了两个 API,pushState
和 replaceState
,通过这两个 API 可以改变 url 地址且不会发送请求。同时还有 popstate
事件。通过这些就能用另一种方式来实现前端路由了,但原理都是跟 hash 实现相同的。用了 HTML5 的实现,单页路由的 url 就不会多出一个#,变得更加美观。但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。
function matchAndUpdate () {
// todo 匹配路径 做 dom 更新操作
}
window.addEventListener('popstate', matchAndUpdate)
Vue router 实现
我们来看一下vue-router
是如何定义的:
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
routes: [...]
})
new Vue({
router
...
})
可以看出来vue-router
是通过 Vue.use
的方法被注入进 Vue 实例中,在使用的时候我们需要全局用到 vue-router
的router-view
和router-link
组件,以及this.$router/$route
这样的实例对象。那么是如何实现这些操作的呢?下面我会分几个章节详细的带你进入vue-router
的世界。(阅读源码是有点枯燥,但是带着问题去了解,就感觉很有意思。如果你对 vue-router 的实现机制也存在一些疑问,可以一起探讨交流)
router/routes.js 文件代码
const routes = [
{
path: '/',
redirect: '/recommend'
},
{
path: '/recommend',
component: () => import('../components/recommend/view.vue')
},
{
path: '/singer',
component: () => import('../components/singer/view.vue')
},
{
path: '/rank',
component: () => import('../components/rank/view.vue')
},
{
path: '/search',
component: () => import('../components/search/view.vue')
}
]
export default routes
Vue-router原理了解一下:
找到一篇文章,分析的很透彻 从vue-router看前端路由的两种实现,文章写的很好,看完这篇文章 ➕ 看源码应该可以理解,这里根据我浅显的理解概括一下:
vue-router通过hash与History interface两种方式实现前端路由,更新视图但不重新请求页面”是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有两种方式
-
hash
---- 利用URL中的hash(“#”) -
利用
History
interface在 HTML5中新增的方法, 详情点击
那么,我们要选择用哪种方式呢?
在vue-router中,它提供mode参数来决定采用哪一种方式,选择流程如下:
mode 参数:
-
默认hash
-
history 注:如果浏览器不支持history新特性,则采用hash方式
-
如果不在浏览器环境则使用abstract(node环境下)
router/index.js 文件代码
import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'
Vue.use(Router)
export default new Router({
// mode: 'history',
routes
})
那么,经过测试,有什么区别呢?
- mode:'hash' ,多了 “ # ”
http://localhost:8080/#/recommend
- mode:'history'
http://localhost:8080/recommend
小白:这么神奇,不同路由显示,打开确是同一个页面嘛?are you kidding me ?
大菜:哈哈,当你选择mode类型之后,程序会根据你选择的mode 类型创建不同的history对象(HashHistory或HTML5History或AbstractHistory),嘿嘿小伙子,我们看看源码就知道了
// 根据mode确定history实际的类并实例化
// 根据mode确定history实际的类并实例化
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
小白:什么HashHistory,什么HTML5History,有什么区别嘛?
大菜:当然是有滴。就例如两个不同的对象,一个擅长画画,一个精通书法。哈哈别急别急,待我慢慢道来。
HashHistory
HashHistory真是身怀绝技,会很多东西。特别是替换路由特别厉害。还可以通过不同的方式,一个是 push ,一个是 replace .
两个方法:HashHistory.push() 和 HashHistory.replace()
**HashHistory.push() **将新路由添加到浏览器访问历史的栈顶
从设置路由改变到视图更新的流程:
$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()
解析:
1 $router.push() //调用方法
2 HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash= XXX)
3 History.transitionTo() //监测更新,更新则调用History.updateRoute()
4 History.updateRoute() //更新路由
5 {app._route= route} //替换当前app路由
6 vm.render() //更新视图
HashHistory.replace()
replace()方法与push()方法不同之处在于,它并不是将新路由添加到浏览器访问历史的栈顶,而是替换掉当前的路由
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.transitionTo(location, route => {
replaceHash(route.fullPath)
onComplete && onComplete(route)
}, onAbort)
}
function replaceHash (path) {
const i = window.location.href.indexOf('#')
window.location.replace(
window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
)
}
领教了HashHistory的超能力,接下来看下
HTML5History
History interface是浏览器历史记录栈提供的接口,通过back(), forward(), go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。
从HTML5开始,History interface有进一步修炼:pushState(), replaceState() 这下不仅是读取了,还可以对浏览器历史记录栈进行修改:
window.history.pushState(stateObject, title, URL)window.history.replaceState(stateObject, title, URL)
-
stateObject: 当浏览器跳转到新的状态时,将触发popState事件,该事件将携带这个stateObject参数的副本
-
title: 所添加记录的标题
-
URL: 所添加记录的URL
1.push
与hash模式类似,只是将window.hash改为history.pushState
2.replace
与hash模式类似,只是将window.replace改为history.replaceState
3.监听地址变化
在HTML5History的构造函数中监听popState(window.onpopstate)
具体源码分析可以参考上面 说的文档
小白:这样看来,HashHistory和HTML5History两个对象都身怀绝技,都能替换当前路由,我选哪个比较好?
大菜:emmm....,一般来说,hash模式与history模式是差不多的,推荐history模式,理由竟然是:"#" 符号有点丑啊...0_0 ",但是呢,但是呢,我们又不能只是看颜值是吧。
-
pushState设置的新URL可以是与当前URL同源的任意URL;而hash只可修改#后面的部分,故只可设置与当前同文档的URL
-
pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串
-
pushState可额外设置title属性供后续使用
-
history模式则会将URL修改得就和正常请求后端的URL一样,如后端没有配置对应/user/id的路由处理,则会返回404错误
根据自己的情况选择啊!
作者:Searchen
链接:https://www.jianshu.com/p/4295aec31302
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。