Vue3复习和总结(vue-router)

代码全部放在->github仓库:https://github.com/zyugat/vue-review-all

前言:分为base、Component、router、Vuex、组合式API。5个部分。

base和Component中的例子不使用脚手架运行,需自行浏览器运行。位置->noCli


router目录:router/index.js、页面:views

当切换页面时,是直接销毁页面

创建router流程:创建router/index.js文件编写配置,创建router-link标签用于跳转页面,创建router-view用于显示对应组件。

目录:

  • base

    • router-link:链接;router-view:显示URL对应组件;children:嵌套路由
  • 路由传参

    • params、query
  • 路由匹配正则

    • 匹配所有内容、匹配xxx开头内容、可重复次数、可选参数
  • 命名路由与视图

    • 命名路由:name属性;命名视图:同时/同级展示多个视图
  • 重定向和别名和props

    • 重定向:redirect、别名:alias
    • props四种模式:布尔模式、命名视图、对象模式、函数模式
  • 导航守卫

    • 全局前置守卫、全局解析守卫、全局后置守卫、路由独享守卫
    • 渲染组件前守卫、组件复用时守卫、离开组件时守卫
  • 元信息

    • 获取meta字段:$router.meta(过渡名称、谁可以访问路由)
  • 滚动行为

    • scrollBehavior(to, from, savedPosition){return{}}
    • el传递CSS选择器或DOM元素、返回savedPosition视为像原生浏览器前进后退、滚动到锚点、延迟滚动
    • router和route区别、懒加载
    • 路由跳转命令、Hash模式、HTML5模式
    • 数据获取:导航完成后获取数据、在导航完成前获取数据
    • 组件内容传递:必须使用 v-slot API 将其传递给 <component>

router-link

创建一个链接

tag:指定渲染成任意组件
replace:页面跳转的时候禁止返回
active-class:对应的路由匹配成功时, 会自动给当前元素设置class

<router-link to="/test1" tag="button" replace active-class="my-active">test1</router-link>
// router/index.js
  const router = createRouter({
  history: createWebHistory(),
  routes,
  linkActiveClass: 'my-active'
})

router-view

显示与url对应的组件。

<router-view></router-view>

router/index.js

// 1. 定义路由组件.
// 也可以从其他文件导入
// import Home from '../components/Home'
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. 创建并挂载根实例
const app = Vue.createApp({})
//确保 _use_ 路由实例使
//整个应用支持路由。
app.use(router)

app.mount('#app')

嵌套路由:使用:children:[{...},{...}]

{
  path: '/test1',
  name: 'test1',
  component: () => import('../components/home'),
  children: [
    {
      path: '',
      // 重定向
      redirect: '/test1/children1'
    },
    {
      path: 'children1',
      component: () => import('../components/children1')
    },
    {
      path: 'children2',
      component: () => import('../components/children2')
    }
  ]
}

路由传参

如果使用带参数的路由,从api/123api/321,因为使用相同的组件实例所以会被复用。意味着生命周期钩子不会被调用。

params:修改路由地址:path:'/params/:abc'

query:修改跳转链接::to="{ path: '/api', query: { name: '我是query数据' } }"

<template>
  <h2>Api</h2>
  <p>路由传参 params:{{ $route.params.abc }}</p>
  <p>路由传参 query:{{ $route.query.name }}</p>
</template>
<div id="router">
  <router-link to="/api/我是params数据">Api-路由传参params</router-link
  ><br />
  <router-link :to="{ path: '/api', query: { name: '我是query数据' } }">
    Api-query
  </router-link>
</div>
const routes = [
  { path: '/api', component: () => import('../views/api') },
  { path: '/api/:abc', component: () => import('../views/api') },
]

image-20211113175341514

image-20211113175356984


路由匹配正则

括号中加入正则表达式

1、将匹配所有内容:path: '/:pathMatch(.*)',并将其放在 $route.params.pathMatch

image-20211113175257422

2、匹配user-开头的路由:path: '/user-:afterUser(.*)',并将匹配内容放在$route.params.afterUser

image-20211113175612451

3、可重复参数:path: '/user-:afterUser(.*)*',最后面加了一个*,意思是会匹配后续所有->user-123456/123/456,返回值是数组。(*->0个或多个。+->1个或多个)

image-20211113180256087

4、可选:?,0个或1个

{ path: '/:pathMatch(.*)', component: () => import('../views/api') },
{ path: '/user-:afterUser(.*)', component: () => import('../views/api') },
{ path: '/user-:afterUser(.*)*', component: () => import('../views/api') },
<p>路由匹配正则表达式 /:pathMatch(.*):{{ $route.params.pathMatch }}</p>
<p>路由匹配正则表达式 /user-:afterUser(.*):{{ $route.params.afterUser }}</p>

命名路由与视图

命名路由:name

  • 没有硬编码的 URL
  • params 的自动编码/解码。
  • 防止你在 url 中出现打字错误。
  • 绕过路径排序(如显示一个)

router.push({ name: 'user', params: { username: 'erina' } })

const routes = [
  {
    path: '/user/:username',
    name: 'user',
    component: User
  }
]
<router-link :to="{ name: 'user', params: { username: 'erina' }}">
  User
</router-link>

命名视图

同时/同级展示多个视图。不是嵌套。

例子中:我希望main组件一直都在中间,切换路由时我希望Left与Right交换位置。

{
  path: '/nameLeft',
  components: {
    default: left,
    main,
    change: right,
  },
},
{
  path: '/nameRight',
  components: {
    default: right,
    main,
    change: left,
  },
},
<router-link to="/nameLeft">left</router-link>——————
<router-link to="/nameRight">Right</router-link>

<router-view />
<router-view name="main" />
<router-view name="change" />

image-20211113183439347

image-20211113183504238


重定向和别名和props

重定向:redirect

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

别名:alias:''

当访问/home时也是访问/页面。

const routes = [{ path: '/', component: Homepage, alias: '/home' }]

开启Props:props: true

props分为四种模式:布尔模式、命名视图、对象模式、函数模式

在路由组件中我们一般是:$route.params.id,获取到属性。现在我们只需要使用props即可。

1、布尔模式

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const routes = [{ path: '/user/:id', component: User, props: true }]

2、命名视图:则需要为每个命名视图定义一个props配置

const routes = [
  {
    path: '/user/:id',
    components: { default: User, sidebar: Sidebar },
    props: { default: true, sidebar: false }
  }
]

3、对象模式:

props: { newsletterPopup: false }

4、函数模式:用于将参数转换为其他类型

例如:URL/search?q=vue将传递{query: 'vue'}作为props传为组件。

props: route => ({ query: route.query.q })

导航守卫

要注意,全局守卫是写在vue-router文件里面

守卫有next,钩子没有。

全局守卫有三个:router.beforeEach全局前置守卫、router.beforeResolve全局解析守卫、router.afterEach全局后置钩子

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局router.beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局router.beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局router.afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
  • to: Route: 即将要进入的目标路由对象
  • from: Route: 当前导航正要离开的路由
  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
    • next():执行下一个钩子
    • next(false):中断当前的导航
    • next('/')next({path: '/'}):跳转不同导航
    • next(error):传入一个Error的实例,导航会被中断且传递错误给router.onError()注册过的回调

路由流程:

router.beforeEach(to, from, next):全局前置守卫

跳转路由之前都会调用这个回调。

router.beforeResolve(to, from, next):全局解析守卫

是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

router.afterEach(to, from):全局后置钩子

路由跳转后的回调。没有next就无法改变导航本身,故用于分析、更改页面标题、声明页面等辅助功能。

beforeEnter(to, from, next):路由独享的守卫

只在进入路由时触发,不会在 paramsqueryhash 改变时触发,在路由配置上直接定义。

{
  path: '/foo',
  component: Foo,
  beforeEnter: (to, from, next) => {
    // ...
  }
}

组件内部守卫:

beforeRouteEnter(to, from, next):渲染组件前。

不能访问组件实例 this,因为实例还未创建

beforeRouteUpdate(to, from, next):组件复用时。

可以访问组件实例 this

beforeRouteLeave(to, from, next):离开组件时。

可以访问组件实例 this

元信息

过渡名称、谁可以访问路由等,可以使用meta属性实现。

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

1、首先 routers 配置中每个路由对象为 路由记录,根据上面的路由配置,/foo/bar 这个 URL 将会匹配父路由记录以及子路由记录。

2、路由匹配到的所有路由记录会暴露为:$route 对象的 $route.matched 数组。(第四行)

3、通过遍历数组检测路由记录的 meta 字段。但是Vue Router中可以使用 $route.meta,非递归合并所有 meta 字段的(从父字段到子字段)的方法。

总结:通过$router.meta获取meta字段

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

滚动行为

const router = createRouter({
  history: createWebHashHistory(),
  routes: [...],
  scrollBehavior(to, from, savedPosition) {
    // 始终滚动到顶部
    return { top: 0 }
  },
})

2、通过 el传递CSS选择器或DOM元素,top和left视为偏移量

const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    // 始终在元素 #main 上方滚动 10px
    return {
      // 也可以这么写
      // el: document.getElementById('main'),
      el: '#main',
      top: -10,
    }
  },
})

3、返回false值或空对象则不变化,返回 savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样:

const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
  },
})

4、滚动到锚点:

const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    if (to.hash) {
      return {
        el: to.hash,
      }
    }
  },
})

5、延迟滚动,返回一个Promise

const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ left: 0, top: 0 })
      }, 500)
    })
  },
})

router与route区别

router是指向Vue实例的router,而route他永远是指向当前活跃的对象,当按钮被点击时(此时按钮正处于活跃),就返回按钮信息。

懒加载,把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件:const home = () => import('../components/home')

同时component (和 components) 配置接收一个返回 Promise 组件的函数,Vue Router 只会在第一次进入页面时才会获取这个函数,然后使用缓存数据。

const UserDetails = () =>
  Promise.resolve({
    /* 组件定义 */
  })

路由跳转this.$router.push('/')

Hash模式:history: createWebHashHistory(),

HTML5模式:history: createWebHistory(),

数据获取

1、导航完成后获取数据:在created钩子中创建watch监听this.$route.params路由属性。当发送改变时获取数据。

2、在导航完成前获取数据:在beforeRouteEnter守卫(渲染组件前调用),中获取数据。

  beforeRouteEnter (to, from, next) {
    getPost(to.params.id, (err, post) => {
      next(vm => vm.setData(err, post))
    })
  },
  // 路由改变前,组件就已经渲染完了
  // 逻辑稍稍不同
  beforeRouteUpdate (to, from, next) {
    this.post = null
    getPost(to.params.id, (err, post) => {
      this.setData(err, post)
      next()
    })
  },
  methods: {
    setData (err, post) {
      if (err) {
        this.error = err.toString()
      } else {
        this.post = post
      }
    }
  }

组件内容传递

你必须使用 v-slot API 将其传递给 <component>

<router-view v-slot="{ Component }">
  <component :is="Component">
    <p>In Vue Router 3, I render inside the route component</p>
  </component>
</router-view>
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐