🎬 面试开场怎么引出这个话题
「在菲克斯的电力合规平台,基于 Snowy 二次开发,有 4 类测评业务(等保/密评/风评/数据安全)。一开始 4 个团队把代码全揉在一个项目里,冲突不断。后来我设计了插件化架构 — 每个业务做成独立插件,自己注册路由、菜单、权限,主框架只提供壳和公共能力。这样 4 个团队并行开发互不打架,某个业务下线直接摘掉插件就行。核心是用 Vue 的 app.use + 动态路由表实现的。」
✅ 抛出 4 个可深挖的点:① 插件化动机 ② 动态路由注册 ③ 插件间依赖 ④ 团队协作。
这是高级前端/前端负责人岗位的核心话题,体现架构能力。
1

为什么要插件化:All-in-one 的痛

❌ 单体项目(改造前)

  • 4 个团队改同一份 router.js,每天 merge 冲突
  • 菜单配置全在一个 menu.json,一改全乱
  • 某个业务下线,要在 N 个文件里删代码
  • 权限点散落各处,无法按业务隔离
  • 新人上手要理解整个项目

✅ 插件化(改造后)

  • 每个业务一个插件目录,自己管自己的路由
  • 插件自己注册菜单,主框架聚合
  • 业务下线 = 注释一行插件引入
  • 权限点封装在插件内,边界清晰
  • 新人只看自己负责的插件目录
🧠 类比
单体项目 = 一大家子住一个屋子,抢厕所打架
插件化 = 主框架是"小区物业",插件是"独立楼房",各自有独立的路由(门)、菜单(门牌)、权限(门禁卡),但共享小区的公共设施(用户登录、UI 框架、API 封装)。
插件的核心契约:我给你提供 install 函数,你(主框架)在合适的时机调我。
2

插件如何注册路由

🔑 插件的核心契约:install 函数
// src/plugins/dengbao/index.js —— 等保插件入口
export default {
  name: 'dengbao',         // 插件唯一标识

  // 插件提供的路由(每个插件有自己的 routes)
  routes: [
    {
      path: '/dengbao',
      component: () => import('./layouts/DengbaoLayout.vue'),
      children: [
        { path: 'task', component: () => import('./views/TaskList.vue') },
        { path: 'report', component: () => import('./views/Report.vue') },
      ]
    }
  ],

  // 菜单
  menus: [
    { title: '等保测评', icon: 'shield', path: '/dengbao/task' }
  ],

  // 权限点
  permissions: ['dengbao:view', 'dengbao:edit'],

  // install:Vue 插件标准钩子
  install(app, options) {
    // 可以注册全局组件、指令、provide 等
    app.component('DengbaoCard', DengbaoCard)
    app.provide('dengbaoConfig', options)
  }
}
📋 主框架:扫描插件 + 聚合路由
// src/plugins/index.js —— 主框架聚合所有插件
import { createApp } from 'vue'
import dengbao from './dengbao'
import mipin from './mipin'
import fengping from './fengping'
import dataSecurity from './data-security'

// 插件清单(要启用哪些,在这里配置)
const plugins = [dengbao, mipin, fengping, dataSecurity]

// 聚合所有插件的路由
export function collectPluginRoutes() {
  const routes = []
  plugins.forEach(p => {
    if (p.routes) routes.push(...p.routes)
  })
  return routes
}

// 聚合菜单
export function collectPluginMenus() {
  return plugins.flatMap(p => p.menus || [])
}

// 安装所有插件
const app = createApp(App)
plugins.forEach(p => app.use(p))
关键点: 路由聚合是运行时动态的,不是写死在 router.js。每个插件导出自己的 routes,主框架扫描聚合。这样加新业务 = 新建一个插件目录 + 在清单加一行,主框架代码零修改
权限路由的坑: 如果结合 RBAC 权限,路由不能在启动时全注册(否则用户能看到没权限的菜单)。要登录后根据用户权限动态 addRoute。流程:登录 → 拉权限 → 过滤插件路由 → router.addRoute → next()。这就是"动态路由"的核心。
3

插件如何注册菜单和权限

📋 菜单聚合 + 权限过滤
// 菜单 store:聚合插件菜单 + 按权限过滤
export const useMenuStore = defineStore('menu', () => {
  const menus = ref([])

  async function generateMenus(userPermissions) {
    const allMenus = collectPluginMenus()

    // 按用户权限过滤:菜单没权限点 = 公共可见;有权限点 = 用户得有这个权限
    menus.value = allMenus.filter(m => {
      if (!m.permission) return true
      return userPermissions.includes(m.permission)
    })
  }

  return { menus, generateMenus }
})
菜单顺序问题: 多个插件的菜单聚合后,顺序怎么定?给每个插件/菜单一个 order 字段,聚合时按 order 排序。这样产品要调整菜单顺序,改插件配置就行,不动主框架。
4

插件间依赖:公共能力抽离

⚠️ 问题

4 个插件都要用"测评报告生成""指标管理""文档比对"这些公共能力。如果每个插件自己写一份,重复且难维护;如果互相 import,插件间强耦合,摘掉一个另一个就崩。

解法:分层架构。 把公共能力抽到 "共享层(shared)",插件只依赖 shared,不互相依赖。shared 是稳定的、所有插件都能用的基础库。
🏗️ 三层架构
src/
├── core/              # 第1层:核心框架(壳、登录、布局、API封装)
│   ├── layouts/
│   ├── api/           # axios 封装、请求拦截
│   └── utils/
├── shared/            # 第2层:跨插件共享(报告生成、指标、文档比对)
│   ├── components/    # 业务公共组件
│   ├── composables/   # 公共逻辑
│   └── api/
└── plugins/           # 第3层:业务插件(互相独立)
    ├── dengbao/       # 等保:只依赖 core + shared
    ├── mipin/         # 密评:只依赖 core + shared
    ├── fengping/      # 风评
    └── data-security/ # 数据安全

# 规则:plugins 之间禁止互相 import,只能用 core 和 shared
# 用 ESLint 规则强制(no-restricted-imports)
强制约束: 用 ESLint 的 no-restricted-imports 规则,禁止 plugins 目录下的文件 import 其他 plugin。这样编译期就能防止插件耦合,比口头约定可靠。
5

动态导入与按需加载

路由懒加载: 所有插件路由用 () => import('xxx') 动态导入,Webpack/Vite 会把每个插件打成独立 chunk。用户访问等保,只加载等保的 chunk;不访问密评,密评的代码永远不下载。4 个业务互不影响首屏体积
插件启用/禁用: 通过环境变量控制哪些插件启用。VITE_PLUGINS=dengbao,mipin 表示只打包这两个。构建时用 Vite 的 import.meta.glob 动态收集启用的插件,实现"按需打包"。
6

目录约定 + 规范落地

📋 插件内部目录约定(每个插件都长这样)
src/plugins/dengbao/
├── index.js          # 插件入口(导出 install/routes/menus)
├── api/              # 本插件的后端接口
├── views/            # 页面
├── components/       # 本插件私有组件
├── composables/      # 本插件私有逻辑
├── store/            # 本插件私有状态
└── routes.js         # 本插件路由配置

# 命名约定:文件名 kebab-case,组件名 PascalCase
# API 文件按模块拆:user.js / task.js / report.js
规范落地: 光口头约定没用,要用工具强制。① ESLint 规则(命名、目录结构);② 自定义脚手架脚本(npm run new:plugin 一键生成标准插件目录);③ Code Review 把关。我们团队的"插件开发规范"文档 + 脚手架脚本,新人 10 分钟就能上手。
7

高频追问 & 容易翻车点

1什么是插件化?和微前端什么区别? 基础
插件化: 同一个项目内,把业务拆成独立模块(插件),编译时聚合。共享同一个 Vue 实例、同一个路由实例。微前端(qiankun/micro-app): 每个子应用独立部署、独立运行时,运行时通过框架组合。
区别: 插件化是"编译期拆分",共享构建;微前端是"运行期拆分",独立部署。插件化适合一个团队、一个仓库;微前端适合多团队、多仓库、独立部署。我们 4 个业务一个团队一个仓库,插件化够了。
💬 这个区分很重要,面试官常问。能说清楚"为什么不用微前端",体现你的技术选型判断力。
2动态路由是怎么实现的? 中等
核心:登录后根据权限 addRoute。 流程:① 定义基础路由(login/404);② 登录成功拉用户权限;③ 从插件聚合所有路由,按权限过滤;④ router.addRoute 逐个添加;⑤ addRoute 后 next({ ...to, replace: true }) 重新触发导航。
坑: addRoute 是异步的,必须 replace 一次否则白屏;404 路由要最后注册,否则动态路由匹配不到。
3插件之间怎么避免耦合? 中等
三层架构 + ESLint 强制。 core(核心)、shared(共享业务能力)、plugins(业务插件)。插件只能依赖 core 和 shared,插件之间禁止互相 import。用 ESLint 的 no-restricted-imports 规则,plugins 目录下的文件 import 其他 plugin 直接报错。编译期约束 > 口头约定
4插件如何按需打包? 中等
路由懒加载:所有路由用动态 import,Vite 自动按插件分 chunk;② 环境变量控制启用插件:VITE_PLUGINS=dengbao,mipin,用 import.meta.glob 只收集启用的插件;③ 不同客户/部署只打包需要的业务,减小体积。
5菜单/权限是前端管还是后端管? 基础
权限点后端管(权威),菜单展示前端聚合。 后端存"用户拥有哪些权限码",前端插件声明"我的菜单需要什么权限码",登录后前端拉权限码列表,过滤出有权限的菜单展示。安全要点: 前端隐藏菜单只是体验优化,真正的权限校验在后端接口(前端可以绕过)。
6Vue 的 app.use 原理是什么? 原理题
app.use(plugin, options) 会调用 plugin.install(app, options)。 如果 plugin 是对象,调它的 install;如果是函数,直接当 install 调。install 里可以注册全局组件(app.component)、指令(app.directive)、provide、全局属性等。这是 Vue 插件的标准机制。我们的插件 routes/menus 是自定义约定(不在 install 里),install 只做组件注册,路由聚合是单独的 collectPluginRoutes 函数。
💬 体现你理解 Vue 插件机制的本质,不是只会调 API。
8

30 秒电梯陈述(背熟)

🎤 30 秒版
「在菲克斯的电力合规平台,基于 Snowy 二次开发,有 4 类测评业务。我设计了插件化架构 — 每个业务做成独立插件,自己注册路由、菜单、权限,主框架只提供壳和公共能力。核心是 Vue 的 app.use + 动态路由聚合,登录后按权限 addRoute。这样 4 个团队并行开发,代码互不冲突,业务下线直接摘插件。为了防止插件耦合,我做了三层架构(core/shared/plugins)+ ESLint 强制约束,plugins 之间禁止互相 import。」
这段话的优势:
① 有业务规模(4 类业务、多团队)→ 真实
② 有架构设计(插件化 + 三层)→ 体现架构能力
③ 有技术细节(动态路由、ESLint 约束)→ 可深聊
④ 有权衡(为什么不用微前端)→ 体现选型判断
这是冲"高级前端/前端负责人"岗位的王牌话题。

面试亮点专题 05 — Vue3 插件化架构深度话题

插件化 vs 微前端 · 动态路由 addRoute · 三层架构 · ESLint 约束 · 按需打包 · app.use 原理

← 返回专题首页