窗外的绿色满满撞入我的眼睛,柳絮隔着纱窗热情的邀舞,可惜我不能出去。好了,这些都是题外话。
一、实现原理
- ps: “更新视图但不重新请求页面”是前端路由原理的核心之一
(参考:https://zhuanlan.zhihu.com/p/27588422)
vue-router 三种运行模式:
- hash: 使用 URL hash 值("#")来作路由。默认模式。
- history: 依赖 HTML5 History API 和服务器配置。
- abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。
- ps:在vue-router中是通过mode这一参数控制路由的实现模式的:
var router = new VueRouter({
mode: 'history', //默认是hash
routes: [
...
]
});


Hash模式
- hash(“#”)符号的本来作用是加在URL中指示网页中的位置;
- '#'符号本身以及它后面的字符称之为hash,可通过window.location.hash属性读取。它具有如下特点:
- hash虽然出现在URL中,但不会被包括在HTTP请求中。它是用来指导浏览器动作的,对服务器端完全无用,因此,改变hash不会重新加载页面;
- 可以为hash的改变添加监听事件:
window.addEventListener("hashchange", funcRef, false)
- 每一次改变hash(window.location.hash),都会在浏览器的访问历史中增加一个记录。
HashHistory.push()
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.transitionTo(location, route => {
pushHash(route.fullPath)
onComplete && onComplete(route)
}, onAbort)
}
function pushHash (path) {
window.location.hash = path
}
- 由此可见,push()方法最主要的是对window的hash进行了直接赋值:
window.location.hash = route.fullPath
-
hash的改变会自动添加到浏览器的访问历史记录中。
HashHistory.push().jpg
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
)
}
- 可以看出,它与push()的实现结构上基本相似,不同点在于它不是直接对window.location.hash进行赋值,而是调用window.location.replace方法将路由进行替换。
HTML5History模式
- window.history.pushState(stateObject, title, URL)
- window.history.replaceState(stateObject, title, URL)
- stateObject: 当浏览器跳转到新的状态时,将触发popState事件,该事件将携带这个stateObject参数的副本。
- title: 所添加记录的标题(暂时没有用处)。
-
URL: 所添加记录的URL。
这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前URL改变了,但浏览器不会立即发送请求该URL,这就为单页应用前端路由“更新视图但不重新请求页面 ”提供了基础。
history.pushState.jpg
history.replaceState().jpg
两种模式比较
- 在一般的需求场景中,hash模式与history模式是差不多的,但几乎所有的文章都推荐使用history模式,理由是:"#" 符号太丑 ";
- 当然,严谨的我们肯定不应该用颜值评价技术的好坏。根据MDN的介绍,调用history.pushState()相比于直接修改hash主要有以下优势:
- pushState设置的新URL可以是与当前URL同源的任意URL;而hash只可修改#后面的部分,故只可设置与当前同文档的URL
- pushState设置的新URL可以与当前URL一模一样,这样也会把记录添加到栈中;而hash设置的新值必须与原来不一样才会触发记录添加到栈中
- pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串
- pushState可额外设置title属性供后续使用
二、使用
1.路由文件的定义
/*
* 路由器模块
* */
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
import Home from '../views/home'
import Messages from '../views/messages'
import MessagesDetail from '../views/messagesDetail'
function loadView(view) {
return () => import(`@/views/${view}.vue`)
}
var router = new VueRouter({
mode: 'history',
routes: [
{
name: 'app',
path: '/',
// component: App,
redirect: '/home',
},
{
name: 'about',
path: '/about',
component: loadView('about'),
children: [
{
name: 'news',
path: '/about/news',
component: loadView('news')
},
{
name: 'messages',
path: '/about/messages',
component: Messages,
children: [
{
path: '/about/messages/:id',
component: MessagesDetail,
//主要用于写某个指定路由跳转时需要执行的逻辑
beforeEnter: (to, from, next) => {
console.log('beforeEnter-to: ', to)
console.log('beforeEnter-from: ', from)
next();
},
afterEnter: (to, from, next) => {
console.log('---afterEnter-to: ', to)
console.log('---afterEnter-from: ', from)
next()
}
},
],
/*
*某个路由的路由钩子函数
*/
//主要用于写某个指定路由跳转时需要执行的逻辑
beforeEnter: (to, from, next) => {
console.log('beforeEnter-to: ', to)
console.log('beforeEnter-from: ', from)
next();
},
afterEnter: (to, from, next) => {
console.log('-----afterEnter-to: ', to)
console.log('-----afterEnter-from: ', from)
next()
}
},
]
},
{
name: 'home',
path: '/home',
component: Home,
}
]
});
/*
*全局路由钩子
*/
//这类钩子主要作用于全局,一般用来判断权限,以及以及页面丢失时候需要执行的操作
router.beforeEach((to, from, next) => {
console.log('beforeEach-to: ', to)
console.log('beforeEach-from: ', from)
next();
})
router.afterEach((to, from, next) => {
console.log('afterEach-to: ', to)
console.log('afterEach-from: ', from)
})
export default router

- 另:需将路由全局注入main.js
import Vue from 'vue'
import App from './App.vue'
import router from '@/router'
import less from 'less'
Vue.use(less)
require('./assets/style/iconfont.css')
Vue.config.productionTip = false; //作用是阻止 vue 在启动时生成生产提示
new Vue({
router,
render: h => h(App)
}).$mount('#app')
三、有关知识点
1. router 与 route
先上一张图来:

- 由此可见,router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter的构造函数得到的实力对象,该对象是一个全局对象,包含了许多关键对象与属性,例如:history:
methods: {
go(num){
if (num === 1){
this.$router.replace({ name: 'news' })
}else if (num === 2){
this.$router.push({ name: 'messages' })
}
}
},
- $route是一个当前路由的路由对象,每个路由都会有一个route对象,是一个局部对象,可获取对应的name,path,params,query等
<h3>ID:{{$route.params.id}}</h3>
2. 路由钩子(分三类)
在使用那部分已经贴出完整的代码,以及应用场景,故此处只做简单的列举:
- 全局路由钩子
——这类钩子主要作用于全局,一般用来判断权限,以及以及页面丢失时候需要执行的操作
router.beforeEach((to, from, next) => {
next();
})
router.afterEach((to, from, next) => {
})
- 某个路由独有的路由钩子
——主要用于写某个指定路由跳转时需要执行的逻辑
beforeEnter: (to, from, next) => {
next();
},
afterEnter: (to, from, next) => {
next()
}
- 路由组件内的路由钩子
export default {
name: "messages",
data() {
return {}
},
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不能获取组件实例 `this`
// 因为当钩子执行前,组件实例还没被创建
next();
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
next()
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
next()
}
}
3. 子路由
<router-link :to="`/about/messages/${v.id}`" class="">{{v.content}}</router-link>
<keep-alive>
<router-view></router-view>
</keep-alive>
.router-link-active{
color: burlywood !important;
}
- 在动态组件上使用
keep-alive
——当在多个组件之间切换的时候,有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。
(https://cn.vuejs.org/v2/guide/components-dynamic-async.html?_sw-precache=3b921049bd7cca2444e9efa512a9d9f5#%E5%9C%A8%E5%8A%A8%E6%80%81%E7%BB%84%E4%BB%B6%E4%B8%8A%E4%BD%BF%E7%94%A8-keep-alive "在动态组件上使用 keep-alive") - 当组件在 <keep-alive> 内被切换,它的
activated
和deactivated
这两个生命周期钩子函数将会被对应执行。 -
activated
钩子函数
——keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用。 -
deactivated
钩子函数
——实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
网友评论