Vite3+Vue3+TypeScript+Element Plus (十五) 搭建企业级轻量框架实践

755 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天

前言

Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。

🍏推荐阅读

Vue Router

功能包括:

  • 嵌套路由映射
  • 动态路由选择
  • 模块化、基于组件的路由配置
  • 路由参数、查询、通配符
  • 展示由 Vue.js 的过渡系统提供的过渡效果
  • 细致的导航控制
  • 自动激活 CSS 类的链接
  • HTML5 history 模式或 hash 模式
  • 可定制的滚动行为
  • URL 的正确编码

安装

# PNPM
pnpm i vue-router -S

本地国际化配置

新增函数:

  • $t:此函数只是配合i18n Ally插件来进行国际化智能提示,并无实际意义(只对提示起作用)
  • transformI18n:此函数国际化转换工具函数(自动读取根目录locales文件夹下文件进行国际化匹配)
// i18n/index.ts
import { App, WritableComputedRef } from 'vue'
import { type I18n, createI18n } from 'vue-i18n'

// 加载国际化配置
function siphonI18n(prefix = 'zh-CN') {
  return Object.fromEntries(
    Object.entries(import.meta.glob('~/locales/*.y(a)?ml', { eager: true })).map(
      ([key, value]: any) => {
        const matched = key.match(/([A-Za-z0-9-_]+)\./i)[1]
        return [matched, value.default]
      }
    )
  )[prefix]
}

export const messages = {
  zh: {
    ...siphonI18n('zh-CN'),
  },
  en: {
    ...siphonI18n('en'),
  },
}

/**
 * 国际化转换工具函数(自动读取根目录locales文件夹下文件进行国际化匹配)
 * @param message message
 * @returns 转化后的message
 */
export function transformI18n(message: any = '') {
  if (!message) {
    return ''
  }

  // 处理存储动态路由的title,格式 {zh:"",en:""}
  if (typeof message === 'object') {
    const locale: string | WritableComputedRef<string> | any = i18n.global.locale
    return message[locale?.value]
  }

  const key = message.match(/(\S*)\./)?.[1]
  if (key && Object.keys(siphonI18n('zh-CN')).includes(key)) {
    return i18n.global.t.call(i18n.global.locale, message)
  } else if (!key && Object.keys(siphonI18n('zh-CN')).includes(message)) {
    // 兼容非嵌套形式的国际化写法
    return i18n.global.t.call(i18n.global.locale, message)
  } else {
    return message
  }
}

/** 此函数只是配合i18n Ally插件来进行国际化智能提示,并无实际意义(只对提示起作用),如果不需要国际化可删除 */
export const $t = (key: string) => key

const i18n: I18n = createI18n({
  legacy: false,
  locale: 'zh',
  fallbackLocale: 'en',
  messages,
})

export function setupI18n(app: App) {
  app.use(i18n)
  return i18n
}

路由配置

// router/index.ts
import type { AppRouteRecordRaw } from '/#/router'
import { createRouter, Router, RouteRecordRaw } from 'vue-router'
import { getHistoryMode } from '@/utils'
import NProgress from '@/utils/progress'
import { $t, transformI18n } from '@/i18n'
import Layout from '@library/layouts/index.vue'

// 根路由
export const ROOT_ROUTE: AppRouteRecordRaw = {
  path: '/',
  name: 'Root',
  meta: {
    title: $t('menus.home'),
    breadcrumbHidden: true,
  },
}

// login on a page
export const LOGIN_ROUTE: AppRouteRecordRaw = {
  path: '/login',
  name: 'Login',
  component: () => import('@/views/login/Login.vue'),
  meta: {
    title: $t('menus.login'),
  },
}

// 404 on a page
export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
  path: '/:path(.*)*',
  name: 'PageNotFound',
  component: Layout,
  meta: {
    title: $t('menus.errorPage'),
    breadcrumbHidden: true,
    hidden: true,
  },
  children: [
    {
      path: '/:path(.*)*',
      name: 'PageNotFound',
      component: () => import('@/views/error/404.vue'),
      meta: {
        title: $t('menus.pageNotFound'),
        breadcrumbHidden: true,
        hidden: true,
      },
    },
  ],
}

// 未经许可的基本路由
export const constantRoutes: AppRouteRecordRaw[] = [LOGIN_ROUTE, PAGE_NOT_FOUND_ROUTE]

/** 路由白名单 */
const routesWhiteList = ['/login']

/** 创建路由实例 */
const router: Router = createRouter({
  history: getHistoryMode(),
  routes: constantRoutes as RouteRecordRaw[],
  // 是否应该禁止尾部斜杠。默认为false
  strict: true,
  scrollBehavior(to, from, savedPosition) {
    return new Promise((resolve) => {
      if (savedPosition) {
        return savedPosition
      } else {
        if (from.meta.saveSrollTop) {
          const top: number = document.documentElement.scrollTop || document.body.scrollTop
          resolve({ left: 0, top })
        }
      }
    })
  },
})

/** 权限认证 */
function setupPermissions(router: Router) {
  router.beforeEach(async (to, from, next) => {
    NProgress.start()
    // 暂时设置为未登录
    const hasToken = false
    if (hasToken) {
    } else {
      if (to.path !== '/login') {
        if (routesWhiteList.includes(to.path)) {
          next()
        } else {
          next({ path: '/login', replace: true })
        }
      } else {
        next()
      }
    }
  })
  router.afterEach(async (to: any) => {
    if (to.meta.title) {
      document.title = `${transformI18n(to.meta.title)}`
    }
    if (NProgress.status) NProgress.done()
  })
}

/** 加载路由配置 */
export const setupRouter = (app: any) => {
  setupPermissions(router)
  app.use(router)
  return router
}

export default router

挂载

在main.ts中引入router并挂载,代码如下:

import 'vue-global-api'
import App from './App.vue'
import { createApp } from 'vue'
import { bootstrap } from '~/library'
import { setupI18n } from '@/i18n'
import { setupStore } from '@/store'
import { setupRouter } from '@/router'
const app = createApp(App)
bootstrap(app)
setupI18n(app)
setupStore(app)
setupRouter(app)
  .isReady()
  .then(() => app.mount('#app'))

App.vue

<script setup lang="ts" name="App"></script>
<template>
  <el-config-provider>
    <router-view />
  </el-config-provider>
</template>

代码已经提交到Gitee

继续学习

废话只说一句:码字不易求个👍,收藏 === 学会,快行动起来吧!🙇‍🙇‍🙇‍。

# Vite+Vue3+TypeScript+Element (十六) 搭建企业级轻量框架实践(明天更新)