Vue 3 + Vite + Monorepo + Qiankun 微前端搭建指南
目录
- 环境准备
- [初始化 Monorepo 项目](#初始化 Monorepo 项目 "#2-%E5%88%9D%E5%A7%8B%E5%8C%96-monorepo-%E9%A1%B9%E7%9B%AE")
- 安装公共依赖
- [配置代码规范和 TypeScript](#配置代码规范和 TypeScript "#4-%E9%85%8D%E7%BD%AE%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83%E5%92%8C-typescript")
- 创建共享库
- [5.1 公共工具库 (
packages/utils)](#5.1 公共工具库 (packages/utils) "#51-%E5%85%AC%E5%85%B1%E5%B7%A5%E5%85%B7%E5%BA%93-packagesutils") - [5.2 公共 Hooks 库 (
packages/hooks)](#5.2 公共 Hooks 库 (packages/hooks) "#52-%E5%85%AC%E5%85%B1-hooks-%E5%BA%93-packageshooks") - [5.3 公共 UI 组件库 (
packages/components)](#5.3 公共 UI 组件库 (packages/components) "#53-%E5%85%AC%E5%85%B1-ui-%E7%BB%84%E4%BB%B6%E5%BA%93-packagescomponents") - [5.4 公共请求封装 (
packages/request)](#5.4 公共请求封装 (packages/request) "#54-%E5%85%AC%E5%85%B1%E8%AF%B7%E6%B1%82%E5%B0%81%E8%A3%85-packagesrequest")
- [5.1 公共工具库 (
- [创建主应用 (
apps/main)](#创建主应用 (apps/main) "#6-%E5%88%9B%E5%BB%BA%E4%B8%BB%E5%BA%94%E7%94%A8-appsmain") - [创建微应用 (
apps/app1)](#创建微应用 (apps/app1) "#7-%E5%88%9B%E5%BB%BA%E5%BE%AE%E5%BA%94%E7%94%A8-appsapp1") - 配置主应用和微应用的路由
- 测试和运行
- 构建和部署
一、技术选型考量
- 核心技术栈确定
- Vue 3:作为核心框架,其组合式 API、更好的 TypeScript 支持以及优异的性能,为中后台项目的复杂逻辑处理和组件复用提供了强大基础。
- Vite:替代传统的 Webpack 构建工具,以其极速的冷启动、按需编译和热更新能力,显著提升开发效率,尤其适合大型项目的开发流程。
- Vue Router 4:与 Vue 3 深度适配,提供了更灵活的路由配置方式,支持动态路由、嵌套路由等功能,满足中后台系统复杂的页面跳转需求。
- Pinia:作为 Vuex 的替代方案,Pinia 具有更简洁的 API 设计、更好的 TypeScript 兼容性,同时支持多 Store 架构,便于状态的模块化管理。
- Element Plus:基于 Vue 3 的 UI 组件库,提供了丰富的中后台常用组件,如表格、表单、弹窗等,能够快速搭建美观、易用的界面。
- Monorepo:采用单一代码仓库管理多个相关项目,实现代码共享、依赖统一管理,简化团队协作流程,尤其适合多项目、多模块的中后台系统。
- qiankun:微前端框架,能够将多个独立的前端应用整合为一个整体,实现应用间的无缝切换、通信和资源共享,满足中后台系统的业务拆分和整合需求。
- 技术栈优势互补 这些技术的组合形成了强大的优势互补。Vue 3 和 Vite 保证了项目的性能和开发体验;Router 和 Pinia 实现了页面路由和状态的高效管理;Element Plus 加速了 UI 开发;Monorepo 优化了项目的组织和协作方式;qiankun 则为系统的微前端架构提供了支持,使得各业务模块可以独立开发、部署和维护,同时又能有机地结合在一起。
整体架构概览
我们将创建以下目录结构:
bash
vue3-monorepo-qiankun/
├── apps/ # 应用目录
│ ├── main/ # 主应用 (qiankun 容器)
│ └── app1/ # 微应用 1
├── packages/ # 共享库目录
│ ├── components/ # 共享组件库
│ ├── hooks/ # 共享 Hooks 库
│ └── utils/ # 共享工具库
├── package.json # 根项目配置
├── pnpm-workspace.yaml # pnpm 工作区配置
└── tsconfig.json # 全局 TypeScript 配置
第一步:环境准备
确保你已经安装了 pnpm 和 node (推荐 v16+)。
bash
npm install -g pnpm
第二步:初始化 Monorepo 项目
-
创建并进入项目根目录
bashmkdir -p vue3-monorepo-qiankun && cd vue3-monorepo-qiankun -
初始化
package.jsonbashpnpm init -y -
创建
pnpm-workspace.yaml文件bashcat > pnpm-workspace.yaml << EOF packages: - 'apps/*' - 'packages/*' EOF -
创建
.npmrc文件 (推荐) 这个文件用于配置 pnpm 的行为,让它更像传统的node_modules结构,方便某些工具识别。bashcat > .npmrc << EOF shamefully-hoist=true EOF
第三步:安装公共依赖
这是本方案的核心。我们将所有共享的运行时依赖和开发依赖都安装在根目录。
-
安装公共运行时依赖 这些是主应用和微应用都需要用到的库,如
vue,element-plus等。bashpnpm add -w vue vue-router pinia element-plus @element-plus/icons-vue axios qiankun说明 :
-w或--workspace-root是关键,它告诉 pnpm 把依赖安装到工作区的根目录,而不是当前目录(虽然这里我们就在根目录)。 -
安装公共开发依赖 这些是构建、 lint、测试等工具,如
vite,typescript,eslint等。bashpnpm add -Dw @vitejs/plugin-vue @vitejs/plugin-vue-jsx vite typescript vue-tsc @types/node eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue vue-eslint-parser @typescript-eslint/eslint-plugin @typescript-eslint/parser vite-plugin-qiankun
执行完毕后,你会发现根目录的 package.json 中已经包含了所有这些依赖,并且根目录下出现了一个 node_modules 文件夹。
第四步: 配置代码规范和 TypeScript
-
创建
tsconfig.json这个文件为整个工作区提供基础的 TypeScript 配置,子项目可以继承它。bashcat > tsconfig.json << EOF { "compilerOptions": { "target": "ESNext", "useDefineForClassFields": true, "module": "ESNext", "moduleResolution": "Node", "strict": true, "jsx": "preserve", "resolveJsonModule": true, "isolatedModules": true, "esModuleInterop": true, "lib": ["ESNext", "DOM"], "skipLibCheck": true, "noEmit": true, "baseUrl": ".", "paths": { "@/*": ["src/*"] } }, "include": ["**/*.ts", "**/*.tsx", "**/*.vue"], "exclude": ["node_modules", "**/dist"] } EOF -
创建 ESLint 和 Prettier 配置 在根目录创建这些配置文件,可以让所有子项目共享同一套代码规范。
bash# 创建 .eslintrc.js cat > .eslintrc.js << EOF module.exports = { root: true, env: { browser: true, es2021: true, node: true, }, extends: [ 'eslint:recommended', 'plugin:vue/vue3-essential', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', ], parser: 'vue-eslint-parser', parserOptions: { ecmaVersion: 'latest', parser: '@typescript-eslint/parser', sourceType: 'module', }, plugins: ['vue', '@typescript-eslint', 'prettier'], rules: { 'prettier/prettier': 'error', 'vue/no-unused-vars': 'error', '@typescript-eslint/no-unused-vars': 'error', '@typescript-eslint/explicit-module-boundary-types': 'off', }, }; EOF # 创建 .prettierrc cat > .prettierrc << EOF { "printWidth": 100, "tabWidth": 2, "useTabs": false, "semi": true, "singleQuote": true, "quoteProps": "as-needed", "trailingComma": "es5", "bracketSpacing": true, "arrowParens": "avoid", "endOfLine": "auto", "vueIndentScriptAndStyle": false } EOF # 创建 .eslintignore 和 .prettierignore cat > .eslintignore << EOF node_modules/ dist/ *.d.ts EOF cp .eslintignore .prettierignore -
更新根目录
package.json的scripts添加一些方便在根目录运行的脚本,比如全局 lint。json{ "scripts": { "dev": "pnpm -r dev", "build": "pnpm -r build", "lint": "eslint . --ext .vue,.js,.ts", "format": "prettier --write .", "clean": "pnpm -r --delete node_modules && rm -rf node_modules" } }
第五步:创建共享库 (packages)
共享库将直接使用根目录的依赖,它们自己的 package.json 只需要声明依赖即可。
5.1 公共工具库 (packages/utils)
-
创建目录并初始化
bashmkdir -p packages/utils/src && cd packages/utils pnpm init -y -
修改
package.json关键是 dependencies 字段,我们声明需要 vue,但 pnpm 会自动从根目录查找。json{ "name": "@your-org/utils", "version": "1.0.0", "type": "module", "main": "src/index.ts", "types": "src/index.ts", "scripts": { "lint": "eslint . --ext .ts" }, "dependencies": { "vue": "^3.4.21" } } -
创建
tsconfig.json继承根目录的配置。json{ "extends": "../../tsconfig.json", "compilerOptions": { "composite": true }, "include": ["src/**/*.ts", "src/**/*.d.ts"] } -
编写工具函数 创建 src/format.ts 并在 src/index.ts 中导出
-
src/format.tstypescriptexport function formatDate(date: Date, fmt = 'YYYY-MM-DD HH:mm:ss') { const o = { 'M+': date.getMonth() + 1, 'D+': date.getDate(), 'H+': date.getHours(), 'm+': date.getMinutes(), 's+': date.getSeconds(), 'q+': Math.floor((date.getMonth() + 3) / 3), S: date.getMilliseconds() }; if (/(Y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); } for (const k in o) { if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k as keyof typeof o]) : (('00' + o[k as keyof typeof o]).substr(('' + o[k as keyof typeof o]).length))); } } return fmt; } -
src/index.tstypescriptexport * from './format';
-
5.2 公共 Hooks 库 (packages/hooks)
-
创建目录并初始化
bashcd ../../ && mkdir -p packages/hooks/src && cd packages/hooks pnpm init -y -
修改
package.jsonjson{ "name": "@your-org/hooks", "version": "1.0.0", "type": "module", "main": "src/index.ts", "types": "src/index.ts", "scripts": { "lint": "eslint . --ext .ts,.vue" }, "dependencies": { "vue": "^3.4.21" } } -
创建
tsconfig.jsonjson{ "extends": "../../tsconfig.json", "compilerOptions": { "composite": true }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"] } -
编写 Hooks
-
src/useStorage.tstypescriptimport { ref, watch, type Ref } from 'vue'; export function useStorage<T>(key: string, defaultValue: T): Ref<T> { const storedValue = localStorage.getItem(key); const value = ref<T>(storedValue ? JSON.parse(storedValue) : defaultValue); watch(value, (newVal) => { localStorage.setItem(key, JSON.stringify(newVal)); }, { deep: true }); return value; } -
src/index.tstypescriptexport * from './useStorage';
-
5.3 公共 UI 组件库 (packages/components)
-
创建目录并初始化
bashcd ../../ && mkdir -p packages/components/src && cd packages/components pnpm init -y -
修改
package.jsonjson{ "name": "@your-org/components", "version": "1.0.0", "type": "module", "main": "src/index.ts", "types": "src/index.ts", "scripts": { "lint": "eslint . --ext .ts,.vue" }, "dependencies": { "vue": "^3.4.21", "element-plus": "^2.7.2" } } -
创建
tsconfig.jsonjson{ "extends": "../../tsconfig.json", "compilerOptions": { "composite": true }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"] } -
编写 UI 组件
-
src/CommonButton/CommonButton.vuevue<template> <el-button :type="type" :loading="loading" @click="onClick"> <slot></slot> </el-button> </template> <script setup lang="ts"> import { defineProps, emit } from 'vue'; const props = defineProps<{ type?: 'primary' | 'success' | 'warning' | 'danger' | 'info'; loading?: boolean; }>(); const emit = defineEmits<{ (e: 'click'): void; }>(); const onClick = () => { emit('click'); }; </script> -
src/index.tstypescriptexport { default as CommonButton } from './CommonButton/CommonButton.vue';
-
5.4 公共请求封装 (packages/request)
-
创建目录并初始化
bashcd ../../ && mkdir -p packages/request/src && cd packages/request pnpm init -y -
修改
package.jsonjson{ "name": "@your-org/request", "version": "1.0.0", "type": "module", "main": "src/index.ts", "types": "src/index.ts", "scripts": { "lint": "eslint . --ext .ts" }, "dependencies": { "axios": "^1.6.8", "vue": "^3.4.21" } } -
创建
tsconfig.jsonjson{ "extends": "../../tsconfig.json", "compilerOptions": { "composite": true }, "include": ["src/**/*.ts", "src/**/*.d.ts"] } -
编写请求封装
-
src/index.tstypescriptimport axios from 'axios'; const service = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 5000 }); // 请求拦截器 service.interceptors.request.use( (config) => { // 可以添加 token 等逻辑 return config; }, (error) => { return Promise.reject(error); } ); // 响应拦截器 service.interceptors.response.use( (response) => { return response.data; }, (error) => { return Promise.reject(error); } ); export default service;
-
第六步:创建主应用 (apps/main)
主应用是一个标准的 Vite 应用,但它的依赖也将由根目录提供。
-
创建目录并初始化 我们使用 --template,但之后会修改依赖。
bashcd ../../ && mkdir -p apps/main && cd apps/main pnpm create vite@latest . --template vue-ts rm -rf node_modules pnpm-lock.yaml执行后,根据提示操作,然后删除 apps/main 目录下的 node_modules 和 pnpm-lock.yaml 文件,因为我们将使用根目录的依赖。
-
修改
package.json删除dependencies和devDependencies下的所有依赖,然后根据需要重新声明它们。pnpm 会自动从根目录链接。json{ "name": "@your-org/main", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vue-tsc && vite build", "lint": "eslint . --ext .vue,.js,.ts", "preview": "vite preview" }, "dependencies": { "@element-plus/icons-vue": "^2.3.1", "@your-org/components": "workspace:^*", "@your-org/hooks": "workspace:^*", "@your-org/utils": "workspace:^*", "@your-org/request": "workspace:^*", "element-plus": "^2.7.2", "pinia": "^2.1.7", "qiankun": "^2.10.16", "vue": "^3.4.21", "vue-router": "^4.3.0" }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue-jsx": "^3.1.0", "vite": "^5.2.11", "vite-plugin-qiankun": "^1.0.11", "vue-tsc": "^1.8.27" } }注意 :
"@your-org/utils": "workspace:^*"是引用工作区内部包的标准方式。 -
修改
vite.config.ts主应用作为 qiankun 容器,需要配置 vite-plugin-qiankun。typescriptimport { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import qiankun from 'vite-plugin-qiankun' import path from 'path' export default defineConfig({ plugins: [ vue(), vueJsx(), qiankun('main-app', { useDevMode: true }) ], resolve: { alias: { '@': path.resolve(__dirname, 'src') }, preserveSymlinks: true, modules: [ path.resolve(__dirname, 'node_modules'), path.resolve(__dirname, '../../node_modules') ] }, server: { port: 8080, open: true, cors: true } }) -
修改
tsconfig.json继承根目录的配置json{ "extends": "../../tsconfig.json", "compilerOptions": { "composite": true "baseUrl": ".", // 基础路径 "paths": { "@/*": ["src/*"] // 映射 @/ 到 src/ } }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] } -
编写主应用代码
-
src/router/index.tstypescriptimport { createRouter, createWebHistory } from 'vue-router' import Home from '@/views/Home/index.vue' import MicroApp from '@/views/MicroApp/index.vue' const routes = [ { path: '/', component: Home }, { path: '/app1', component: MicroApp } ] const router = createRouter({ history: createWebHistory(), routes }) export default router -
src/main.tstypescriptimport { createApp } from 'vue' import App from './App.vue' import router from './router' import { createPinia } from 'pinia' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' import { CommonButton } from '@your-org/components' const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.component('CommonButton', CommonButton) app.use(createPinia()) app.use(router) app.use(ElementPlus) app.mount('#app') // 暴露全局变量给微应用 (window as any).Vue = app.config.globalProperties.constructor (window as any).ElementPlus = ElementPlus (window as any).Pinia = app.config.globalProperties.$pinia (window as any).VueRouter = router -
src/views/MicroApp/index.vuevue<template> <div id="micro-app-container"></div> </template> <script setup lang="ts"> import { onMounted, onUnmounted } from 'vue'; import { loadMicroApp, unmountMicroApp } from 'qiankun'; let microApp: any = null; onMounted(() => { microApp = loadMicroApp({ name: 'app1', entry: '//localhost:8081', container: '#micro-app-container', props: { token: 'main-app-token' } }) }); onUnmounted(() => { unmountMicroApp('app1'); }); </script>
-
第七步:创建微应用 (apps/app1)
微应用的配置与主应用类似,但有一个关键区别:当它被 qiankun 加载时,需要通过 externals 排除已由主应用提供的依赖。
-
创建目录并初始化 同样,删除自动生成的
node_modules和pnpm-lock.yaml。bashcd ../../ && mkdir -p apps/app1 && cd apps/app1 pnpm create vite@latest . --template vue-ts rm -rf node_modules pnpm-lock.yaml -
修改
package.json与主应用类似,声明依赖json{ "name": "@your-org/app1", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "dev:qiankun": "vite --mode qiankun", "build": "vue-tsc && vite build", "build:qiankun": "vue-tsc && vite build --mode qiankun", "lint": "eslint . --ext .vue,.js,.ts", "preview": "vite preview" }, "dependencies": { "@element-plus/icons-vue": "^2.3.1", "@your-org/components": "workspace:^*", "@your-org/hooks": "workspace:^*", "@your-org/utils": "workspace:^*", "@your-org/request": "workspace:^*", "element-plus": "^2.7.2", "pinia": "^2.1.7", "vue": "^3.4.21", "vue-router": "^4.3.0" }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue-jsx": "^3.1.0", "vite": "^5.2.11", "vite-plugin-qiankun": "^1.0.11", "vue-tsc": "^1.8.27" } } -
修改
vite.config.ts这里是核心,我们需要根据运行模式来动态配置build.rollupOptions.externals。typescriptimport { defineConfig, loadEnv } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import qiankun from 'vite-plugin-qiankun' import path from 'path' export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd()) const isQiankunMode = env.VITE_QIANKUN === 'true' return { plugins: [ vue(), vueJsx(), qiankun('app1', { useDevMode: !isQiankunMode }) ], resolve: { alias: { '@': path.resolve(__dirname, 'src') }, preserveSymlinks: true, modules: [ path.resolve(__dirname, 'node_modules'), path.resolve(__dirname, '../../node_modules') ] }, server: { port: 8081, open: true, cors: true }, base: isQiankunMode ? '/app1/' : '/', build: { rollupOptions: { external: isQiankunMode ? [ 'vue', 'vue-router', 'pinia', 'element-plus', 'axios', '@element-plus/icons-vue', '@your-org/utils', '@your-org/hooks', '@your-org/components', '@your-org/request' ] : [], output: { globals: isQiankunMode ? { vue: 'Vue', 'vue-router': 'VueRouter', pinia: 'Pinia', 'element-plus': 'ElementPlus', axios: 'axios', '@element-plus/icons-vue': 'ElementPlusIconsVue', '@your-org/utils': 'YourOrgUtils', '@your-org/hooks': 'YourOrgHooks', '@your-org/components': 'YourOrgComponents', '@your-org/request': 'YourOrgRequest' } : {} } } } } }) -
创建
apps/app1/.env.qiankun创建apps/app1/.env.qiankun文件,用于 qiankun 模式bashcat > .env.qiankun << EOF NODE_ENV=development VITE_QIANKUN=true EOF -
修改
tsconfig.json同样继承根目录配置json{ "extends": "../../tsconfig.json", "compilerOptions": { "composite": true, "baseUrl": ".", // 基础路径 "paths": { "@/*": ["src/*"] // 映射 @/ 到 src/ } }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] } -
编写微应用代码 微应用的入口文件
src/main.ts需要遵循 qiankun 的协议,导出bootstrap,mount,unmount三个生命周期函数。-
src/router/index.tstypescriptimport { createRouter, createWebHistory } from 'vue-router' import Home from '@/views/Home/index.vue' import List from '@/views/List/index.vue' const routes = [ { path: '/', redirect: '/home' }, { path: '/home', component: Home }, { path: '/list', component: List } ] const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes }) export default router -
src/main.tstypescriptimport { createApp } from 'vue' import App from './App.vue' import router from './router' import { createPinia } from 'pinia' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' import { CommonButton } from '@your-org/components' import request from '@your-org/request' let app: any = null function render(props: any = {}) { const { container } = props app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.config.globalProperties.$request = request app.component('CommonButton', CommonButton) app.use(createPinia()) app.use(router) app.use(ElementPlus) app.mount(container ? container.querySelector('#app') : '#app') } if (!(window as any).__POWERED_BY_QIANKUN__) { render() } export async function bootstrap() { console.log('微应用 app1 启动') } export async function mount(props: any) { console.log('微应用 app1 挂载', props) render(props) } export async function unmount() { console.log('微应用 app1 卸载') app.unmount() app = null }
重要 : 主应用需要在全局 (
window) 上挂载微应用externals中声明的那些库,例如window.Vue = Vue,这样微应用在运行时才能找到它们。这部分逻辑通常放在主应用的src/main.ts中。 -
第八步:配置主应用和微应用的路由
- 主应用路由:负责加载微应用(如
/app1路径)。 - 微应用路由:内部路由相对独立(如
/home、/list),会被 qiankun 自动拼接为/app1/home、/app1/list。
第九步:测试和运行
-
安装所有依赖
bashcd ../../.. pnpm install -
安装所有依赖 这会根据所有
package.json的声明,从根目录统一安装和链接。bashpnpm install -
启动项目
bash# 同时启动所有应用 pnpm dev # 或者单独启动 pnpm --filter @your-org/main dev pnpm --filter @your-org/app1 dev:qiankun -
访问地址
- 主应用:http://localhost:8080
- 微应用独立运行:http://localhost:8081
- 主应用加载微应用:http://localhost:8080/app1
第十步. 构建和部署
-
构建项目
bashpnpm build -
部署配置(Nginx 示例)
nginxserver { listen 80; server_name your-domain.com; root /path/to/vue3-monorepo-qiankun/apps/main/dist; location / { try_files $uri $uri/ /index.html; } location /app1 { alias /path/to/vue3-monorepo-qiankun/apps/app1/dist; try_files $uri $uri/ /app1/index.html; } }
总结
通过以上步骤,你已经成功搭建了一个完整的 Vue 3 + Vite + Monorepo + Qiankun 微前端项目,包括公共工具库、Hooks 库、UI 组件库和请求封装。所有公共依赖都提取到了根目录进行统一管理,实现了代码复用和版本统一。