{T}

组件库拆分与重用:el-admin-components 抽离

概述

真正把模板项目里的业务组件抽离成独立组件库,第一步不是「复制文件」,而是先决定组件库对 Element Plus 的依赖策略——这个决定会一路影响是否 external element-plus、是否把样式打进产物、包体积大小和能否脱离 admin 模板单独复用。本文以 el-admin-components 为例,讲清楚抽离工程里的两条并存的链路(库模式负责产物交付、Playground 负责本地验证)、npm link 联调的坑、包名/构建文件名/exports/类型声明必须对齐,以及迁移组件时最容易漏掉的样式系统与隐形前置依赖。

学习目标

  • 在抽离前先决定 Element Plus 依赖策略(消费方全局注入 vs 库内自带),而不是先复制组件
  • 理解 Playground 是组件库的第一个真实消费方,比直接发包更能暴露问题
  • 掌握 npm link 链接的是 package.json.name 而非目录名这一关键事实
  • 保证包名、构建文件名、exports 与类型声明路径四者一致,避免「能运行但类型失效」
  • 迁移组件时把样式系统、预处理器、Vue 宏、Auto Import 等隐形环境依赖一并迁过来

一、抽离前先决定 Element Plus 依赖策略

课程一开始就抛出两种使用方式:

  • 方案 A:消费方全局使用 Element Plus(通过 CDN 或 app.use(ElementPlus) 注入),组件库只依赖消费方已有的环境;
  • 方案 B:组件库内部自行带上 Element Plus 的组件和样式,更独立,但对非 admin 模板更友好,代价是包体积更大、升级更被动。

这两个方案没有绝对对错,但它们影响的东西完全不同:是否 external element-plus、是否需要把样式一并打包、还能不能方便走 CDN external、组件库是否强绑定某套 UI 生态。课程这里先实现「从 0 到 1」,优化问题留到后面,这个节奏是对的。

二、库模式与 Playground 是两条并存的链路

这套组件库工程通常同时包含三类目录:src/(组件源码)、playground/(本地调试与验证入口)、dist/(最终构建产物)。库模式由 build.libexports、类型声明等配置决定,用于构建可发布产物;Playground 是一个真实的消费方实验场。

ts
// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    lib: {
      entry: 'src/index.ts',
      name: 'ElAdminComponents',
      fileName: 'el-admin-components',
    },
    rollupOptions: {
      external: ['vue'],
    },
  },
})

Playground 不只是「随便放个页面」,它本质上是组件库的第一个真实消费方。课程后面大多数排错,其实都是通过 Playground 暴露出来的。

课程的本地联调路径是:在组件库根目录执行 npm link,再在 playground 里执行 npm link 包名。npm 官方文档明确:npm link 是两步过程,第二步使用的包名来自 package.json.name,不是目录名。这也解释了课程里的报错——目录名改了、包名改了,但 exports、构建文件名、Playground 导入名没完全同步,就会出现「模块能看到,但声明文件或导出成员对不上」。

bash
# 组件库项目
npm link
# playground 项目
npm link el-admin-components

npm link 默认不会把链接依赖写进 package.json。改了 package.json.name 后,所有基于包名的联调入口都要一起改。「目录名对了但仍然找不到模块」时,先检查包名,而不是先怀疑 Vite。

四、包名、构建文件名、exports、类型声明必须对齐

课程中反复碰到的问题本质上都指向一点:组件库构建产物有名字,package.json 暴露入口有名字,Playground 里导入的还是那个名字。这几处不一致时,就会出现「模块路径能解析、运行没问题,但 TS 报找不到声明文件或没有导出某个成员」的半联通状态。

json
{
  "name": "el-admin-components",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/el-admin-components.js",
      "require": "./dist/el-admin-components.umd.cjs"
    }
  }
}

类型声明能否被消费方识别,取决于 types / exports.types 是否对得上。课程里「重启扩展宿主 / 重新 install / link 后恢复正常」的根因,其实是入口和声明链路重新对齐了。

五、迁移组件最容易漏的是样式系统与隐形前置

把业务组件拷进 components 项目后,马上就会遇到依赖缺失。迁移的真实难点在于:你复制过来的不是一个纯 .vue 文件,它往往依赖 UnoCSS 原子类、UnoCSS reset、Sass、Vue 宏语法实验特性、Auto Import 约定、Element Plus 基础组件。正确姿势不是「复制后直接 build」,而是缺什么补什么,并且想清楚这个依赖是组件库需要还是 Playground 需要。

UnoCSS 在 Vite 组件库里要分成两段接入:构建期插 UnoCSS(),运行期入口导入 virtual:uno.css,如需 Tailwind 风格 reset 再导入 @unocss/reset/tailwind.css

ts
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import UnoCSS from 'unocss/vite'

export default defineConfig({
  plugins: [
    vue({
      script: {
        defineModel: true,
        propsDestructure: true,
      },
    }),
    UnoCSS(),
  ],
})
ts
// src/main.ts
import 'virtual:uno.css'
import '@unocss/reset/tailwind.css'

只复制 uno.config.ts 不等于 UnoCSS 已接通。Vue 宏语法(defineModel、props 解构)、computed 等自动导入、.scss 依赖都属于「隐形前置条件」,不补齐就会构建成功但体验不完整。sass 是明确的构建依赖,缺了会直接构建失败。

六、消费方全局注册 Element Plus 验证链路

如果组件库选「消费方全局使用 Element Plus」,那么 Playground 就必须先把 Element Plus 当成真实外部依赖装起来并全局注册。课程最后头像组件没正常渲染,原因不是组件逻辑错,而是它依赖的 ElAvatar 没有可用的 Element Plus 运行环境——这正好验证了前面的架构题。

ts
// playground/src/main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'

createApp(App).use(ElementPlus).mount('#app')

组件库项目和 Playground 项目是两套依赖语境,不要混成一个。「组件构建成功但页面空白 / 基础组件不渲染」时,优先检查 Playground 是否补齐了消费方依赖。npm link 联调时,构建产物更新、声明文件更新和 Playground 依赖刷新并不总是同步,经常需要重新安装或重新链接并重启编辑器 TS 服务。


常见问题

问题原因解决方案
npm link 后 Playground 仍找不到包用了目录名而不是 package.json.name检查组件库 name 字段,按包名重新 link
模块能导入但 TS 提示找不到声明文件types / exports.types 与产物路径不一致或旧缓存未刷新对齐 exports、重新构建、重新安装 / link 并刷新 TS 服务
业务组件迁移后样式全乱没补 UnoCSS 插件、入口样式或 reset配置 UnoCSS(),入口导入 virtual:uno.css 与对应 reset
构建报 Sass 相关错误组件依赖 scss / sass 但新项目没装安装 sass 并检查样式依赖链
头像组件只看到占位不显示 Element Plus 效果Playground 没全局安装 Element Plus在 Playground 安装 element-plusapp.use(ElementPlus)
改了包名后 Playground 又报模块不存在构建文件名、exports、导入名未同步统一包名、产物名、入口映射和导入名
组件迁移过去后缺什么爆什么迁的是「组件 + 工程环境」而非 .vue 文件逐项补齐宏语法、Auto Import、样式系统、预处理器和三方库依赖

延伸阅读