Skip to content

vue-router ​

安装配置 ​

> pnpm install vue-router@4

路由配置结构 ​

  • index.ts
// index.ts
import { createRouter, createWebHistory } from 'vue-router';

import routesList from './routes';

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  scrollBehavior: () => ({ top: 0 }),
  routes: routesList
});

export default router;
  • routes.ts
import type { RouteRecordRaw } from 'vue-router';

import { routerAccount } from './modules/account';

const routesList: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'index',
    component: () => import('@/views/index.vue'),
    meta: {
      index: 0
    }
  },
  ...routerAccount(),
  {
    path: '/:catchAll(.*)',
    name: 'catchError',
    component: () => import('@/views/catch-error/error.vue'),
    meta: {
      index: 9999
    }
  }
];
  • modules/account.ts
export function routerAccount() {
  const route = [
    {
      path: '/account',
      name: 'account',
      meta: {
        index: 200
      },
      children: [
        {
          path: 'enroll',
          name: 'accountEnroll',
          component: () => import('@/views/account/enroll.vue'),
          meta: {
            index: 210
          }
        }
      ]
    }
  ];

  return route;
}

API接口 ​

// 第三方资源类库或插件
import { 
  RouterLink,
  RouterView,
  START_LOCATION,
  createMemoryHistory,
  createRouter, 
  createRouterMatcher, 
  createWebHashHistory,
  createWebHistory, 
  isNavigationFailure,
  matchedRouteKey,
  onBeforeRouteLeave,
  onBeforeRouteUpdate,
  parseQuery, 
  routeLocationKey,
  routerKey,
  routerViewLocationKey, 
  stringifyQuery,
  useLink,
  useRoute,
  useRouter,
  viewDepthKey, 
} from 'vue-router';


/**
 * @当前业务
 * 具体逻辑实现代码
 */
const { proxy: { $nextTick, $forceUpdate, $slots } } = getCurrentInstance();

const router = useRouter(); // 返回路由器实例。相当于在模板中使用 $router。
const route = useRoute(); // 返回当前的路由地址。相当于在模板中使用 $route。

// 当前路由
console.log(route.path);

onMounted(() => {
  $forceUpdate()
}

$nextTick(() => {
  //
})

onBeforeRouteUpdate(async (to, from) => {

});

onBeforeRouteLeave(async (to, from) => {
  //
});

// 路由
const switchPath = (path: string) => {
  router.push({ path });
};

路由按需加载(懒加载) ​

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载,把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。

const routes: Array<RouteRecordRaw> = [
  {
    path: '/article',
    component: import(/* webpackChunkName: "index" */ '@/components/index.vue')
  }
];

动态路由 ​

const routes: Array<RouteRecordRaw> = [
  {
    path: '/article/:id',
    component: Article
  }
];

完整的导航解析流程 ​

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

beforeunload 事件 ​

当浏览器窗口关闭或者刷新时,会触发 beforeunload 事件。当前页面不会直接关闭,可以点击确定按钮关闭或刷新,也可以取消关闭或刷新。

window.addEventListener('beforeunload', (event) => {
  // Cancel the event as stated by the standard.
  event.preventDefault();
  // Chrome requires returnValue to be set.
  event.returnValue = '';
});

路由转场动画 ​

<template>
  <div class="com-frame component-container">
    <router-view v-slot="{ Component }" class="com-frame router-view">
      <keep-alive>
        <transition name="el-zoom-in-center" mode="out-in">
          <component :is="Component" />
        </transition>
      </keep-alive>
    </router-view>
  </div>
</template>

<script setup lang="ts">
// 第三方资源类库或插件
import { ref, watchEffect, watch } from "vue";
import { RouterView, useRouter, useRoute } from 'vue-router';

/**
 * @当前业务
 * 具体逻辑实现代码
 */
const router = useRouter();
const route = useRoute();


watch(router.currentRoute, (newValue, oldValue) => {
  console.info('路由: ', newValue, oldValue);
}, { deep: true });
</script>

<style lang="scss" scoped>
</style>

路由转场动画 ​

路由进入与离开,前进与后退转场切换动画。

<template>
  <div id="fujinhuo" class="com-frame com-flexh">
    <!--旧语法-->
    <transition :name="swipeAnimate">
      <router-view class="router-view"></router-view>
    </transition>

    <!--Vue3语法-->
    <router-view v-slot="{ Component }">
      <transition :name="swipeAnimate">
        <keep-alive>
          <component :is="Component" />
        </keep-alive>
      </transition>
    </router-view>
  </div>
</template>

<script>
  export default {
    name: 'fujinhuo',
    data() {
      return {
        routeList: [
          { route: '/message/index', icon: '&#xe670;', text: '消息', active: false, css: 'font-size:.17rem;'},
          { route: '/goods/index', icon: '&#xe63a;', text: '购物', active: false, css: 'font-size:.18rem;'},
          { route: '/nearby/index', icon: '&#xe688;', text: '附近', active: false, css: 'font-size:.25rem;'},
          { route: '/activity/index', icon: '&#xe6a6;', text: '活动', active: false, css: 'font-size:.18rem;'},
          { route: '/account/index', icon: '&#xe676;', text: '我的', active: false, css: 'font-size:.18rem;'}
        ],
        swipeAnimate: ""
      }
    },
    components: {},
    watch: {
      $route(to, from) {
        this.routeList.forEach((idx)=> {
          idx.active = false;
          if (to.path == idx.route) {
            idx.active = true;
          }
        });

        //路由转场动画
        if (typeof to.meta.index !== 'number' || typeof from.meta.index !== 'number') {
          return ;
        }
        if (to.meta.index > from.meta.index) {
          this.swipeAnimate = "slide-left";
          return;
        }
        this.swipeAnimate = "slide-right";
      }
    },
    methods: {
      changeItem(dx) {
        this.$router.push({
          path: dx.route
        });
      }
    }
  }
</script>

<style type="text/css">
  @use "~@/assets/css/default.css";
  @use "~@/assets/css/public.css";


  .slide-right-enter-active, .slide-right-leave-active, .slide-left-enter-active, .slide-left-leave-active {
    will-change: transform;
    transition: all 200ms;
    position: absolute;
  }
  .slide-right-enter {
    opacity: 0;
    transform: translate3d(-100%, 0, 0);
  }
  .slide-right-leave-active {
    opacity: 0;
    transform: translate3d(100%, 0, 0);
  }
  .slide-left-enter {
    opacity: 0;
    transform: translate3d(100%, 0, 0);
  }
  .slide-left-leave-active {
    opacity: 0;
    transform: translate3d(-100%, 0, 0);
  }
</style>

Released under the MIT License.