vue 是 Vue Router 是 Vuejs 官方的路由器,他和 Vue.js 深度集成,是用于单页应用中组件之间的导航,本质上就是通过 components 和 router 进行映射绑定,使用 router-link 传入指定的组件地址,通过 router-view 渲染已经和组件地址绑定好的组件。
<template>
<div>
<router-link to="/index/login">login</router-link>
<router-link to="/index/search">search</router-link>
<router-view></router-view>
</div>
</template>
再实际的项目的开发中,肯定是有多层级的页面。配置多层级的路由就是给上一级路由增加 children 在参数,在 children 添加新的路由信息
routes: [
{
path: "/index",
component: renderView,
children: [
{
path: "index",
name: "name",
component: Index,
children:[
{
path: "product",
name: "product",
component: Product,
}
]
},
{
path: "login/:user/comic/:age",
name: "name",
component: Login,
},
{
path: "search",
name: "search",
component: Search,
},
{
path: "*",
name: "rank",
component: Rank,
},
],
},
],
一个路由就是一个 Object ,如果有下一层级,就在里面加上一个 children 数组。
获取 URL 的参数是 router 的 path 里面加上 : 号,用来区分是一个动态的参数。在 render 组件的时候,执行 this.$route.params 就可以获取到动态传递的参数。
// router.js
{
path: "comic/:id/chapter/:id",
name: "name",
component: Comic,
},
// component.vue
console.log(this.$route.params);
//{ comic:123, chapter:456 }
而默认路由地址,这个一般会是设置成主页或者 404 的情况,就是在找不到 URL 的地址是映射到什么组件的情况下,跳转到 404 页面或者是统一跳转到首页。一般情况下,是会跳转到 404 。然后把这个配置的映射放在了 router 的最后一项。
// router.js
......
{
path: "search",
name: "search",
component: Search,
},
{
path: "*",
name: "rank",
component: Rank,
},
JavaScript 执行路由跳转这个是我自己的说法,官网给出的说法是叫作编程式路由 。一开始看到这个词逼格很高,但看完解释就是代码操作路由跳转。最后,还是按我自己的理解来把这个标题定为 JavaScript 执行路由跳转。在 Vue Router 中,有两种执行路由跳转的方式,第一种是声明式,第二种是编程式。
声明式:
<router-link to="/index/login">login</router-link>
通过 router-link 标签执行指定跳转。
编程式:
// vue
this.$router.push()
this.router.push({ name: 'user', params: { userId }}) // -> /user/123
this.router.push({ path: `/user/${userId}` }) // -> /user/123
// router
router.push(...);
在 vue 实例中,可以通过 $router 访问路由,可以直接使用 this.$router.push() 进行操作。在使用的时候需要注意的一点就是,当有 path 的时候, params 会被忽略。所以需要像上面一样以字符串形式拼接 URL。
与 router.push 相似的还有 router.replace,他们之间唯一不同的地方就是, router.replace 会替换掉当前的历史记录。这个和 location.href 和 location.replace 是类似的。而官网最后也提到了,router.push、 router.replace 和 router.go 跟 window.history.pushState、 window.history.replaceState 和 window.history.go (opens new window)好像, 实际上它们确实是效仿 window.history API 的。
路由命名只需要在 router 中在 path 同级下增加一个 name,之后使用 router.path ( name: index ,..)
即可。
// set
routes: [
{
path: '/index/:Id',
name: 'index',
component: Index
}
]
// link
router.push({ name: 'index', params: { Id: 123 } })
多视图展示 ,实质上就是增加 router-view 的标签,再通过 router 的 component 增加视图的名称。
<div>
<router-view class="one" name="one"></router-view>
<router-view class="two" name="two"></router-view>
</div>
routes: [
{
path: '/',
components: {
default: Index,
one: Search,
two: Details,
}
}
]
使用 redirect 参数,对 URL 进行替换 , 重定向的场景一般适用于兼容的情况下,比如项目改造升级,原有的 URL 如果希望保持不变,那么就可以用重定向指向新的 URL 。
routes: [
{ path: '/index', redirect: '/index/index' }
]
还有一个方法是叫别名,但是这个一般我没有使用,总感觉这样的方法会留下一些潜规则,所以一直不会使用这个方法,他的原理就是 URL 不变,但却走了另一个映射。
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
以对象模式通过 props 进行解耦
routes: [
{ path: '/user/:id', component: User, props: true },
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
以函数模式
routes: [
{
path: '/search',
component: Search,
props: route => ({ query: route.query.name })
}
]
vue-router 的钩子函数也叫导航守卫。这里有三种守卫类型,第一种是全局前置守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
console.log(to)
})
导航在出发的时候它都会执行。这里有一点要注意是,确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。
// BAD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
// 如果用户未能验证身份,则 `next` 会被调用两次
next()
})
// GOOD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
第二种是全局解析守卫,这个和全局前置守卫差不多,唯一的区别就是在导航确认之前所有的守卫和异步路由组件被解析后执行。
最后一种是全局后置钩子,这个和前面不同的是,这个钩子没有 next 函数体。
router.afterEach((to, from) => {
console.log(to)
})
除了全局守卫之外还有路由独享的路由,他的用法就是在 routes 里面加上 beforeEnter。
routes: [
{
path: '/index',
component: Index,
beforeEnter: (to, from, next) => {
// ...
}
}
]
还有一个组件内的守卫,他的方法是写在组件里面的。他也有三个方法,后面会有一个具体例子。
const Index = {
template: `...`,
beforeRouteEnter(to, from, next) {
},
beforeRouteUpdate(to, from, next) {
},
beforeRouteLeave(to, from, next) {
}
}
像以往获取数据的方法一般是写在了组件的函数里面,也就是导航完成后,执行数据的拉取。那么,还有另一种方法就是,在导航之前加载数据。
它的原理就是我们在组件的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。
import { ajax } from '../../js/common/ajax-helper';
export default {
data() {
return {
num: 1
}
},
// 方法一
beforeRouteEnter(to, from, next) {
console.log(to)
console.log(window.location.href)
ajax(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
// 方法二
beforeRouteUpdate (to, from, next) {
this.post = null
ajax(to.params.id, (err, post) => {
this.setData(err, post)});
next();
},
methods: {
setData(err, num){
this.num = num;
console.log(this.num)
}
}
}
vm 是当前实例,在进入这个路由之前获取 to.params.id ,发送请求拿到数据之后,通过 next 执行将数据挂载。
方法二是已经到了路由更新之前的阶段,可以直接通过 this 执行组件代码。执行完毕再执行下一步
我们需要将不同路由对应的组件分割成不同的模块,然后在路由在被访问的时候才加载对应的组件,这样能够大大降低页面性能的损耗。
const Index = () =>
import(/* webpackChunkName: "Index" */ './Index.vue');
const Search = () =>
import(/* webpackChunkName: "Search" */ './Search.vue');
const Details = () =>
import(/* webpackChunkName: "Details" */ './Details.vue')
const router = new VueRouter({
routes: [{ path: '/Details', component: Details }]
});
这里其实也是 vue 和 webpack 结合使用的功能,到了新的 vite 工具可能会使用新的一些方法可以后面再了解下。
这里引用一段 DEMO 的代码,也是比较简单。
import VueRouter from 'vue-router'
const { isNavigationFailure, NavigationFailureType } = VueRouter
router.push('/admin').catch(failure => {
if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
showToast('Login in order to access the admin panel')
}
})
使用 vue-router 最好的方法还是阅读文档,若换一个 React 又有属于它的插件,虽说,大致的逻辑不会相差太远,但肯定是另外一套写法。所以,在理解了一个插件的实现功能之后去理解下一个类似的插件就可以带着一些问题去了解它,理解起来就会更容易上手也能对插件之间进行对比和选型。
在下次接触 react 的路由插件可以从这几个点去思考
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。