feat:更新文档架构
This commit is contained in:
367
docs/Web/Vue/Vue3-Architecture.md
Normal file
367
docs/Web/Vue/Vue3-Architecture.md
Normal file
@@ -0,0 +1,367 @@
|
||||
---
|
||||
title: Vue3架构原理
|
||||
date: 2025-12-15 23:25
|
||||
---
|
||||
|
||||
# 一、Vue3 整体架构图
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Vue 3] --> B[Compiler]
|
||||
A --> C[Runtime]
|
||||
A --> D[Reactivity]
|
||||
|
||||
B --> B1[Parser]
|
||||
B --> B2[Transformer]
|
||||
B --> B3[Codegen]
|
||||
|
||||
C --> C1[Virtual DOM]
|
||||
C --> C2[Renderer]
|
||||
C --> C3[Component]
|
||||
|
||||
D --> D1[Proxy]
|
||||
D --> D2[Effect]
|
||||
D --> D3[Dependency Graph]
|
||||
|
||||
B3 -->|生成| C1
|
||||
C1 -->|Diff/Patch| C2
|
||||
D3 -->|驱动更新| C1
|
||||
```
|
||||
|
||||
## 1.1 编译器模块
|
||||
  编译器负责将模板转换为可执行的渲染函数,包含**解析器(Parser)、转换器(Transformer)和代码生成器(Codegen)** 三个子模块。
|
||||
|
||||
- compiler-core: 核心编译器,负责基础的编译功能,如生成抽象语法树(AST)、基础的模板编译等,是平台无关的编译器核心。
|
||||
- compiler-dom: 基于compiler-core,针对浏览器环境的编译模块,重写了编译和解析方法,处理与原生 DOM API 相关的编译工作。
|
||||
- compiler-sfc: 用于处理 Vue 单文件组件(.vue)的编译,将单文件组件中的模板、脚本和样式进行解析和编译。
|
||||
- compiler-ssr:服务端渲染的编译模块,负责在服务端将 Vue 组件编译为适合服务器端渲染的代码。
|
||||
|
||||
## 1.2 运行时模块
|
||||
  运行时系统处理**虚拟DOM的创建、更新和渲染**,包含组件实例管理、虚拟DOM diff算法和平台渲染器。
|
||||
|
||||
- runtime-core:运行时核心模块,包含了 Vue 组件的生命周期、虚拟节点(vnode)、观察者(watch)等核心 API,是平台无关的运行时核心。
|
||||
- runtime-dom:运行时与 DOM 相关的核心模块,提供了与浏览器 DOM 交互的功能,如创建应用实例、操作 DOM 元素等。
|
||||
|
||||
## 1.3 响应式系统
|
||||
  响应式系统基于Proxy实现数据劫持,通过依赖收集和触发更新机制驱动视图变化。
|
||||
  三大系统协同工作:编译器生成的渲染函数由运行时执行,响应式系统追踪数据变化触发组件更新,形成完整的渲染闭环。
|
||||
|
||||
# 二、基础模块
|
||||
## 2.1 响应式系统
|
||||
```mermaid
|
||||
graph LR
|
||||
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
|
||||
|
||||
A(原始对象):::process -->|Proxy 包装| B(响应式对象):::process
|
||||
B -->|属性读取| C(依赖收集):::process
|
||||
B -->|属性修改| D(触发更新):::process
|
||||
C --> E(副作用函数):::process
|
||||
D --> E
|
||||
E -->|更新视图| F(真实 DOM):::process
|
||||
```
|
||||
|
||||
  Vue3的响应式系统通过Proxy代理对象属性,在get操作时通过track函数收集当前正在执行的副作用(如组件渲染函数)作为依赖,存储到依赖关系图中。当set操作发生时,trigger函数根据依赖关系图找到所有关联的副作用函数,通过调度器批量执行。
|
||||
  这种设计使得数据变化能精准触发组件更新,同时通过异步批处理避免重复渲染。
|
||||
  响应式系统独立于其他模块,可单独使用,为Vue3提供了高效的状态管理能力。
|
||||
|
||||
## 2.2 虚拟 DOM
|
||||
```mermaid
|
||||
graph LR
|
||||
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
|
||||
|
||||
A(旧虚拟 DOM 树):::process -->|Diff 算法| B(差异比较):::process
|
||||
C(新虚拟 DOM 树):::process --> B
|
||||
B -->|更新操作| D(真实 DOM):::process
|
||||
```
|
||||
|
||||
  当组件更新时,Vue会比较新旧虚拟DOM树的差异。通过Patch Flags快速识别动态节点类型(如文本、样式等),仅对标记为动态的部分进行比较;对于带key的子节点列表,使用最长递增子序列算法最小化DOM操作;完全静态的子树则直接跳过diff过程。
|
||||
  这种靶向更新策略将传统虚拟DOM算法O(n³)的时间复杂度优化到接近O(n),同时配合异步批量更新机制,确保复杂应用也能保持流畅渲染
|
||||
|
||||
## 2.3 编译器
|
||||
```mermaid
|
||||
graph LR
|
||||
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
|
||||
|
||||
A(模板字符串):::process -->|解析| B(抽象语法树 - AST):::process
|
||||
B -->|转换| C(优化后的 AST):::process
|
||||
C -->|生成| D(渲染函数代码):::process
|
||||
```
|
||||
|
||||
  Vue编译器将模板字符串转换为渲染函数,经历解析、转换和代码生成三个阶段。
|
||||
  解析阶段通过词法分析生成原始AST抽象语法树;转换阶段对AST进行优化,包括静态节点提升(将不变节点提取到渲染函数外部)、补丁标记(给动态节点添加优化标识)、树结构打平(减少动态子节点的嵌套层级);最后代码生成阶段输出优化的渲染函数。
|
||||
  这些优化使得运行时能跳过静态内容比较,直接定位动态节点,大幅提升渲染性能。
|
||||
|
||||
## 2.4 渲染器
|
||||
```mermaid
|
||||
graph LR
|
||||
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
|
||||
|
||||
A(虚拟 DOM 树):::process -->|创建节点| B(真实 DOM 节点):::process
|
||||
B -->|插入节点| C(页面):::process
|
||||
A -->|更新节点| B
|
||||
```
|
||||
|
||||
  Vue渲染器是连接虚拟DOM与真实平台的桥梁,分为虚拟DOM处理层、Patch算法层和平台操作层。
|
||||
  它通过抽象平台API(如createElement、insert等)实现跨平台能力,开发者可配置不同平台的底层操作来支持Web、Canvas甚至原生应用。
|
||||
  渲染器内部采用增量更新策略,结合编译器提供的优化提示(如静态标记、动态子节点索引),确保DOM操作最精简。
|
||||
  这种设计使Vue在保证Web端高性能的同时,也能灵活适配各种渲染环境。
|
||||
|
||||
# 三、Vue3应用生命周期
|
||||
## 3.1 初始化阶段
|
||||
  流程:
|
||||
1. 调用`createApp()`创建应用实例
|
||||
2. 创建平台专属渲染器(DOM/SSR/Test)
|
||||
3. 初始化应用上下文和插件系统
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// runtime-dom/index.ts
|
||||
const renderer = createRenderer({
|
||||
patchProp, // DOM属性操作
|
||||
...nodeOps // DOM节点操作集合
|
||||
})
|
||||
|
||||
function createApp(rootComponent) {
|
||||
const context = createAppContext()
|
||||
const app = {
|
||||
_component: rootComponent,
|
||||
mount(container) {
|
||||
const vnode = createVNode(rootComponent)
|
||||
renderer.render(vnode, container)
|
||||
return vnode.component.proxy
|
||||
}
|
||||
}
|
||||
return app
|
||||
}
|
||||
```
|
||||
|
||||
## 3.2 组件挂载阶段
|
||||
  流程:
|
||||
1. 标准化容器(字符串选择器 → DOM元素)
|
||||
2. 创建根组件虚拟节点(vnode)
|
||||
3. 初始化组件:解析props和slots、执行setup函数、建立响应式连接等
|
||||
4. 建立渲染effect,为组件创建响应式的副作用函数
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// runtime-core/renderer.ts
|
||||
function mountComponent(vnode, container) {
|
||||
// 1. 创建组件实例
|
||||
const instance = createComponentInstance(vnode)
|
||||
|
||||
// 2. 初始化组件
|
||||
setupComponent(instance)
|
||||
|
||||
// 3. 建立渲染effect
|
||||
setupRenderEffect(instance, container)
|
||||
}
|
||||
|
||||
function setupRenderEffect(instance, container) {
|
||||
instance.update = effect(() => {
|
||||
if (!instance.isMounted) {
|
||||
// 首次渲染
|
||||
const subTree = (instance.subTree = renderComponentRoot(instance))
|
||||
patch(null, subTree, container)
|
||||
instance.isMounted = true
|
||||
} else {
|
||||
// 更新渲染
|
||||
const nextTree = renderComponentRoot(instance)
|
||||
patch(instance.subTree, nextTree, container)
|
||||
instance.subTree = nextTree
|
||||
}
|
||||
}, { scheduler: queueJob })
|
||||
}
|
||||
```
|
||||
|
||||
## 3.3 响应式更新阶段
|
||||
  流程:
|
||||
1. 数据变更触发Proxy的set拦截
|
||||
2. 通过trigger触发依赖的effect
|
||||
3. 调度器将更新任务加入队列
|
||||
4. 下一个tick执行队列中的任务
|
||||
5. 执行组件render函数生成新vnode
|
||||
6. 调用patch进行差异更新
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// reactivity/reactive.ts
|
||||
function createReactiveObject(target) {
|
||||
return new Proxy(target, {
|
||||
set(target, key, value, receiver) {
|
||||
const oldValue = target[key]
|
||||
const result = Reflect.set(target, key, value, receiver)
|
||||
if (hasChanged(value, oldValue)) {
|
||||
trigger(target, key) // 触发更新
|
||||
}
|
||||
return result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// runtime-core/scheduler.ts
|
||||
const queue = []
|
||||
function queueJob(job) {
|
||||
if (!queue.includes(job)) {
|
||||
queue.push(job)
|
||||
queueFlush()
|
||||
}
|
||||
}
|
||||
|
||||
function queueFlush() {
|
||||
if (!isFlushing) {
|
||||
nextTick(flushJobs)
|
||||
}
|
||||
}
|
||||
|
||||
function flushJobs() {
|
||||
isFlushing = true
|
||||
queue.sort((a, b) => a.id - b.id) // 保证父组件先更新
|
||||
for (let i = 0; i < queue.length; i++) {
|
||||
queue[i]()
|
||||
}
|
||||
queue.length = 0
|
||||
isFlushing = false
|
||||
}
|
||||
```
|
||||
|
||||
## 3.4 虚拟DOM Patch阶段
|
||||
  流程:
|
||||
1. 比较新旧vnode的类型
|
||||
2. 不同类型:卸载旧节点,挂载新节点
|
||||
3. 相同类型:更新节点
|
||||
4. 子节点对比采用高效diff算法
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// runtime-core/renderer.ts
|
||||
function patch(n1, n2, container) {
|
||||
// 1. 类型不同直接卸载
|
||||
if (n1 && !isSameVNodeType(n1, n2)) {
|
||||
unmount(n1)
|
||||
n1 = null
|
||||
}
|
||||
|
||||
const { type, patchFlag } = n2
|
||||
|
||||
// 2. 根据类型处理
|
||||
switch (type) {
|
||||
case Text:
|
||||
processText(n1, n2, container)
|
||||
break
|
||||
case Fragment:
|
||||
processFragment(n1, n2, container)
|
||||
break
|
||||
default:
|
||||
if (shapeFlag & ShapeFlags.ELEMENT) {
|
||||
processElement(n1, n2, container)
|
||||
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
||||
processComponent(n1, n2, container)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processElement(n1, n2, container) {
|
||||
if (n1 == null) {
|
||||
mountElement(n2, container)
|
||||
} else {
|
||||
patchElement(n1, n2)
|
||||
}
|
||||
}
|
||||
|
||||
function patchElement(n1, n2) {
|
||||
// 根据patchFlag进行优化更新
|
||||
if (n2.patchFlag & PatchFlags.FULL_PROPS) {
|
||||
// 全量props更新
|
||||
} else {
|
||||
// 只更新动态props
|
||||
}
|
||||
|
||||
// 更新子节点
|
||||
patchChildren(n1, n2)
|
||||
}
|
||||
```
|
||||
|
||||
## 3.5 组件卸载阶段
|
||||
  流程:
|
||||
1. 触发beforeUnmount生命周期钩子
|
||||
2. 停止组件渲染effect
|
||||
3. 递归卸载子组件
|
||||
4. 移除DOM节点
|
||||
5. 触发unmounted生命周期钩子
|
||||
6. 清理响应式依赖
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// runtime-core/renderer.ts
|
||||
function unmount(vnode) {
|
||||
if (vnode.type === Fragment) {
|
||||
unmountChildren(vnode.children)
|
||||
return
|
||||
}
|
||||
|
||||
const { component } = vnode
|
||||
if (component) {
|
||||
unmountComponent(component)
|
||||
} else {
|
||||
remove(vnode.el!)
|
||||
}
|
||||
}
|
||||
|
||||
function unmountComponent(instance) {
|
||||
// 1. 触发beforeUnmount
|
||||
if (instance.bum) {
|
||||
invokeArrayFns(instance.bum)
|
||||
}
|
||||
|
||||
// 2. 停止响应式effect
|
||||
stop(instance.update)
|
||||
|
||||
// 3. 递归卸载子树
|
||||
unmount(instance.subTree)
|
||||
|
||||
// 4. 触发unmounted
|
||||
if (instance.um) {
|
||||
queuePostRenderEffect(instance.um)
|
||||
}
|
||||
|
||||
// 5. 清理引用
|
||||
instance.isUnmounted = true
|
||||
}
|
||||
```
|
||||
|
||||
## 3.6 编译阶段
|
||||
  流程:
|
||||
1. 将模板解析为AST
|
||||
2. 转换AST(静态提升/标记PatchFlag)
|
||||
3. 生成可执行的渲染函数代码
|
||||
4. 运行时直接使用优化后的渲染函数
|
||||
|
||||
  关键代码:
|
||||
```typescript
|
||||
// compiler-core/src/compile.ts
|
||||
function baseCompile(template, options) {
|
||||
// 1. 解析阶段
|
||||
const ast = parse(template)
|
||||
|
||||
// 2. 转换阶段
|
||||
transform(ast, {
|
||||
hoistStatic: true, // 静态提升
|
||||
nodeTransforms: [
|
||||
transformIf,
|
||||
transformFor,
|
||||
transformExpression
|
||||
]
|
||||
})
|
||||
|
||||
// 3. 代码生成
|
||||
const code = generate(ast, {
|
||||
mode: 'function',
|
||||
runtimeGlobalName: 'Vue',
|
||||
prefixIdentifiers: true
|
||||
})
|
||||
|
||||
return {
|
||||
ast,
|
||||
code: `with(this){return ${code}}` // 生成渲染函数
|
||||
}
|
||||
}
|
||||
```
|
||||
419
docs/Web/Vue/Vue3-Common.md
Normal file
419
docs/Web/Vue/Vue3-Common.md
Normal file
@@ -0,0 +1,419 @@
|
||||
---
|
||||
title: Vue3-Common
|
||||
date: 2025-12-15 23:25
|
||||
---
|
||||
|
||||
# 一、依赖管理
|
||||
  本项目会传递安装的依赖有:
|
||||
|
||||
| 包名称 | 版本 | 含义和用途说明 |
|
||||
|-------|------|---------------|
|
||||
| `@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插件
|
||||
|
||||
```cmd
|
||||
pnpm add vite-plugin-dts -D
|
||||
```
|
||||
|
||||
```typescript
|
||||
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打包配置
|
||||
```typescript
|
||||
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 导出路径
|
||||
```json
|
||||
{
|
||||
"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'`。
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"vue3-common/*": ["node_modules/vue3-common/dist/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 四、发布框架
|
||||
1. 发布到本地
|
||||
执行`npm pack`命令,会在项目文件夹下生成`.tgz`文件,其他项目通过文件路径形式引入:
|
||||
```json
|
||||
"vue3-common": "file:../vue3-common/vue3-common-1.0.0.tgz"
|
||||
```
|
||||
|
||||
2. 发布到git
|
||||
将dist文件夹发布到git仓库,其他项目通过git形式引入:
|
||||
```json
|
||||
"vue3-common": "git+https://gitee.com/Cxx0822/vue3-common#master"
|
||||
```
|
||||
|
||||
# 五、组件使用说明
|
||||
## 5.1 菜单组件
|
||||
1. 安装Vue Router
|
||||
```cmd
|
||||
pnpm install vue-router
|
||||
```
|
||||
|
||||
2. 在src/views文件夹中新建vue文件,例如:
|
||||
```cmd
|
||||
views
|
||||
viewA
|
||||
index.vue
|
||||
viewB
|
||||
index.vue
|
||||
```
|
||||
|
||||
3. 在src/views文件夹下新建meta.ts配置文件
|
||||
```typescript
|
||||
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图标文件(参考下文图标组件)。
|
||||
|
||||
4. 在src目录新建router文件夹,新建menu.ts文件
|
||||
```typescript
|
||||
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文件夹并生成路由菜单。
|
||||
|
||||
5. 如果还有其他的常量路由,可以在src/router中新建constant.ts文件:
|
||||
```typescript
|
||||
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中的路由定义。
|
||||
|
||||
6. 在src/router中新建index.ts:
|
||||
```typescript
|
||||
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文件夹下所有的路由文件,并添加路由守卫。
|
||||
|
||||
7. 在main.ts中配置路由:
|
||||
```ts
|
||||
// 引入路由
|
||||
import router from './router'
|
||||
|
||||
// 创建Vue3实例
|
||||
const app = createApp(App)
|
||||
|
||||
// 使用路由
|
||||
app.use(router)
|
||||
```
|
||||
|
||||
## 5.2 图标组件
|
||||
1. 安装vite-plugin-svg-icons依赖
|
||||
```cmd
|
||||
pnpm install vite-plugin-svg-icons -D
|
||||
```
|
||||
|
||||
2. 配置vite.config.ts
|
||||
```typescript
|
||||
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]'
|
||||
})
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
3. 在main.ts中注册
|
||||
```typescript
|
||||
// 注册svg-icon
|
||||
import 'virtual:svg-icons-register'
|
||||
```
|
||||
|
||||
4. 在src/icons/svg目录中添加svg图标
|
||||
5. 在组件中使用
|
||||
```vue
|
||||
<template>
|
||||
<svg-icon name="user"/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { SvgIcon } from 'vue3-common'
|
||||
</script>
|
||||
```
|
||||
Reference in New Issue
Block a user