Vue 3 + Vite + Router + Pinia + Element Plus + Monorepo + qiankun 构建企业级中后台前端框架


**Vue 3 + Vite + Monorepo + Qiankun 微前端搭建指南**

**目录**

  1. 环境准备\](#1-环境准备)

  2. 安装公共依赖\](#3-安装公共依赖)

  3. 创建共享库\](#5-创建共享库)

  • 5.2 公共 Hooks 库 (\`packages/hooks\`)\](#52-公共-hooks-库-packageshooks)

  • 5.4 公共请求封装 (\`packages/request\`)\](#54-公共请求封装-packagesrequest)

  1. 创建微应用 (\`apps/app1\`)\](#7-创建微应用-appsapp1)

  2. 测试和运行\](#9-测试和运行)


一、技术选型考量

  1. 核心技术栈确定
  • Vue 3:作为核心框架,其组合式 API、更好的 TypeScript 支持以及优异的性能,为中后台项目的复杂逻辑处理和组件复用提供了强大基础。

  • Vite:替代传统的 Webpack 构建工具,以其极速的冷启动、按需编译和热更新能力,显著提升开发效率,尤其适合大型项目的开发流程。

  • Vue Router 4:与 Vue 3 深度适配,提供了更灵活的路由配置方式,支持动态路由、嵌套路由等功能,满足中后台系统复杂的页面跳转需求。

  • Pinia:作为 Vuex 的替代方案,Pinia 具有更简洁的 API 设计、更好的 TypeScript 兼容性,同时支持多 Store 架构,便于状态的模块化管理。

  • Element Plus:基于 Vue 3 的 UI 组件库,提供了丰富的中后台常用组件,如表格、表单、弹窗等,能够快速搭建美观、易用的界面。

  • Monorepo:采用单一代码仓库管理多个相关项目,实现代码共享、依赖统一管理,简化团队协作流程,尤其适合多项目、多模块的中后台系统。

  • qiankun:微前端框架,能够将多个独立的前端应用整合为一个整体,实现应用间的无缝切换、通信和资源共享,满足中后台系统的业务拆分和整合需求。

  1. 技术栈优势互补

这些技术的组合形成了强大的优势互补。Vue 3 和 Vite 保证了项目的性能和开发体验;Router 和 Pinia 实现了页面路由和状态的高效管理;Element Plus 加速了 UI 开发;Monorepo 优化了项目的组织和协作方式;qiankun 则为系统的微前端架构提供了支持,使得各业务模块可以独立开发、部署和维护,同时又能有机地结合在一起。

整体架构概览

我们将创建以下目录结构:

```

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 项目**

  1. **创建并进入项目根目录**

```bash

mkdir -p vue3-monorepo-qiankun && cd vue3-monorepo-qiankun

```

  1. **初始化 `package.json`**

```bash

pnpm init -y

```

  1. **创建 `pnpm-workspace.yaml` 文件**

```bash

cat > pnpm-workspace.yaml << EOF

packages:

  • 'apps/*'

  • 'packages/*'

EOF

```

  1. **创建 `.npmrc` 文件 (推荐)**

这个文件用于配置 pnpm 的行为,让它更像传统的 `node_modules` 结构,方便某些工具识别。

```bash

cat > .npmrc << EOF

shamefully-hoist=true

EOF

```


**第三步:安装公共依赖**

这是本方案的核心。我们将所有共享的运行时依赖和开发依赖都安装在根目录。

  1. **安装公共运行时依赖**

这些是主应用和微应用都需要用到的库,如 `vue`, `element-plus` 等。

```bash

pnpm add -w vue vue-router pinia element-plus @element-plus/icons-vue axios qiankun

```

> **说明**: `-w` 或 `--workspace-root` 是关键,它告诉 pnpm 把依赖安装到工作区的根目录,而不是当前目录(虽然这里我们就在根目录)。

  1. **安装公共开发依赖**

这些是构建、 lint、测试等工具,如 `vite`, `typescript`, `eslint` 等。

```bash

pnpm 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**

  1. **创建 `tsconfig.json`**

这个文件为整个工作区提供基础的 TypeScript 配置,子项目可以继承它。

```bash

cat > 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

```

  1. **创建 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

```

  1. **更新根目录 `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`)**

  1. **创建目录并初始化**

```bash

mkdir -p packages/utils/src && cd packages/utils

pnpm init -y

```

  1. **修改 `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"

}

}

```

  1. **创建 `tsconfig.json`**

继承根目录的配置。

```json

{

"extends": "../../tsconfig.json",

"compilerOptions": {

"composite": true

},

"include": ["src/**/*.ts", "src/**/*.d.ts"]

}

```

  1. **编写工具函数**

创建 src/format.ts 并在 src/index.ts 中导出

  • `src/format.ts`

```typescript

export 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.ts`

```typescript

export * from './format';

```

**5.2 公共 Hooks 库 (`packages/hooks`)**

  1. **创建目录并初始化**

```bash

cd ../../ && mkdir -p packages/hooks/src && cd packages/hooks

pnpm init -y

```

  1. **修改 `package.json`**

```json

{

"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"

}

}

```

  1. **创建 `tsconfig.json`**

```json

{

"extends": "../../tsconfig.json",

"compilerOptions": {

"composite": true

},

"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"]

}

```

  1. **编写 Hooks**
  • `src/useStorage.ts`

```typescript

import { 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.ts`

```typescript

export * from './useStorage';

```

**5.3 公共 UI 组件库 (`packages/components`)**

  1. **创建目录并初始化**

```bash

cd ../../ && mkdir -p packages/components/src && cd packages/components

pnpm init -y

```

  1. **修改 `package.json`**

```json

{

"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"

}

}

```

  1. **创建 `tsconfig.json`**

```json

{

"extends": "../../tsconfig.json",

"compilerOptions": {

"composite": true

},

"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue"]

}

```

  1. **编写 UI 组件**
  • `src/CommonButton/CommonButton.vue`

```vue

<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.ts`

```typescript

export { default as CommonButton } from './CommonButton/CommonButton.vue';

```

**5.4 公共请求封装 (`packages/request`)**

  1. **创建目录并初始化**

```bash

cd ../../ && mkdir -p packages/request/src && cd packages/request

pnpm init -y

```

  1. **修改 `package.json`**

```json

{

"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"

}

}

```

  1. **创建 `tsconfig.json`**

```json

{

"extends": "../../tsconfig.json",

"compilerOptions": {

"composite": true

},

"include": ["src/**/*.ts", "src/**/*.d.ts"]

}

```

  1. **编写请求封装**
  • `src/index.ts`

```typescript

import 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 应用,但它的依赖也将由根目录提供。

  1. **创建目录并初始化**

我们使用 --template,但之后会修改依赖。

```bash

cd ../../ && 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 文件,因为我们将使用根目录的依赖。

  1. **修改 `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:^*"` 是引用工作区内部包的标准方式。

  1. **修改 `vite.config.ts`**

主应用作为 qiankun 容器,需要配置 vite-plugin-qiankun。

```typescript

import { 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

}

})

```

  1. **修改 `tsconfig.json`**

继承根目录的配置

```json

{

"extends": "../../tsconfig.json",

"compilerOptions": {

"composite": true

"baseUrl": ".", // 基础路径

"paths": {

"@/*": ["src/*"] // 映射 @/ 到 src/

}

},

"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]

}

```

  1. **编写主应用代码**
  • `src/router/index.ts`

```typescript

import { 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.ts`

```typescript

import { 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.vue`

```vue

<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` 排除已由主应用提供的依赖**。

  1. **创建目录并初始化**

同样,删除自动生成的 `node_modules` 和 `pnpm-lock.yaml`。

```bash

cd ../../ && mkdir -p apps/app1 && cd apps/app1

pnpm create vite@latest . --template vue-ts

rm -rf node_modules pnpm-lock.yaml

```

  1. **修改 `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"

}

}

```

  1. **修改 `vite.config.ts`**

这里是核心,我们需要根据运行模式来动态配置 `build.rollupOptions.externals`。

```typescript

import { 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'

} : {}

}

}

}

}

})

```

  1. **创建 `apps/app1/.env.qiankun`**

创建 `apps/app1/.env.qiankun` 文件,用于 qiankun 模式

```bash

cat > .env.qiankun << EOF

NODE_ENV=development

VITE_QIANKUN=true

EOF

```

  1. **修改 `tsconfig.json`**

同样继承根目录配置

```json

{

"extends": "../../tsconfig.json",

"compilerOptions": {

"composite": true,

"baseUrl": ".", // 基础路径

"paths": {

"@/*": ["src/*"] // 映射 @/ 到 src/

}

},

"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]

}

```

  1. **编写微应用代码**

微应用的入口文件 `src/main.ts` 需要遵循 qiankun 的协议,导出 `bootstrap`, `mount`, `unmount` 三个生命周期函数。

  • `src/router/index.ts`

```typescript

import { 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.ts`

```typescript

import { 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`。


**第九步:测试和运行**

  1. **安装所有依赖**

```bash

cd ../../..

pnpm install

```

  1. **安装所有依赖**

这会根据所有 `package.json` 的声明,从根目录统一安装和链接。

```bash

pnpm install

```

  1. **启动项目**

```bash

同时启动所有应用

pnpm dev

或者单独启动

pnpm --filter @your-org/main dev

pnpm --filter @your-org/app1 dev:qiankun

```

  1. **访问地址**

**第十步. 构建和部署**

  1. **构建项目**

```bash

pnpm build

```

  1. **部署配置(Nginx 示例)**

```nginx

server {

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 组件库和请求封装。所有公共依赖都提取到了根目录进行统一管理,实现了代码复用和版本统一。