大前端百科全书,前端界的百科全书,记录前端各相关领域知识点,方便后续查阅及面试准备
- 说一下vue-router的原理是什么?
- 介绍下 vue-router 中的导航钩子函数
- 完整的路由导航解析流程(不包括其他生命周期)
- 说一下路由钩子在 vue 生命周期的体现?
- Vue 切换路由的时候,需要保存当时状态的功能,怎么实现呢?
- Vue-router history 模式部署的时候要注意什么?nginx 和 node 时候分别怎么处理?
说一下vue-router的原理是什么?
实现原理:vue-router的原理就是更新视图而不重新请求页面
vue-router可以通过mode参数设置为三种模式:hash模式、history模式、abstract模式。
hash模式
默认是hash模式,基于浏览器history api,使用
window.addEventListener('hashchange', callback, false)
对浏览器地址进行监听
当调用push时,把新路由添加到浏览器访问历史的栈顶。
使用replace时,把浏览器访问历史的栈顶路由替换成新路由。
hash的值等于url中#及其以后的内容。
浏览器是根据hash值的变化,将页面加载到相应的DOM位置。
锚点变化只是浏览器的行为,每次锚点变化后依然会在浏览器中留下一条历史记录,可以通过浏览器的后退按钮回到上一个位置
history模式
基于浏览器history api ,使用window.onpopstate对浏览器地址进行监听。
对浏览器history api中的pushState()、replaceState()进行封装,当方法调用,会对浏览器的历史栈进行修改。从而实现URL的跳转而无需加载页面。
但是他的问题在于当刷新页面的时候会走后端路由,所以需要服务端的辅助来兜底,避免URL无法匹配到资源时能返回页面
abstract
不涉及和浏览器地址的相关记录。流程跟hash模式一样,通过数组维护模拟浏览器的历史记录栈,服务端下使用。使用一个不依赖于浏览器的浏览器历史虚拟管理后台
总结 hash模式和history模式都是通过window.addEvevtListenter()方法监听 hashchange和popState进行相应路由的操作。可以通过back、foward、go等方法访问浏览器的历史记录栈,进行各种跳转。而abstract模式是自己维护一个模拟的浏览器历史记录栈的数组
介绍下 vue-router 中的导航钩子函数
全局的钩子函数 beforeEach 和 afterEach
beforeEach 有三个参数,to 代表要进入的路由对象,from 代表离开的路由对象。next 是一个必须要执行的函数,如果不传参数,那就执行下一个钩子函数,如果传入 false,则终止跳转,如果传入一个路径,则导航到对应的路由,如果传入 error ,则导航终止,error 传入错误的监听函数
单个路由独享的钩子函数 beforeEnter
它是在路由配置上直接进行定义的
组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。
它们是直接在路由组件内部直接进行定义的
完整的路由导航解析流程(不包括其他生命周期)
- 触发进入其他路由。
- 调用要离开路由的组件守卫 beforeRouteLeave
- 调用全局前置守卫: beforeEach
- 在重用的组件里调用 beforeRouteUpdate
- 调用路由独享守卫 beforeEnter
- 解析异步路由组件
- 在将要进入的路由组件中调用 beforeRouteEnter
- 调用全局解析守卫 beforeResolve
- 导航被确认
- 调用全局后置钩子的 afterEach 钩子
- 触发 DOM 更新(mounted)
- 执行 beforeRouteEnter 守卫中传给 next 的回调函数
触发钩子的完成顺序
路由导航、keep-alive、 组件生命周期钩子结合起来的,假设是从 a 组件离开,第一次进入 b 组件,触发顺序:
- beforeRouteLeave:路由组件的组件离开路由前钩子,可取消路由离开。
- beforeEach:路由全局前置守卫,可用于登录验证、全局路由 Loading 等。
- beforeEnter: 路由独享守卫
- beforeRouteEnter: 路由组件的组件进入路由前钩子
- beforeResolve:路由全局解析守卫
- afterEach:路由全局后置钩子
- beforeCreate 组件生命周期,不能访问 this
- created:组件生命周期,可以访问 this, 不能访问dom
- beforeMount:组件生命周期
- deactivated: 离开缓存组件 a,或者触发 a 的beforeDestroy 和 destroyed 组件销毁钩子
- mounted:访问或者操作 dom
- activated:进入缓存组件,进入a的嵌套子组件(如果有的话)
- 执行beforeRouteEnter回调函数next。
说一下路由钩子在 vue 生命周期的体现?
Vue-Router 导航守卫
有的时候,我们需要通过路由来进行一些操作,比如最常见的登录权限验证,当用户满足条件时,才让其进入导航,否则就取消跳转,并跳到登录页面让其登录。
为此我们有很多种方法可以植入路由的导航过程:全局的,单个路由独享的,组件级的
全局路由钩子
vue router 全局有三个路由钩子:
- router.beforeEach 全局前置守卫 进入路由之前
- router.beforeResolve 全局解析守卫(2.5.0+) 在 beforeRouteEnter 调用之后调用
- router.afterEach 全局后置钩子 进入路由之后
具体使用:
- beforeEach (判断是否登录, 没登录就跳转到登录页)
router.beforeEach((to, from, next) => {
let ifInfo = Vue.prototype.$common.getSession("userData"); // 判断是否登录的存储信息
if (!ifInfo) {
// sessionStorage里没有储存user信息
if (to.path == "/") {
//如果是登录页面路径,就直接next()
next();
} else {
//不然就跳转到登录
Message.warning("请重新登录!");
window.location.href = Vue.prototype.$loginUrl;
}
} else {
return next();
}
});
- afterEach(跳转之后滚动条返回顶部)
router.afterEach((to, from) => {
// 跳转之后滚动条回到顶部
window.scrollTo(0, 0);
});
单个路由独享钩子
beforeEnter
如果不想全局配置守卫的话,可以为某些路由单独配置守卫
export default [
{
path: "/",
name: "login",
component: login,
beforeEnter: (to, from, next) => {
console.log("即将进入登录页面");
next();
},
},
];
组件内的钩子
beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave- beforeRouteEnter: 进入组件前触发
- beforeRouteUpdate: 当前地址改变并且该组件被复用时触发,举例来说,带有动态参数的路径 list/:id,在/list/1 和/list/2 之间跳转的时候,由于会渲染同样的 list 组件,这个钩子在这种情况下就会被调用
- beforeRouteLeave: 离开组件被调用
注意:beforeRouteEnter 组件内还访问不到 this,因为该守卫执行前组件实例还没有被创建,需要传一个回调给 next 来访问,比如:
beforeRouteEnter(to, from, next) {
next(target => {
if (from.path == '/classProcess') {
target.isFromProcess = true
}
})
}
beforeRouteUpdate 和 beforeRouteLeave 可以访问组件实例 this
Vue 切换路由的时候,需要保存当时状态的功能,怎么实现呢?
beforeRouteLeave
beforeRouteLeave(to,from,next){
if('用户已经输入了信息'){
// 出现弹窗提醒保存草稿,或者自动后台为其保存
// do something
}else{
next(true);// 用户离开
}
}
这里可以把数据存储到vuex,缺点就是页面刷新数据会丢失
存储到localStorage并且和vuex关联
存储到数据库中
keep-alive
用keep-alive缓存路由
Vue-router history 模式部署的时候要注意什么?server 端用 nginx 和 node 时候分别怎么处理?
Vue-router 默认是 hash 模式,使用 url 的 hash 来模拟一个完整的 url,当 url 改变的时候,页面不会重新加载。比如:使用 hash 模式的时候,那么访问变成 http://localhost:8080/page/#/这样的访问。但是如果路由使用history的话,那么访问的路径变成如下http://localhost:8080/page/.
需要注意的问题
如果使用的是 history 这种模式,在非首页情况下刷新页面或直接访问的时候就会返回 404,导致页面丢失。
这是因为利用 H5 history API 来实现的。通过 history.pushState 方法实现 URL 的跳转而无需重新加载页面。但是它的问题在于当刷新页面的时候会走后端路由,相当于直接在浏览器里输入这个地址,要对服务器发起 http 请求,但是这个目标在服务器上又不存在这个路由,所以会返回 404
解决方式:需要服务端的辅助来兜底,避免 URL 无法匹配到资源时能返回页面
Nginx 配置
location / {
try_files $uri $uri/ /index.html;
}
node 配置
// 可以使用 connect-history-api-fallback 这个中间件
// npm install --save connect-history-api-fallback
// app.js
var history = require("connect-history-api-fallback");
var connect = require("connect");
var app = connect().use(history()).listen(3000);
// 或者使用 express
var express = require("express");
var app = express();
app.use(history());