Files
blog-press/docs/Web-Front/Vue/Vue3-Common.md
2025-12-15 23:25:25 +08:00

11 KiB
Raw Blame History

一、依赖管理

  本项目会传递安装的依赖有:

包名称 版本 含义和用途说明
@fortawesome/fontawesome-free ^6.7.2 FontAwesome 图标库的免费版本
@popperjs/core ^2.11.8 工具提示和弹出框定位引擎
axios ^1.4.0 基于 Promise 的 HTTP 客户端
compressorjs ^1.2.1 纯 JavaScript 图片压缩库
crypto-js ^4.2.0 JavaScript 加密算法库
dayjs ^1.11.13 轻量级的日期处理库
echarts ^5.5.1 百度开源的数据可视化图表库
element-plus ^2.6.0 基于 Vue 3 的桌面端 UI 组件库
js-cookie ^3.0.5 JavaScript Cookie 操作库
lunar-calendar ^0.1.4 农历日历转换库
lunar-javascript ^1.6.13 农历日期处理的 JavaScript 库
path-browserify ^1.0.1 Node.js path 模块的浏览器版本兼容实现
qs ^6.13.0 URL 查询字符串解析和序列化库
v-calendar ^3.1.2 Vue.js 的日历和日期选择器组件

::: tip 如果使用pnpm安装会传递peerDependencies部分。 :::

::: warning 建议将Vite、Typescript、@types等构建工具依赖放在devDependencies中。 :::

二、vite.config.ts配置

2.1 生成Typescript类型文件

安装vite-plugin-dts插件

pnpm add vite-plugin-dts -D
import dts from 'vite-plugin-dts'

// 打包输出文件夹
const outDirPath = 'dist'
// 需要打包的类型文件
const TARGET_TYPE_FOLDERS = ['src/components', 'src/utils', 'src/types', 'src/vue3-common.ts']

export default defineConfig({
  plugins: [
    // 生成类型文件
    dts({
      // 需要处理的文件
      include: TARGET_TYPE_FOLDERS,
      // 使用特定的 tsconfig 配置
      tsconfigPath: path.resolve(__dirname, 'tsconfig.app.json'),
      // 类型文件输出目录
      outDir: path.resolve(__dirname, outDirPath)
    })
  ],
})

打包后会在dist目录生成.d.ts文件提供完整的Typescript类型支持。

2.2 build打包配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import dts from 'vite-plugin-dts'
import path from 'path'
import fs from 'fs'
import { ICommonObj } from './src/types'

// 打包输出文件夹
const outDirPath = 'dist'
// 需要单个打包的文件夹(组件和工具类)
const TARGET_LIB_FOLDERS = ['src/components', 'src/utils']
// 需要排除的第三方依赖
const EXTERNAL = ['vue', 'element-plus', 'echarts', 'axios', 'moment', 'crypto-js', 'spark-md5', 'path-browserify']

/**
 * 递归读取文件夹下的所有文件
 * @param folderPath 文件夹
 */
const getFilesFromFolder = (folderPath: string) => {
  const files: string[] = []

  // 读取文件夹中的内容
  fs.readdirSync(folderPath).forEach((item: string) => {
    const fullPath = path.join(folderPath, item)
    // 读取文件状态 如果是文件夹,递归读取
    if (fs.statSync(fullPath).isDirectory()) {
      files.push(...getFilesFromFolder(fullPath))
    } else {
      // 否则添加至文件列表
      files.push(fullPath)
    }
  })

  return files
}

// 动态生成入口文件
const inputEntries = TARGET_LIB_FOLDERS.reduce((entries: ICommonObj, folder) => {
  // 获取文件夹内的所有文件
  const files = getFilesFromFolder(path.resolve(__dirname, folder))

  files.forEach((filePath) => {
    // 判断文件是否是 .vue 或 .ts 文件
    if (filePath.endsWith('.vue') || filePath.endsWith('.ts')) {
      // 获取相对路径并保持目录结构
      const relativePath = path.relative('src', filePath)
      const entryName = path.join('lib', relativePath.replace(/\.(vue|ts)$/, ''))

      entries[entryName] = filePath // 为每个文件创建一个入口
    }
  })

  return entries
}, {})

// https://vitejs.dev/config/
export default defineConfig({
  build: {
    // lib文件配置
    lib: {
      // 入口文件
      entry: path.resolve(__dirname, 'src/vue3-common.ts'),
      formats: ['es'],
      name: 'vue3-common',
      // 文件名
      fileName: (format) => `vue3-common.${format}.js`
    },
    // 输出文件路径
    outDir: outDirPath,
    // 是否将css文件分割
    cssCodeSplit: true,
    // Rollup打包配置
    rollupOptions: {
      // 需要排除的依赖 通常为第三方库
      external: EXTERNAL,
      // 输入配置
      input: {
        // 入口文件
        'vue3-common': path.resolve(__dirname, 'src/vue3-common.ts'),
        // 其余需要单独打包的文件
        ...inputEntries
      },
      // 输出配置
      output: {
        dir: path.resolve(__dirname, outDirPath),
        // 入口文件
        entryFileNames: '[name].js',
        // chunk文件
        chunkFileNames: 'lib/[name].js',
        // 资源文件
        assetFileNames: 'styles/[name].[ext]'
      }
    },
    // 是否压缩代码
    minify: false
  }
})

打包后会在dist目录生成源码文件。

三、package.json配置

3.1 导出路径

{
  "exports": {
    ".": {
      "import": "./dist/vue3-common.js",
      "require": "./dist/vue3-common.js",
      "types": "./dist/vue3-common.d.ts"
    },
    "./components/SvgIcon.vue": {
      "import": "./dist/lib/components/SvgIcon.js",
      "types": "./dist/components/SvgIcon.vue.d.ts"
    },
    "./styles/SvgIcon.css": "./dist/styles/SvgIcon.css",
    "./components/LoginForm.vue": {
      "import": "./dist/lib/components/LoginForm.js",
      "types": "./dist/components/LoginForm.vue.d.ts"
    },
    "./styles/LoginForm.css": "./dist/styles/LoginForm.css",
    "./components/MenuItem.vue": {
      "import": "./dist/lib/components/MenuItem.js",
      "types": "./dist/components/MenuItem.vue.d.ts"
    },
    "./components/MenuList.vue": {
      "import": "./dist/lib/components/MenuList.js",
      "types": "./dist/components/MenuList.vue.d.ts"
    },
    "./components/Hamburger.vue": {
      "import": "./dist/lib/components/Hamburger.js",
      "types": "./dist/components/Hamburger.vue.d.ts"
    },
    "./styles/Hamburger.css": "./dist/styles/Hamburger.css",
    "./components/MultiInput.vue": {
      "import": "./dist/lib/components/MultiInput.js",
      "types": "./dist/components/MultiInput.vue.d.ts"
    },
    "./styles/MultiInput.css": "./dist/styles/MultiInput.css",
    "./types": {
      "types": "./dist/types/index.d.ts"
    },
    "./utils/axiosUtil": {
      "import": "./dist/lib/utils/axiosUtil.js",
      "types": "./dist/types/utils/axiosUtil.d.ts"
    },
    "./utils/cryptoUtil": {
      "import": "./dist/lib/utils/cryptoUtil.js",
      "types": "./dist/types/utils/cryptoUtil.d.ts"
    },
    "./utils/dataUtil": {
      "import": "./dist/lib/utils/dataUtil.js",
      "types": "./dist/types/utils/dataUtil.d.ts"
    },
  }
}

在项目中引入common包后通过配置tsconfig.json即可实现按需引入功能import { fun1 } from 'vue3-common/utils/index'

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "vue3-common/*": ["node_modules/vue3-common/dist/*"]
    }
  }
}

四、发布框架

  1. 发布到本地
    执行npm pack命令,会在项目文件夹下生成.tgz文件,其他项目通过文件路径形式引入:
"vue3-common": "file:../vue3-common/vue3-common-1.0.0.tgz"
  1. 发布到git
    将dist文件夹发布到git仓库其他项目通过git形式引入
"vue3-common": "git+https://gitee.com/Cxx0822/vue3-common#master"

五、组件使用说明

5.1 菜单组件

  1. 安装Vue Router
pnpm install vue-router
  1. 在src/views文件夹中新建vue文件例如
views
  viewA
    index.vue
  viewB
    index.vue  
  1. 在src/views文件夹下新建meta.ts配置文件
import type { IRouteMetaConfig } from 'vue3-common/types'

export const metaList: IRouteMetaConfig = {
  '/viewA': {
    title: 'viewA',
    icon: 'viewA',
    order: 1,
    redirect: '/viewA/index'
  },
  '/viewA/index': {
    title: 'viewA-Index',
    icon: ''
  },
  '/viewB': {
    title: 'viewB',
    icon: 'viewB',
    order: 2,
    redirect: '/viewB/index'
  },
  '/viewB/index': {
    title: 'viewB-index',
    icon: ''
  }
}

order对应的显示顺序关系icon对应的为src/icons/svg中的svg图标文件参考下文图标组件

  1. 在src目录新建router文件夹新建menu.ts文件
import { getRoutersByModules, sortRoutesByOrder } from 'vue3-common/utils/routerUtil'
import Layout from '@/layout/index.vue'
import { metaList } from '@/views/meta'

const modules = {
  ...import.meta.glob('@/views/viewA/**/*.vue'),
  ...import.meta.glob('@/views/viewB/**/*.vue'),
}

const menuRoutes = getRoutersByModules(modules, Layout, metaList)

export default sortRoutesByOrder(menuRoutes)

导入刚才的views文件夹并生成路由菜单。

  1. 如果还有其他的常量路由可以在src/router中新建constant.ts文件
import type { RouteRecordRaw } from 'vue-router'

const constantRoutes: RouteRecordRaw[] = [
  {
    path: '/',
    redirect: '/login'
  },
  // 主页
  {
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    meta: { hidden: true }
  }
]

export default constantRoutes

该部分即Vue Router中的路由定义。

  1. 在src/router中新建index.ts
import { createRouter, createWebHashHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'

import { setupRouteGuard } from 'vue3-common/utils/permissionUtil'

// 使用 import.meta.glob 自动导入所有 src/router 目录下的 .ts 文件
const routeModules = import.meta.glob('./*.ts', { eager: true })

// 将所有模块的默认导出(即路由配置)合并成一个路由数组
const routes: RouteRecordRaw[] = Object.values(routeModules)
  .map((module: any) => module.default) // 获取每个模块的默认导出
  .flat() // 扁平化数组,确保所有路由项都在一个数组中

const router = createRouter({
  history: createWebHashHistory(),
  scrollBehavior: () => ({ top: 0 }),
  routes
})

// 设置路由守卫
setupRouteGuard(router)

export default router

遍历src/router文件夹下所有的路由文件并添加路由守卫。

  1. 在main.ts中配置路由
// 引入路由
import router from './router'

// 创建Vue3实例
const app = createApp(App)

// 使用路由
app.use(router)

5.2 图标组件

  1. 安装vite-plugin-svg-icons依赖
pnpm install vite-plugin-svg-icons -D
  1. 配置vite.config.ts
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'

export default defineConfig({
  plugins: [
    vue(),
    createSvgIconsPlugin({
      // 指定 SVG图标 保存的文件夹路径
      iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],
      // 指定 使用svg图标的格式
      symbolId: 'icon-[dir]-[name]'
    })
  ],
})
  1. 在main.ts中注册
// 注册svg-icon
import 'virtual:svg-icons-register'
  1. 在src/icons/svg目录中添加svg图标
  2. 在组件中使用
<template>
  <svg-icon name="user"/>
</template>

<script setup>
import { SvgIcon } from 'vue3-common'
</script>