Vue3.0全家桶最全入门指南 - vue-router@4.x和vuex@4.x (3/4)

50,375 阅读4分钟

本系列文章目录

三、vue-router@4.xvuex@4.x

vue2.x使用的是vue-router@3.xvuex@3.x,vue3.x使用的是vue-router@4.xvuex@4.x

这里要避免跟vue3.x的版本号混合了,其实vue3.x使用的routervuex都是4.x。

这里为了方便理解,统一使用vue2.x routervue3.x router代替****vue-router@3.xvue-router@4.x,统一使用vue2.x vuexvue3.x vuex代替****vuex@3.xvuex@4.x

1、vue-router@4.x

创建实例

// vue2.x router
import Vue from 'vue'
import Router from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

Vue.use(Router)

const router = new Router({
  base: process.env.BASE_URL,
  mode: 'history',
  scrollBehavior: () => ({ y: 0 }),
  routes
})

export default router

// vue3.x router
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

scrollBehavior滚动行为

vue3.x router弃用了vue2.x router中的 { selector, x, y, offset },使用{ el, left, top, behavior }代替,新的api语义更接近原生DOM

// vue2.x router
const router = new Router({
  base: process.env.BASE_URL,
  mode: 'history',
  scrollBehavior: () => ({ x: 0, y: 0 }),
  routes
})

// vue3.x router
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
  scrollBehavior(to, from, savedPosition) {
    // scroll to id `#app` + 200px, and scroll smoothly (when supported by the browser)
    return {
      el: '#app',
      top: 0,
      left: 0,
      behavior: 'smooth'
    }
  }
})

路由组件跳转

vue2.x使用路由选项redirect设置路由自动调整,vue3.x中移除了这个选项,将在子路由中添加一个空路径路由来匹配跳转

// vue2.x router
[
  {
    path: '/',
    component: Layout,
    name: 'WebHome',
    meta: { title: '平台首页' },
    redirect: '/dashboard', // 这里写跳转
    children: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        meta: { title: '工作台' },
        component: () => import('../views/dashboard/index.vue')
      }
    ]
  }
]

// vue3.x router
[
  {
    path: '/',
    component: Layout,
    name: 'WebHome',
    meta: { title: '平台首页' },
    children: [
      { path: '', redirect: 'dashboard' }, // 这里写跳转
      {
        path: 'dashboard',
        name: 'Dashboard',
        meta: { title: '工作台' },
        component: () => import('../views/dashboard/index.vue')
      }
    ]
  }
]

捕获所有路由:/:catchAll(.*)

捕获所有路由 ( /* ) 时,现在必须使用带有自定义正则表达式的参数进行定义:/:catchAll(.*)

// vue2.x router
const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '/user/:a*' },
  ]
})

// vue3.x router
const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/user/:a:catchAll(.*)', component: component },
  ]
})

当路由为 /user/a/b 时,捕获到的 params{"a": "a", "catchAll": "/b"}

router.resolve

router.matchrouter.resolve 合并在一起为 router.resolve,但签名略有不同

// vue2.x router
...
resolve ( to: RawLocation, current?: Route, append?: boolean) {
  ...
  return {
    location,
    route,
    href,
    normalizedTo: location,
    resolved: route
  }
}

// vue3.x router
function resolve(
    rawLocation: Readonly<RouteLocationRaw>,
    currentLocation?: Readonly<RouteLocationNormalizedLoaded>
  ): RouteLocation & { href: string } {
  ...
  let matchedRoute = matcher.resolve(matcherLocation, currentLocation)
  ...
  return {
    fullPath,
    hash,
    query: normalizeQuery(rawLocation.query),
    ...matchedRoute,
    redirectedFrom: undefined,
    href: routerHistory.base + fullPath,
  }
}

获取当前路由

删除 router.getMatchedComponents,可以从 router.currentRoute.value.matched 中获取

router.getMatchedComponents 返回目标位置或是当前路由匹配的组件数组 (是数组的定义/构造类,不是实例)。通常在服务端渲染的数据预加载时使用。
[{
  aliasOf: undefined
  beforeEnter: undefined
  children: []
  components: {default: {}, other: {}}
  instances: {default: null, other: Proxy}
  leaveGuards: []
  meta: {}
  name: undefined
  path: "/"
  props: ƒ (to)
  updateGuards: []
}]

使用

如果使用 <transition>,则可能需要等待 router 准备就绪才能挂载应用程序

app.use(router)
// Note: on Server Side, you need to manually push the initial location
router.isReady().then(() => app.mount('#app'))

一般情况下,正常挂载也是可以使用 <transition> 的,但是现在导航都是异步的,如果在路由初始化时有路由守卫,则在 resolve 之前会出现一个初始渲染的过渡,就像给 <transiton> 提供一个 appear 一样

在服务端渲染 (SSR) 中,需要传递合适的 history mode

const history = isServer ? createMemoryHistory() : createWebHistory()
const router = createRouter({ routes, history })
// on server only
router.push(req.url) // request url
router.isReady().then(() => {
  // resolve the request
})

push 或者 resolve 一个不存在的命名路由时,将会引发错误,而不是导航到根路由 "/" 并且不显示任何内容

在 vue2.x router 中,当 push 一个不存在的命名路由时,路由会导航到根路由 "/" 下,并且不会渲染任何内容。浏览器控制台只会打印警告,并且 url 会跳转到根路由 / 下。

const router = new VueRouter({
  mode: 'history',
  routes: [{ path: '/', name: 'foo', component: Foo }]
}
this.$router.push({ name: 'baz' })

在 vue3.x router 中,同样做法会引发错误。

const router = createRouter({
  history: routerHistory(),
  routes: [{ path: '/', name: 'foo', component: Foo }]
})
...
import { useRouter } from 'vue-next-router'
...
const router = userRouter()
router.push({ name: 'baz' })) // 这行代码会报错

获取路由

网上一些教程会告诉你通过ctx访问routerstore对象,但是其实这种方式只能在develement模式有效,在production环境编译后,ctx在develement下看到的属性都无法访问,容易误导大家

错误示例:

import { getCurrentInstance } from 'vue'

export default {
  setup () {
    const { ctx } = getCurrentInstance()
    console.log(ctx)
    console.log(ctx.$router.currentRoute.value)
    const userId = computed(() => ctx.$store.state.app.userId)
    return {
      userId
    }
  }
}

正确使用:

import { getCurrentInstance } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'

export default {
  setup () {
    const { ctx } = getCurrentInstance()
    console.log(ctx)
    const router = useRouter()
    const route = useRoute()
    const store = userStore()
    console.log(router, route, store)
    console.log(router.currentRoute.value)
    const userId = computed(() => store.state.app.userId)
    return {
      userId
    }
  }
}

2、vuex@4.x

vuex4.x很少breaking change,整体改动较少

创建实例

// vue2.x vuex
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  getters: {},
  modules: {}
}

// vue3.x vuex
import Vuex from 'vuex'

export default Vuex.createStore({
  state: {},
  mutations: {},
  actions: {},
  getters: {},
  modules: {}
})

获取store

// vue3.x vuex
import { getCurrentInstance } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'

export default {
  setup () {
    const { ctx } = getCurrentInstance()
    console.log(ctx)
    const router = useRouter()
    const route = useRoute()
    const store = userStore()
    console.log(router, route, store)
    console.log(router.currentRoute.value)
    const userId = computed(() => store.state.app.userId)
    return {
      userId
    }
  }
}

本系列文章目录