garfish+monorepo+pnpm搭建微前端框架

156 阅读7分钟

关于 monorepo 的概念本文不再赘述,至于为什么选用 pnpm 作为包管理器而不用 npm 或者 yarn ,参考Pnpm: 最先进的包管理器

pnpm 不仅有优化磁盘空间快速等优势,在这里主要pnpm 有一个 --filter属性

在menorepo(多依赖包仓库) 环境中指定只安装或更新某个依赖包到某个项目中

一.环境准备

  • Node.js:v16.0.0+(Garfish 对 Node 版本有基础要求)

  • pnpm:v7.0.0+(monorepo 核心工具)

1.全局安装pnpm

npm install -g pnpm

二.项目初始化

1.创建一个项目文件夹初始化package.json

如:

pnpm mkdir garfish-pnpm-monorepo

cd到这个文件夹中执行命令

npm init -y

2. 配置 pnpm monorepo 核心文件

在根目录创建 pnpm-workspace.yaml,指定工作区范围(所有子应用放在 packages 目录下):

# pnpm-workspace.yaml packages: 
# 主应用 
- 'packages/main-app'
# 子应用(可扩展多个)
- 'packages/vue-app' 
- 'packages/react-app' 
# 可选:公共工具包 
# - 'packages/utils'

三、创建主应用(main-app)

主应用是微前端的 “容器”,负责加载所有子应用,这里选择 Vue3 + Vite(Garfish 对 Vite 适配性好)。

1. 创建主应用目录并初始化

# 创建 packages 目录(存放所有应用) 
mkdir -p packages/main-app 
# 进入主应用目录,用 vite 创建 Vue3 项目 
cd packages/main-app 
pnpm create vite@latest . --template vue

2. 安装主应用依赖

# 回到根目录,安装主应用依赖(pnpm 会自动关联 workspace) 
cd ../../ 
pnpm install -F main-app # -F 表示指定 workspace 包

3.安装 Garfish 核心依赖

在根目录安装 Garfish(-Dw 表示开发依赖 + 工作区根共享,所有子应用也能复用):

pnpm add garfish @garfish/router -Dw
//安装 garfish还可以使用的命令,-W 表示安装在全局的 workspace 里, 这样所有 package 都可以共用该文件
pnpm add garfish @garfish/router -WD

4. 配置主应用(核心步骤)

修改主应用代码,集成 Garfish 并注册子应用:

步骤 4.1:修改主应用目录结构

packages/main-app/src 下创建 micro-app.js(专门管理 Garfish 配置),并修改 main.js 引入。

步骤 4.2:编写 Garfish 配置文件(micro-app.js)

// packages/main-app/src/micro-app.js 
import Garfish from 'garfish'; 
import { createRouter } from '@garfish/router'; 

// 初始化 Garfish 
export function initGarfish() { 
    // 1. 创建 Garfish 路由(与主应用路由联动) 
    const garfishRouter = createRouter(); 
    // 2. 配置并启动 
    Garfish Garfish.run({ 
    // 主应用根节点(子应用会挂载到这个节点下) 
    domGetter: '#micro-app-container', 
    // 开启沙箱(隔离子应用的样式/JS) 
    sandbox: { 
        open: true, // 开启沙箱 
        strictIsolation: true, // 严格隔离(样式不泄漏) 
    }, 
    
    // 注册子应用列表(核心!) 
    apps: [ 
        { 
            // 子应用唯一标识 
            name: 'vue-app', 
            // 子应用访问地址(本地开发地址,生产需替换为打包后的地址) 
            entry: 'http://localhost:5174', 
            // 子应用路由匹配规则(访问 /vue-app 时加载该子应用) 
            activeWhen: '/vue-app', 
            // 子应用基础路径(需与子应用配置一致) 
            basename: '/vue-app', 
        }, 
        { 
            name: 'react-app', 
            entry: 'http://localhost:5175', 
            activeWhen: '/react-app', 
            basename: '/react-app',
          },
       ], 
       // 挂载 Garfish 路由到主应用 
       router: garfishRouter, 
       // 全局生命周期(可选) 
       lifecycle: { beforeMount: (app) => { 
            console.log(`子应用 ${app.name} 即将挂载`); 
        }, 
       mounted: (app) => { 
            console.log(`子应用 ${app.name} 挂载完成`); 
           }, 
        }, 
    }); 
}

步骤 4.3:修改主应用入口文件(main.js)

// packages/main-app/src/main.js 
import { createApp } from 'vue' 
import './style.css' 
import App from './App.vue' 
import { initGarfish } from './micro-app.js' 

const app = createApp(App) 
app.mount('#app') 

// 初始化 Garfish(主应用挂载后启动微前端) 
initGarfish()

步骤 4.4:修改主应用页面(App.vue)

添加子应用挂载容器和导航链接:

<!-- packages/main-app/src/App.vue --> 
<template> 
    <div id="app"> 
        <!-- 主应用导航 --> 
        <nav style="padding: 20px; background: #f5f5f5;"> 
            <a href="/vue-app" style="margin-right: 20px;">Vue 子应用</a> 
            <a href="/react-app">React 子应用</a> 
        </nav> 
       <!-- 子应用挂载容器(需与 Garfish 配置的 domGetter 一致) --> 
        <div id="micro-app-container" style="margin: 20px;"></div>
    </div> 
</template> 

<script setup></script>
<style scoped></style>

步骤 4.5:修改主应用 Vite 配置(vite.config.js)

确保主应用端口固定(避免冲突),并允许跨域(子应用加载需要):

// packages/main-app/vite.config.js 
import { defineConfig } from 'vite' 
import vue from '@vitejs/plugin-vue' 
export default defineConfig({ 
    plugins: [vue()], 
    server: { 
        port: 5173, // 主应用固定端口 
        cors: true, // 允许跨域(必须!子应用加载主应用资源需要)
    }, 
})

四、创建子应用(以 Vue 子应用为例,React 同理)

1. 创建 Vue 子应用

# 回到根目录 cd ../../ 
# 创建 Vue 子应用目录 
mkdir -p packages/vue-app 
# 进入子应用目录,用 vite 创建 Vue3 项目 
cd packages/vue-app 
pnpm create vite@latest . --template vue

2. 安装子应用依赖

# 回到根目录安装 
cd ../../ 
pnpm install -F vue-app

// 还可以使用的命令
pnpm install --filter vue-app  // --filter 表示要作用到哪个子项目

3. 适配 Garfish 改造子应用(核心)

子应用需要适配微前端的 “挂载 / 卸载” 生命周期,且需配置基础路径、跨域等。

步骤 3.1:修改子应用 Vite 配置(vite.config.js)
// packages/vue-app/vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  // 关键:设置子应用基础路径(需与主应用 Garfish 配置的 basename 一致)
  base: '/vue-app/',
  server: {
    port: 5174, // 子应用固定端口(与主应用配置的 entry 端口一致)
    cors: true, // 允许跨域(主应用加载子应用需要)
    origin: 'http://localhost:5174', // 显式指定 origin(避免 Garfish 解析错误)
  },
  build: {
    // 打包后资源路径(避免子应用资源 404)
    assetsDir: 'assets',
    // 输出目录(可选,统一打包到根 dist 下)
    outDir: '../../dist/vue-app',
  },
})

步骤 3.2:修改子应用入口文件(main.js)

适配 Garfish 生命周期,支持 “独立运行” 和 “微前端运行” 两种模式:

// packages/vue-app/src/main.js
import { createApp } from 'vue' 
import './style.css' 
import App from './App.vue'

// 定义挂载函数(供 Garfish 调用) 
function render(props = {}) { 
    const { container } = props; 
     // 微前端模式:挂载到主应用指定容器;独立模式:挂载到 #app 
    const app = createApp(App);
    app.mount(container ? container.querySelector('#app') : '#app');
 }
 
// 独立运行时(直接访问子应用)
if (!window.__GARFISH__) { 
    render(); 
}

// 暴露 Garfish 所需的生命周期函数(必须!)
export const provider = { 
    // 子应用挂载时调用 
    render, 
    // 子应用卸载时调用(清理资源) 
    destroy({ container }) { 
        const app = document.querySelector(container ? container.querySelector('#app') : '#app'); 
        if (app) app.innerHTML = ''; 
     },
};
步骤 3.3:修改子应用根组件(App.vue)

确保子应用有独立的挂载节点 #app

<!-- packages/vue-app/src/App.vue --> 
<template> 
    <div id="app"> 
        <h1>Vue 子应用</h1> 
        <p>当前模式:{{ isGarfish ? '微前端' : '独立运行' }}</p>
    </div> 
</template> 
<script setup> 
import { ref, onMounted } from 'vue' 

const isGarfish = ref(false) 
onMounted(() => { 
    // 判断是否在 Garfish 微前端环境中 
    isGarfish.value = !!window.__GARFISH__
}) 
</script> 
<style scoped></style>

4. 创建 React 子应用(同理适配)

# 回到根目录 cd ../../ 
mkdir -p packages/react-app 
cd packages/react-app 
pnpm create vite@latest . --template react 
# 安装依赖 
cd ../../ 
pnpm install -F react-app
React 子应用适配关键配置
  1. vite.config.js(同 Vue 子应用,端口改为 5175,base 改为 /react-app/);
  2. src/main.jsx(适配 Garfish 生命周期):
import React from 'react' 
import ReactDOM from 'react-dom/client' 
import './index.css' 
import App from './App' 
function render(props = {}) { 
const { container } = props; 
const root = ReactDOM.createRoot( 
    container ? container.querySelector('#root') : document.getElementById('root') 
 ); 
 
root.render( 
    <React.StrictMode> 
        <App /> 
    </React.StrictMode> 
    );
} 
// 独立运行 
if (!window.__GARFISH__) { render(); } 
// 暴露生命周期 
export const provider = { 
    render, 
    destroy({ container }) { 
        const root = container ? container.querySelector('#root') : document.getElementById('root'); 
        if (root) ReactDOM.unmountComponentAtNode(root);
    },
};

五、统一管理运行脚本

在根目录的 package.json 中添加脚本,方便一键启动所有应用:

{ 
"scripts": { 
        "dev:main": "pnpm -F main-app dev", 
        "dev:vue": "pnpm -F vue-app dev",
        "dev:react": "pnpm -F react-app dev", 
        // 跨平台并行启动(需先安装 concurrently) 
        "dev": "concurrently \"pnpm dev:main\" \"pnpm dev:vue\" \"pnpm dev:react\"" 
    } 
}

安装 concurrently(跨平台并行运行命令):

pnpm add concurrently -Dw

六、测试运行

# 根目录执行
pnpm dev

启动成功后,访问主应用地址 http://localhost:5173

  • 点击 “Vue 子应用”,页面会加载 http://localhost:5174 的 Vue 子应用;
  • 点击 “React 子应用”,页面会加载 http://localhost:5175 的 React 子应用;
  • 直接访问 http://localhost:5174/http://localhost:5175,子应用也能独立运行。

七、生产环境打包(可选)

在根 package.json 添加打包脚本:

json

{
  "scripts": {
    "build:main": "pnpm -F main-app build",
    "build:vue": "pnpm -F vue-app build",
    "build:react": "pnpm -F react-app build",
    "build": "pnpm run build:main && pnpm run build:vue && pnpm run build:react"
  }
}

执行打包:

pnpm build

打包后,所有应用的产物会输出到对应的 dist 目录,只需将主应用和子应用的静态资源部署到服务器,并修改主应用 Garfish 配置中的 entry 为生产环境地址即可。

总结

  1. pnpm monorepo 核心:通过 pnpm-workspace.yaml 管理工作区,所有应用放在 packages 目录,依赖统一管理,避免重复安装;
  2. Garfish 主应用关键:配置 domGetter(子应用挂载容器)、apps(子应用列表)、开启跨域和沙箱隔离;
  3. 子应用适配关键:设置 base 基础路径、暴露 provider 生命周期函数、开启跨域,支持 “独立运行” 和 “微前端运行” 双模式。