Vue重点知识总结—vue-router

1.vue-router导航守卫(生命周期钩子)

导航守卫主要⽤来对路由的跳转进⾏监控,控制它的跳转或取消,路由守卫有全局的, 单个路由独享的, 或者组件级的。

导航钩⼦有3个参数

  • to:即将要进⼊的⽬标路由对象;
  • from:当前导航即将要离开的路由对象;
  • next:调⽤该⽅法后,才能进⼊下⼀个钩⼦函数(afterEach)。

具体有哪些钩子

  1. 全局前置守卫:router.beforeEach
  2. 全局解析守卫:router.beforeResolve
  3. 全局后置钩子:router.afterEach
  4. 路由独享钩子:在路由配置中添加beforeEnter钩子
  5. 组件内钩子:beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave

简单理解 beforeEach

每次通过跳转路由时,都会触发beforeEach这个钩⼦函数,这个回调函数共有三个参数,to,from,next这三个参数,to表⽰我要跳转的⽬标路由对应的参数,from表⽰来⾃那个路由,就是当前导航即将要离开的路由对象,next是⼀个回调函数,⼀定要调⽤next⽅法来resolve这个钩⼦函数,否则无法进⼊下⼀个钩⼦函数,也无法加载路由。

详见文档

2.导航守卫解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

3.路由懒加载如何实现,原理是什么?

路由懒加载需要导入异步组件,最常用的是通过import()来实现它。

function load(component) {
    return () => import(`views/${component}`)
}
// 箭头函数
const asyncPage = () => import('./views/home.vue')
// import()函数需要作为返回值,其返回Promise

编译打包后,会把每个路由组件的代码分割成一个js文件,初始化时不会加载这些js文件,只当激活路由时(访问该路由时)才会去按需加载对应的路由组件js文件。可以加快项⽬的加载速度。

在Vue3中,在组件中使用异步组件时需要使用defineAsyncComponent()函数才能实现懒加载,但在vue-router中同样可以使用import()方法

这时由于在 Vue 3 中,函数组件被定义为纯函数,异步组件定义需要通过将其包装在一个新的 defineAsyncComponent helper 中来显式定义。

import { defineAsyncComponent } from 'vue'
const home = defineAsyncComponent(() => import('@/views/home.vue'))
export default {
  name: 'async-components',
  components:{
    home
  }
};

4.vue-router如何做用户登录权限?

第一次打开页面或刷新页面时候可以在路由前置钩子router.beforeEach中查询用户信息。再根据返回的用户信息和to,from参数判断登录权限(这里要把用户的信息和登录状态做一个缓存),然后使用next回调控制路由跳转方向。具体的代码要根据具体的项目进行实施。

不是第一次打开页面情况,跳转时直接判断缓存中的登录状态和用户信息来判断是否拥有权限。若有权限则继续跳转到指定的路由。

5. vue-router 3.1.0 <router-link>新增的v-slot属性怎么⽤?

router-link 通过⼀个作⽤域插槽暴露底层的定制能⼒。方便我们更自由的定制导航链接的形式。记得把 custom 配置传递给 <router-link>,以防止它将内容包裹在 <a> 元素内。

详见文档

6. <router-view> 的 v-slot

详见文档

7. 如何实现⼀个路径渲染多个组件?

可以通过命名视图(router-view),它容许同⼀界⾯中拥有多个单独命名的视图,⽽不是只有⼀个单独的出⼝。如果router-view 没有设置名字,那么默认为default

<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" name="RightSidebar"></router-view>

一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。在配置routes时,确保正确使用 components 配置 (带上 s):

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      components: {
        default: Home,
        // LeftSidebar: LeftSidebar 的缩写
        LeftSidebar,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        RightSidebar,
      },
    },
  ],
})

详见文档

8. 如何实现多个路径共享⼀个组件?

只需将多个路径的component字段的值设置为同⼀个组件即可。

9. 如何监测动态路由的变化

可以通过watch⽅法来对$route进⾏监听,或者通过导航守卫的钩⼦函数beforeRouteUpdate来监听它的变化。

在Vue3中可使用useRoute()onBeforeRouteUpdate

10. Vue 如何去除url中的 #

哈希字符(#)部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO 中确实有不好的影响。

如果你担心这个问题,可以使用 HTML5 模式,将路由模式改为history

路由切换时本质是向history栈中添加一个路由,也就是添加一个history记录。

vue2(vue-router 3.x,已经弃用):

const router = new VueRouter({
  mode: 'history', // 也可设为 hash
  routes: [...]
})

vue3(vue-router 4.x):

import { createRouter, createWebHashHistory } from 'vue-router'

// hash 模式是用 createWebHashHistory() 创建的:
const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    //...
  ],
})

// 用 createWebHistory() 创建 HTML5 模式,推荐使用这个模式:
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    //...
  ],
})

11. $route 和 $router 的区别

$route是用来获取路由信息的:

它是当前路由信息的⼀个对象,⾥⾯包含路由的⼀些基本属性,包括name(路由名称)、meta(配置的路由元信息)、path(当前路径)、hash(hash参数)、query(查询参数)、params(当前传递参数)、fullPath(当前全路径)、matched(匹配项)等等。

每一个路由都会有一个$route对象,是一个局部的对象。

$router是用来操作路由的:

$router是VueRouter的一个实例,他包含了所有的路由,以及路由的跳转方法,钩子函数等,也包含一些子对象(例如history)。

12. Vue-router 使⽤params与query传参有什么区别

⽤法上

query要⽤path来引⼊,params要⽤name来引⼊,接收参数都是类似的,分别是this.$route.query.namethis.$route.params.name

// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })

// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

// `params` 不能与 `path` 一起使用,这时,params无法传递
router.push({ path: '/user', params: { username } }) // -> /user

// 占位符传参时,params参数对应的字段会被拼接到path中
// 使用 `name` 和 `params` 从自动 URL 编码中获益
const routes = [
  { name: 'user',path: '/user/:username' },
]
router.push({ name: 'user', params: { username } }) // -> /user/eduardo

展⽰上

  • query更加类似于我们ajax中get传参,params则类似于post,说的再简单⼀点,前者在浏览器地址栏中显⽰参数,后者则不显⽰
  • params是路由的⼀部分,必须要有,组件才能正常执行。query是拼接在url后⾯的参数,若没有也可正常执行组件。
  • query不设置也可以通过浏览器url进行传参,params不设置的时候,刷新页⾯或者返回参数会丢失
  • 刷新页面后,通过params传参会出现参数丢失的情况,可以通过query的传参⽅式或者在路由匹配规则加⼊占位符即可以解决参数丢失的情况。

13. Vue路由实现的底层原理

Vue的路由实现:hash 模式和 history 模式

hash模式:

早期前端路由的实现是基于window.location.hash 来实现的,window.location.hash 的值就是 URL中#后面的内容。

特点:hash虽然在URL中,但不被包括在HTTP请求中;只是用来指导浏览器动作,对服务端无作用,且hash的切换不会重加载页面。

hash 模式下,仅 # 号之前的内容会被包含在请求中,因此对于后端来说,即使路由切换错误,也不会返回 404 。

但是 hash 模式有两个缺点:

  1. url 不太美观
  2. 对SEO不太友好

history模式:

history采用HTML5的新特性;且提供了两个新方法:history.pushState()history.repalceState()可以对浏览器历史记录栈内存进行修改,且页面不会重载。

history.pushState方法接受三个参数,依次为:

  • state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填null。
  • title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null。
  • url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

假定当前网址是example.com/1.html,我们使用pushState方法在浏览记录(history对象)中添加一个新记录。

var stateObj = { foo: 'bar' };
history.pushState(stateObj, 'page 2', '2.html');

这样地址栏就可以看到我们自己存的标识了。历史记录也就存进去了。

每当激活的历史记录发生变化时,都会触发popstate事件,调用history.pushState()或者history.replaceState()不会触发popstate事件。pushState事件只会在其他浏览器操作时触发, 比如点击后退按钮(或者在JavaScript中调用history.back()history.go()方法)。

window.addEventListener('popstate', function(event) {
  //做一些操作
  console.log(event.state) // history.pushState()中传入的state状态对象
});

缺点:若没有对服务器(如nginx)进行配置,刷新本不存在的(pushState新增的历史记录)url页面时会出现404状况。

加载中...
加载中...