参考文章链接
https://segmentfault.com/a/1190000011519350
https://www.cnblogs.com/anjx/p/11242387.html
vue的路由模式,分为三种模式:
1.Hash模式:使用URL的hash值来作为路由,支持所有浏览器。
2.History模式:以HTML5 History API 和服务器配置。
3.Abstract模式:支持所有js运行模式。如果发现没有浏览器的API,路由会强制进入这个模式。
两种模式的区别
1.hash模式——即地址栏URL中的#符号。比如http://www.abc.com/#/hello
特点在于:hash虽然出现URL中,但不会被包含在HTTP请求中,对后端完全没影响,因此改变hash不会重新加载页面。
hash模式下,仅hash符号之前的内容会被包含在请求中,因此对于后端,即使没有做到对路由的全覆盖,也不会返回错误。
hash模式背后的原理是onhashchange事件,可以在window对象上监听这个事件:
window.onhashchange = function (event) {
let hash = location.hash.slice(1);
document.body.style.color = hash;
}
因为hash发生变化的url都会被浏览器记录下来,从而你会发现浏览器的前进后退都可以用了
2.history模式——利用了H5 History Interface中新增的pushState()和replaceState()方法。这两方法应用于浏览器的历史记录栈,在当前已有的back、forward、go的基础上,提供了对历史记录进行修改的功能。
history模式下,前端的url必须和实际向后端发起请求的url一致。如www.abc.com/detail/id。如果后端缺少对/detail/id的路由处理,将返回404错误。
Vue-router 报NavigationDuplicated的解决方案
因为Vue-router在3.1之后把$router.push()方法改为了Promise。所以假如没有回调函数,错误信息就会交给全局的路由错误处理,因此会报错:[NavigationDuplicated {_name: "NavigationDuplicated", name: "NavigationDuplicated"}]
解决方法是重新改写VueRouter的push和replace方法,添加回调处理错误
import VueRouter from 'vue-router'
const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return VueRouterPush.call(this, location).catch(err => err)
}
const VueRouterReplace = VueRouter.prototype.replace
VueRouter.prototype.replace = function replace(location) {
return VueRouterReplace.call(this, location).catch(err => err)
}
Vue.use(VueRouter)
路由组件的按需加载
vue项目实现按需加载的3种方式
1.vue异步组件
2.es提案的import()
3.webpack的require.ensure()
1.vue异步组件技术
vue-router配置路由,使用vue的异步组件技术,可以实现按需加载。
但是,这种情况下一个组件生产一个js文件。
{
path: '/promisedemo',
name: 'PromiseDemo',
component: resolve => require(['../components/PromiseDemo'], resolve)
}
2.es提案的import()
- 推荐使用这种方式(需要webpack > 2.4)
- webpack官方文档:webpack中使用import()
// 下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。
const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')
const ImportFuncDemo2 = () => import('../components/ImportFuncDemo2')
// 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。
// const ImportFuncDemo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo')
// const ImportFuncDemo2 = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo2')
3.webpack提供的require.ensure()
vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。
{
path: '/promisedemo',
name: 'PromiseDemo',
component: resolve => require.ensure([], () => resolve(require('../components/PromiseDemo')), 'demo')
},
{
path: '/hello',
name: 'Hello',
// component: Hello
component: resolve => require.ensure([], () => resolve(require('../components/Hello')), 'demo')
}
路由守卫
vue-router全局守卫:
1.router.beforeEach 全局前置守卫 进入路由之前
2.router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用
3.router.afterEach 全局后置钩子 进入路由之后
导航钩子有3个参数:
1、to:即将要进入的目标路由对象;
2、from:当前导航即将要离开的路由对象;
3、next :调用该方法后,才能进入下一个钩子函数(afterEach)。
next()//直接进to 所指路由
next(false) //中断当前路由
next('route') //跳转指定路由
next('error') //跳转错误路由
router.beforeEach((to, from, next) => {
if (from) {
document.title = to.meta.title;
} else {
document.title = "创量广告投放平台";
}
next();
});
路由独享守卫
如果你不想全局配置守卫的话,你可以为某些路由单独配置守卫beforeEnter
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// 参数用法什么的都一样,调用顺序在全局前置守卫后面,所以不会被全局守卫覆盖
// ...
}
}
]
})
路由组件内的守卫:
1.beforeRouteEnter 进入路由前
2.beforeRouteUpdate (2.2) 路由复用同一个组件时
3.beforeRouteLeave 离开当前路由时
beforeRouteEnter (to, from, next) {
// 在路由独享守卫后调用 不!能!获取组件实例 `this`,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用 可以访问组件实例 `this`
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用,可以访问组件实例 `this`
}
beforeRouteEnter访问this
因为钩子在组件实例还没被创建的时候调用,所以不能获取组件实例 this,可以通过传一个回调给next来访问组件实例 。
但是回调的执行时机在mounted后面,所以在我看来这里对this的访问意义不太大,可以放在created或者mounted里面。
beforeRouteEnter (to, from, next) {
console.log('在路由独享守卫后调用');
next(vm => {
// 通过 `vm` 访问组件实例`this` 执行回调的时机在mounted后面,
})
}
beforeRouteLeave
导航离开该组件的对应路由时调用,我们用它来禁止用户离开,比如还未保存草稿,或者在用户离开前,将setInterval销毁,防止离开之后,定时器还在调用。
beforeRouteLeave (to, from , next) {
if (文章保存) {
next(); // 允许离开或者可以跳到别的路由 上面讲过了
} else {
next(false); // 取消离开
}
}
完整的路由导航解析流程(不包括其他生命周期):
1.触发进入其他路由。
2.调用要离开路由的组件守卫beforeRouteLeave
3.调用局前置守卫:beforeEach
4.在重用的组件里调用 beforeRouteUpdate
5.调用路由独享守卫 beforeEnter。
6.解析异步路由组件。
7.在将要进入的路由组件中调用beforeRouteEnter
8.调用全局解析守卫 beforeResolve
9.导航被确认。
10.调用全局后置钩子的 afterEach 钩子。
11.触发DOM更新(mounted)。
12.执行beforeRouteEnter 守卫中传给 next 的回调函数
1:导航守卫的执行顺序是怎么样的?
beforeRouteLeave < beforeEach < beforeRouteUpdate < beforeEnter < beforeRouteEnter < beforeResolve < afterEach
2:导航守卫中的next的用处?
next的作用,使导航守卫队列的继续向下迭代
3:为什么afterEach守卫没有next?
afterEach根本不在导航守卫队列内,没有迭代的next
4:beforeEach是否可以叠加?
beforeEach是可以叠加的,所有的全局前置守卫按顺序存放在beforeHooks的数组里面,
5:路由跳转经历了哪几部分?
路由跳转的核心方法是transitionTo,在跳转过程中经历了一次confirmTransition,
路由跳转
1. router-link
2. this.$router.push() (函数里面调用)
3. this.$router.replace() (用法同push)
4. this.$router.go(n)
一、不带参
1.1 router-link
//name,path都行, 建议用name
注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
1.2 this.$router.push()
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
1.3 this.$router.replace() (用法同push)
二、带参
2.1 router-link
params传参数 (类似post)
路由配置 path: "/home/:id"
或者 path: "/home:id"
不配置path ,第一次可请求,刷新页面id会消失
配置path,刷新页面id会保留
html 取参 $route.params.id
script 取参this.$route.params.id
query传参数 (类似get,url后面会显示参数)
路由可不配置
html 取参 $route.query.id
script 取参 this.$route.query.id
2.2 this.$router.push
query传参
this.$router.push({
name:'home',
query: {id:'1'}
})
this.$router.push({
path:'/home',
query: {id:'1'}
})
this.$router.push("baidu.com?tab=2");
路由可不配置
html 取参 $route.query.id
script 取参 this.$route.query.id
params传参
this.$router.push({
name:'home',
params: {id:'1'}
})
只能用 name
路由配置 path: "/home/:id"
或者 path: "/home:id"
不配置path ,第一次可请求,刷新页面id会消失
配置path,刷新页面id会保留
html 取参 $route.params.id
script 取参 this.$route.params.id
2.3 this.$router.replace() (用法同push)
2.4 this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
区别:
this.$router.push 跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面
this.$router.replace 跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
this.$router.go(n) 向前或者向后跳转n个页面,n可为正整数或负整数