vue-router学习笔记

335 阅读6分钟

6.1 相关理解

6.1.1 vue-router 的理解

vue 的一个插件库,专门用来实现 SPA 应用

6.1.2 对 SPA 应用的理解

  1. 单页 Web 应用(single page web application,SPA)。
  2. 整个应用只有一个完整的页面。
  3. 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
  4. 数据需要通过 ajax 请求获取。

6.1.3 路由的理解

  1. 什么是路由?

    1. 一个路由就是一组映射关系(key - value)
    2. key 为路径, value 可能是 function 或 component

  2. 路由分类

    1. 后端路由:

      1. 理解:value 是 function, 用于处理客户端提交的请求。
      2. 工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。

    2. 前端路由:

      1. 理解:value 是 component,用于展示页面内容。
      2. 工作过程:当浏览器的路径改变时, 对应的组件就会显示。

6.2 基本路由

6.2.1 效果

6.2.2.基本使用

  1. 安装vue-router,命令:npm i vue-router

  2. 应用插件:Vue.use(VueRouter)

  3. 编写router配置项:

    v3版本:

//引入VueRouter
import VueRouter from 'vue-router'
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'

//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
  routes:[
    {
      path:'/about',
      component:About
    },
    {
      path:'/home',
      component:Home
    }
  ]
})

//暴露router
export default router
v4版本:
// 1. 定义路由组件.
// 也可以从其他文件导入
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }

// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
// 我们后面再讨论嵌套路由。
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
]

// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = VueRouter.createRouter({
  // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
  history: VueRouter.createWebHashHistory(),
  routes, // `routes: routes` 的缩写
})

// 5. 创建并挂载根实例(下面代码可以写在main.ts里,本页面export default router)
const app = Vue.createApp({})
//确保 _use_ 路由实例使
//整个应用支持路由。
app.use(router)

app.mount('#app')

// 现在,应用已经启动了!
  1. 实现切换(active-class可配置高亮样式)
<router-link active-class="active" to="/about">About</router-link>
  1. 指定展示位置
<router-view></router-view>

6.2.3几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。
  5. setup中使用useRouter来获取router,使用useRoute来获取route对象

6.2.4 总结: 编写使用路由的 3 步

  1. 定义路由组件
  2. 注册路由
  3. 使用路由

6.3路由匹配

6.3.1 动态路由匹配

const routes = [
  // 动态字段以冒号开始
  { path: '/users/:id', component: User },
  //像 /users/johnny 和 /users/jolyne 这样的 URL 都会映射到同一个路由。
]

使用带有参数的路由时需要注意的是,当用户从 /users/johnny 导航到 /users/jolyne 时,相同的组件实例将被重复使用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用

要对同一个组件中参数的变化做出响应的话,你可以简单地 watch $route 对象上的任意属性,在这个场景中,就是 $route.params

const User = {
  template: '...',
  created() {
    this.$watch(
      () => this.$route.params,
      (toParams, previousParams) => {
        // 对路由变化做出响应...
      }
    )
  },
}
//或者,使用 [beforeRouteUpdate 导航守卫](https://ustcse.wolai.com/owWFTtB47ZXBoQ9ZM3F8BE#wVjkEggJ5q9DXTTm9uLWGy),它也可以取消导航:
const User = {
  template: '...',
  async beforeRouteUpdate(to, from) {
    // 对路由变化做出响应...
    this.userData = await fetchUser(to.params.id)
  },
}

6.3.2 捕获所有路由或 404 Not found 路由

const routes = [
  // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
  // 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下
  { path: '/user-:afterUser(.*)', component: UserGeneric },
]

6.3.3 高级路由匹配语法

静态路由

/about

动态路由

/user/:id

在参数中自定义正则

const routes = [
  // /:orderId -> 仅匹配数字
  { path: '/:orderId(\d+)' },
  // /:productName -> 匹配其他任何内容
  { path: '/:productName' },
]

可重复的参数

const routes = [
  // /:chapters ->  匹配 /one, /one/two, /one/two/three, 等
  { path: '/:chapters+' },
  // /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
  { path: '/:chapters*' },
]
// 给定 { path: '/:chapters*', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// 产生 /
router.resolve({ name: 'chapters', params: { chapters: ['a', 'b'] } }).href
// 产生 /a/b

// 给定 { path: '/:chapters+', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// 抛出错误,因为 `chapters` 为空 

可与自定义正则结合使用

const routes = [
  // 仅匹配数字
  // 匹配 /1, /1/2, 等
  { path: '/:chapters(\d+)+' },
  // 匹配 /, /1, /1/2, 等
  { path: '/:chapters(\d+)*' },
]

Sensitive 与 strict 路由配置

默认情况下,所有路由是不区分大小写的,并且能匹配带有或不带有尾部斜线的路由。例如,路由 /users 将匹配 /users/users/、甚至 /Users/。这种行为可以通过 strictsensitive 选项来修改,它们可以既可以应用在整个全局路由上,又可以应用于当前路由上:

sensitive:控制大小写敏感

strict:控制能否带有尾部斜线

const router = createRouter({
  history: createWebHistory(),
  routes: [
    // 将匹配 /users/posva 而非:
    // - /users/posva/strict: true
    // - /Users/posva 当 sensitive: true
    { path: '/users/:id', sensitive: true },
    // 将匹配 /users, /Users, 以及 /users/42 而非 /users/ 或 /users/42/
    { path: '/users/:id?' },
  ]
  strict: true, // applies to all routes
})

可选参数

你也可以通过使用 ? 修饰符(0 个或 1 个)将一个参数标记为可选:

const routes = [
  // 匹配 /users 和 /users/posva
  { path: '/users/:userId?' },
  // 匹配 /users 和 /users/42
  { path: '/users/:userId(\d+)?' },
]

请注意,* 在技术上也标志着一个参数是可选的,但 ? 参数不能重复。

6.4 嵌套(多级)路由

  1. 配置路由规则,使用children配置项:
routes:[
  {
    path:'/about',
    component:About,
  },
  {
    path:'/home',
    component:Home,
    children:[ //通过children配置子级路由
      {
        path:'', //子路由不匹配时,默认嵌入到Home的<route-view />位置
        component:first
      },
      {
        path:'news', //此处一定不要写:/news
        component:News
      },
      {
        path:'message',//此处一定不要写:/message
        component:Message
      }
    ]
  }
]
  1. 跳转(要写完整路径):
<router-link to="/home/news">News</router-link>
  1. 嵌套的命名路由
const routes = [
  {
    path: '/user/:id',
    name: 'user-parent'
    component: User,
    children: [{ path: '', name: 'user', component: UserHome }],
  },
]
//命名后,会匹配到哪个呢?

6.5 编程式路由导航

6.5.1基本使用

  1. 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活
声明式编程式
<router-link :to="...">router.push(...)
  1. 相关 API:

    1. this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
    2. this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
    3. this.$router.back(): 请求(返回)上一个记录路由
  2. 具体编码:

//$router的两个API
this.$router.push({
    name:'xiangqing',
    params:{
      id:xxx,
      title:xxx
    }
})

this.$router.replace({
    name:'xiangqing',
    params:{
      id:xxx,
      title:xxx
    }
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退
const router = useRouter();

**push:**

// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

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

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

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

**replace:**

router.push({ path: '/home', replace: true })
router.replace({ path: '/home' })
<router-link replace .......>home</router-link>

**go:**

// 向前移动一条记录,与 router.forward() 相同
router.go(1)

// 返回一条记录,与 router.back() 相同
router.go(-1)

// 前进 3 条记录
router.go(3)

// 如果没有那么多记录,静默失败
router.go(-100)
router.go(100)
注意:如果提供了 `path``params` 会被忽略,上述例子中的 `query` 并不属于这种情况。
const username = 'eduardo'
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) // -> /user
当指定 `params` 时,可提供 `string``number` 参数。**任何其他类型(如 ****`undefined`****、****`false`**** 等)都将被自动字符串化**。

6.5.2.缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。
  2. 具体编码:
<keep-alive include="News"> 
    <router-view></router-view>
</keep-alive>

6.5.3两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。

  2. 具体名字:

    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。

6.6 路由传参

6.6.1基本使用

  1. 配置路由,声明接收params参数
{
  path:'/home',
  component:Home,
  children:[
    {
      path:'news',
      component:News
    },
    {
      path:'message',
      component:Message,
      children:[
        {
          name:'xiangqing',
          path:'detail/:id/:title', //使用占位符声明接收params参数
          component:Detail
        }
      ]
    }
  ]
}
  1. 传递参数
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
        
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link 
  :to="{
    name:'xiangqing',
    params:{
       id:666,
       title:'你好'
    }
  }"
>跳转</router-link>
  1. 接收参数:
$route.params.id
$route.params.title

6.6.2路由的props配置

作用:让路由组件更方便的收到参数

//原本的写法:要在组件中使用 $route ,与路由紧密耦合,
//这限制了组件的灵活性,因为它只能用于特定的 URL
const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}
const routes = [{ path: '/user/:id', component: User }]
{
  name:'xiangqing',
  path:'detail/:id',
  components: { default: User, detail: Detail},

  //第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
  // props:{a:900}

  //第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
  // props:true
  //对于有命名视图的路由,你必须为每个命名视图定义 props 配置
  // props: { default: true, detail: false }
  
  //第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
  //请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。
  //如果你需要状态来定义 props,请使用包装组件,这样 vue 才可以对状态变化做出反应。
  props(route){
    return {
      id:route.query.id,
      title:route.query.title
    }
  }
}

6.6.3<router-link>的replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>

特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) // -> /user

6.7 命名路由

除了 path 之外,你还可以为任何路由提供 name。这有以下优点:

  • 没有硬编码的 URL
  • params 的自动编码/解码。
  • 防止你在 url 中出现打字错误。
  • 绕过路径排序(如显示一个)
const routes = [
  {
    path: '/user/:username',
    name: 'user',
    component: User,
  },
]
<router-link :to="{ name: 'user', params: { username: 'doing' }}">
  User
</router-link>
router.push({ name: 'user', params: { username: 'doing' } })

6.8 命名视图

有时候想同时 (同级) 展示多个视图,而不是嵌套展示

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

6.9 重定向和别名

6.9.1重定向

//路径
const routes = [{ path: '/home', redirect: '/' }]

//命名路由
const routes = [{ path: '/home', redirect: { name: 'homepage' } }]

//方法
const routes = [
  {
    // /search/screens -> /search?q=screens
    path: '/search/:searchText',
    redirect: to => {
      // 方法接收目标路由作为参数
      // return 重定向的字符串路径/路径对象
      return { path: '/search', query: { q: to.params.searchText } }
    },
  },
  {
    path: '/search',
    // ...
  },
]

//相对重定向
const routes = [
  {
    // 将总是把/users/123/posts重定向到/users/123/profile。
    path: '/users/:id/posts',
    redirect: to => {
      // 该函数接收目标路由作为参数
      // 相对位置不以`/`开头
      // 或 { path: 'profile'}
      return 'profile'
    },
  },
]

请注意,导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在上面的例子中,在 /home 路由中添加 beforeEnter 守卫不会有任何效果。

在写 redirect 的时候,可以省略 component 配置,因为它从来没有被直接访问过,所以没有组件要渲染。唯一的例外是嵌套路由:如果一个路由记录有 childrenredirect 属性,它也应该有 component 属性。

6.9.2别名

/ 别名为 /home,意味着当用户访问 /home 时URL 仍然是 /home,但会被匹配为用户正在访问 /

const routes = [
  {
    path: '/users/:id',
    component: UsersByIdLayout,
    children: [
      // 为这 3 个 URL 呈现 UserDetails
      // - /users/24
      // - /users/24/profile
      // - /24
      { path: 'profile', component: UserDetails, alias: ['/:id', '/abc/:id'] },
    ],
  },
]

6.10路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。

  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。

  3. hash模式:

    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
import { createRouter, createWebHashHistory } from 'vue-router'

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    //...
  ],
})
  1. history模式:

    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
import { createRouter, createWebHistory } from 'vue-router'

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

6.11路由守卫

  1. 作用:对路由进行权限控制
  2. 分类:全局守卫、独享守卫、组件内守卫

全局守卫:

v3版本:

//**全局前置守卫**:初始化时执行、每次路由切换前执行
/*
第三个参数next:
确保 next 在任何给定的导航守卫中都被严格调用一次。
它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错
后置守卫无法调用next
*/
router.beforeEach((to,from,next)=>{
  console.log('beforeEach',to,from)
  if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
    if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
      next() //放行
    }else{
      alert('暂无权限查看')
      // next({name:'guanyu'})
    }
  }else{
    next() //放行
  }
})

//**全局后置守卫**:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
  console.log('afterEach',to,from)
  if(to.meta.title){ 
    document.title = to.meta.title //修改网页的title
  }else{
    document.title = 'vue_test'
  }
})

v4版本:

 //**全局前置守卫**:初始化时执行、每次路由切换前执行
 router.beforeEach(async (to, from) => {
   if (to.meta.requireAuth) {
    if (!appStore.user.auth.isAdmin) {
      return {
        name: to.meta.redirect as string,
        params: to.params,
      };
    }
  } else {
    return true;
  }
 })
 
/*
 可以返回的值如下:

1.false: 取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),
         那么 URL 地址会重置到 from 路由对应的地址。
2.一个路由地址: 通过一个路由地址跳转到一个不同的地址,就像你调用 router.push() 一样,
              你可以设置诸如 replace: true 或 name: 'home' 之类的配置。
              当前的导航被中断,然后进行一个新的导航,就和 from 一样。
*/
              
//**全局解析守卫**:和router.beforeEach类似,在每次导航时都会触发,
//但是确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用
//是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。
router.beforeResolve(async to => {
  if (to.meta.requiresCamera) {
    try {
      await askForCameraPermission()
    } catch (error) {
      if (error instanceof NotAllowedError) {
        // ... 处理错误,然后取消导航
        return false
      } else {
        // 意料之外的错误,取消导航并把错误传给全局处理器
        throw error
      }
    }
  }
})

//**全局后置守卫**:初始化时执行、每次路由切换后执行
router.afterEach((to,from,failure)=>{
  console.log('afterEach',to,from)
  if(to.meta.title){ 
    document.title = to.meta.title //修改网页的title
  }else{
    document.title = 'vue_test'
  }
})

独享守卫:

//可以直接在路由配置上定义 beforeEnter 守卫
//beforeEnter 守卫 只在**进入路由时**触发,不会在 params、query 或 hash 改变时触发。
const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from) => {
      // reject the navigation
      return false
    },
  },
]
//也可以将一个函数数组传递给 beforeEnter,这在为不同的路由重用守卫时很有用
function removeQueryParams(to) {
  if (Object.keys(to.query).length)
    return { path: to.path, query: {}, hash: to.hash }
}

function removeHash(to) {
  if (to.hash) return { path: to.path, query: to.query, hash: '' }
}

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: [removeQueryParams, removeHash],
  },
  {
    path: '/about',
    component: UserDetails,
    beforeEnter: [removeQueryParams],
  },
]

组件内守卫:

const UserDetails = {
  template: `...`,
  beforeRouteEnter(to, from) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
    // 因为当守卫执行时,组件实例还没被创建!
  },
  /* 
    //通过给next添加回调来访问this,仅适用于beforeRouteEnter。
    //beforeRouteUpdate和beforeRouteLeave本身支持访问this,所以没有必要
    beforeRouteEnter (to, from, next) {
      next(vm => {
        // 通过 `vm` 访问组件实例
      })
    }
  */
  beforeRouteUpdate(to, from) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
    // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from) {
    // 在导航离开渲染该组件的对应路由时调用
    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  },
}

//在组合式api中,可通过onBeforeRouteUpdate和onBeforeRouteLeave分别添加update和leave守卫

完整的导航解析流程

  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 的回调函数,创建好的组件实例会作为回调函数的参数传入

6.12路由元信息meta

将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的meta属性来实现,并且它可以在路由地址和导航守卫上都被访问到

const routes = [
  {
    path: '/posts',
    component: PostsLayout,
    children: [
      {
        path: 'new',
        component: PostsNew,
        // 只有经过身份验证的用户才能创建帖子
        meta: { requiresAuth: true }
      },
      {
        path: ':id',
        component: PostsDetail
        // 任何人都可以阅读文章
        meta: { requiresAuth: false }
      }
    ]
  }
]

那么如何访问这个 meta 字段呢?

首先,我们称呼 routes 配置中的每个路由对象为 路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,它可能匹配多个路由记录。

例如,根据上面的路由配置,/posts/new 这个 URL 将会匹配父路由记录 (path: '/posts') 以及子路由记录 (path: 'new')。

一个路由匹配到的所有路由记录会暴露为 $route 对象(还有在导航守卫中的路由对象)的$route.matched 数组。我们需要遍历这个数组来检查路由记录中的 meta 字段,但是 Vue Router 还为你提供了一个 $route.meta 方法,它是一个非递归合并**所有 **meta 字段的(从父字段到子字段)的方法。这意味着你可以简单地写

router.beforeEach((to, from) => {
  // 而不是去检查每条路由记录
  // to.matched.some(record => record.meta.requiresAuth)
  if (to.meta.requiresAuth && !auth.isLoggedIn()) {
    // 此路由需要授权,请检查是否已登录
    // 如果没有,则重定向到登录页面
    return {
      path: '/login',
      // 保存我们所在的位置,以便以后再来
      query: { redirect: to.fullPath },
    }
  }
})

6.13过渡动效

6.14滚动行为

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

注意: 这个功能只在支持 history.pushState 的浏览器中可用。

const router = createRouter({
  history: createWebHashHistory(),
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
  }
})

6.15路由懒加载

// 将
// import UserDetails from './views/UserDetails.vue'
// 替换成
const UserDetails = () => import('./views/UserDetails.vue')

const router = createRouter({
  // ...
  routes: [{ path: '/users/:id', component: UserDetails }],
})

在Vite中,你可以在rollupOptions下定义分块:

// vite.config.js
export default defineConfig({
  build: {
    rollupOptions: {
      // https://rollupjs.org/guide/en/#outputmanualchunks
      output: {
        manualChunks(id) {
          // 将pinia的全局库实例打包进vendor,避免和页面一起打包造成资源重复引入
          if (id.includes(path.resolve(__dirname, '/src/store/index.ts'))) {
            return 'vendor';
          }
          // tdesign,单独拆包
          else if (id.includes('node_modules/tdesign-vue-next')) {
            return 'tdesign-vue-next';
          }
        },
      },
    },
  },
})

6.16 导航故障

6.17 动态路由