使用 Vant CLI 搭建 UI 组件库:从 H5 到小程序跨平台

使用 Vant CLI 搭建 UI 组件库:从 H5 到小程序跨平台

本文详细介绍如何使用 Vant CLI 搭建两种不同类型的 UI 组件库:Vue 3 移动端 H5 组件库微信/支付宝小程序跨平台组件库,涵盖完整的搭建过程、目录结构、关键配置。


目录

  • [一、Vant CLI 简介](#一、Vant CLI 简介 "#%E4%B8%80vant-cli-%E7%AE%80%E4%BB%8B")
  • [二、项目一:Axi-UI --- Vue 3 移动端组件库](#二、项目一:Axi-UI — Vue 3 移动端组件库 "#%E4%BA%8C%E9%A1%B9%E7%9B%AE%E4%B8%80axi-ui--vue-3-%E7%A7%BB%E5%8A%A8%E7%AB%AF%E7%BB%84%E4%BB%B6%E5%BA%93")
  • [三、项目二:miniapp-ui --- 微信/支付宝小程序跨平台组件库](#三、项目二:miniapp-ui — 微信/支付宝小程序跨平台组件库 "#%E4%B8%89%E9%A1%B9%E7%9B%AE%E4%BA%8Cminiapp-ui--%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98%E5%AE%9D%E5%B0%8F%E7%A8%8B%E5%BA%8F%E8%B7%A8%E5%B9%B3%E5%8F%B0%E7%BB%84%E4%BB%B6%E5%BA%93")
  • 四、技术对比总结

一、Vant CLI 简介

Vant CLI 是 Vant 官方提供的组件库开发工具,核心能力包括:

  • 项目脚手架:约定式目录结构,自动发现组件
  • 文档站点:基于 README.md + demo/index.vue 自动生成文档站点
  • 开发服务vant-cli dev 启动文档预览
  • 代码规范:内置 ESLint + Prettier 配置
  • 构建打包vant-cli build 构建组件库(输出 es/lib 目录)

关键认知:Vant CLI 是脚手架工具,构建目标(H5 还是小程序)由你的源码写法和构建配置决定。


二、项目一:Axi-UI --- Vue 3 移动端组件库

2.1 项目定位

基于 Vue 3 的移动端 H5 UI 组件库,标准 Vant CLI 使用方式。

2.2 搭建步骤

第一步:环境准备
bash 复制代码
# Node.js >= 16.0.0
node -v
第二步:创建项目并安装依赖
bash 复制代码
mkdir Axi-UI && cd Axi-UI
npm init -y

# 安装核心依赖
npm i @vant/cli @vant/eslint-config eslint prettier vue@3 sass -D
npm i @vant/cli -D  # 如果上面没装好,单独执行
第三步:配置 package.json
json 复制代码
{
  "name": "Axi-UI",
  "version": "1.0.0",
  "main": "lib/Axi-UI.js",
  "module": "es/index.js",
  "style": "lib/index.css",
  "typings": "lib/index.d.ts",
  "files": ["lib", "es"],
  "scripts": {
    "dev": "vant-cli dev",
    "lint": "vant-cli lint",
    "build": "vant-cli build",
    "build:site": "vant-cli build-site"
  },
  "peerDependencies": {
    "vue": "^3.3.4"
  },
  "devDependencies": {
    "@vant/cli": "^7.0.0",
    "vue": "^3.3.4",
    "sass": "^1.49.7"
  },
  "nano-staged": {
    "*.md": "prettier --write",
    "*.{ts,tsx,js,vue,less,scss}": "prettier --write",
    "*.{ts,tsx,js,vue}": "eslint --fix"
  },
  "eslintConfig": {
    "root": true,
    "extends": ["@vant"]
  },
  "prettier": {
    "singleQuote": true
  },
  "browserslist": ["Chrome >= 51", "iOS >= 10"]
}
第四步:创建 vant.config.mjs(⚠️ 必须用 .mjs 后缀)
js 复制代码
export default {
  name: 'Axi-UI',
  build: {
    css: { preprocessor: 'sass' },
    site: { publicPath: '/Axi-UI/' },
  },
  site: {
    title: 'Axi-UI',
    logo: 'https://fastly.jsdelivr.net/npm/@vant/assets/logo.png',
    nav: [
      {
        title: '开发指南',
        items: [
          { path: 'home', title: '介绍' },
          { path: 'quickstart', title: '快速上手' },
        ],
      },
      {
        title: '基础组件',
        items: [
          { path: 'demo-button', title: 'DemoButton 按钮' },
        ],
      },
    ],
  },
};
第五步:创建目录和文件
bash 复制代码
Axi-UI/
├── vant.config.mjs            # Vant CLI 配置
├── package.json               # 项目配置
├── docs/                      # 文档目录
│   ├── home.md                # 介绍页
│   └── quickstart.md          # 快速上手
└── src/                       # 组件源码
    └── demo-button/           # 组件目录
        ├── index.vue          # 组件实现(模板+脚本+样式)
        ├── README.md          # 组件文档
        ├── demo/
        │   └── index.vue      # 组件演示(文档站点右栏渲染)
        └── test/
            └── index.spec.js  # 单元测试

组件源码 (src/demo-button/index.vue)

vue 复制代码
<template>
  <button class="demo-button">
    <slot />
  </button>
</template>

<script>
export default {
  name: 'DemoButton',
  props: {
    color: String,
    type: {
      type: String,
      default: 'primary',
    },
  },
};
</script>

<style lang="scss">
.demo-button {
  min-width: 120px;
  color: #fff;
  font-size: 16px;
  line-height: 36px;
  background-color: #f44;
  border: none;
  border-radius: 30px;
}
</style>

组件演示 (src/demo-button/demo/index.vue)

vue 复制代码
<template>
  <demo-block title="基础用法">
    <demo-button type="primary" style="margin-left: 15px">按钮</demo-button>
  </demo-block>

  <demo-block title="自定义颜色">
    <demo-button color="#03a9f4" style="margin-left: 15px">按钮</demo-button>
  </demo-block>
</template>

<script setup>
import DemoButton from '../index.vue';
</script>

关键<demo-block> 是 Vant CLI 提供的内置组件,用于文档站点的示例区块展示。

组件文档 (src/demo-button/README.md)

markdown 复制代码
# DemoButton 按钮

### 介绍
DemoButton 是一个示例按钮组件

### 引入
import { DemoButton } from 'Axi-UI';
Vue.use(DemoButton);

## 代码演示
### 基础用法
<demo-button type="primary" />

## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| type | 按钮类型 | _string_ | `primary` |
| color | 按钮颜色 | _string_ | - |

### Events
| 事件名 | 说明 | 回调参数 |
|--------|------|----------|
| click | 点击时触发 | _event: MouseEvent_ |

### Slots
| 名称 | 说明 |
|------|------|
| default | 默认插槽 |
第六步:启动
bash 复制代码
npm run dev
# 访问 http://localhost:3000/

2.3 最终目录结构

bash 复制代码
Axi-UI/
├── vant.config.mjs
├── package.json
├── .eslintignore
├── .gitignore
├── .husky/
│   ├── commit-msg
│   └── pre-commit
├── docs/
│   ├── home.md
│   └── quickstart.md
├── src/
│   └── demo-button/
│       ├── index.vue
│       ├── README.md
│       ├── demo/
│       │   └── index.vue
│       └── test/
│           ├── index.spec.js
│           └── __snapshots__/
│               └── index.spec.js.snap
├── es/                          # 构建产物(ES Module)
├── lib/                         # 构建产物(CommonJS)
└── site-dist/                   # 文档站点构建产物

2.4 技术栈

层级 技术
框架 Vue 3
构建工具 @vant/cli v7
CSS 预处理 Sass (SCSS)
代码规范 ESLint + Prettier
Git 钩子 Husky + nano-staged
测试 Jest + @vue/test-utils
输出格式 CommonJS + ESM 双格式

三、项目二:miniapp-ui --- 微信/支付宝小程序跨平台组件库

3.1 项目定位

基于 Vant CLI + Gulp 的微信/支付宝小程序跨平台 UI 组件库,一套源码,双平台输出

3.2 核心设计思路

css 复制代码
src/weapp/button/   ──→  构建(PLATFORM=weapp)  ──→  dist/weapp/button/
src/alipay/button/  ──→  构建(PLATFORM=alipay) ──→  dist/alipay/button/
src/common/          ──→  被两边 import 复用     ──→  公共逻辑共享
  • 组件按平台分目录src/weapp/src/alipay/ 各自独立
  • 公共逻辑复用src/common/ 存放双平台共享代码
  • 构建按平台分离 :通过 cross-env PLATFORM=weapp|alipay 切换构建目标

3.3 搭建步骤

第一步:创建项目并安装依赖
bash 复制代码
mkdir miniapp-ui && cd miniapp-ui
npm init -y

# 安装核心依赖
npm i @vant/cli @vant/eslint-config eslint prettier vue@3 sass -D

# 安装构建相关
npm i cross-env gulp gulp-rename gulp-sass gulp-typescript miniprogram-api-typings -D
第二步:配置 package.json
json 复制代码
{
  "name": "miniapp-ui",
  "version": "1.0.0",
  "description": "微信/支付宝小程序跨平台UI组件库",
  "main": "dist/weapp/index.js",
  "files": ["dist"],
  "scripts": {
    "dev": "vant-cli dev",
    "lint": "vant-cli lint",
    "build:weapp": "cross-env PLATFORM=weapp gulp -f build/compiler.js",
    "build:alipay": "cross-env PLATFORM=alipay gulp -f build/compiler.js",
    "build": "npm run build:weapp && npm run build:alipay",
    "dev:weapp": "cross-env PLATFORM=weapp gulp -f build/compiler.js && echo '请在微信开发者工具中打开 dist/weapp/'",
    "dev:alipay": "cross-env PLATFORM=alipay gulp -f build/compiler.js && echo '请在支付宝IDE中打开 dist/alipay/'",
    "watch:weapp": "cross-env PLATFORM=weapp gulp watch -f build/compiler.js",
    "watch:alipay": "cross-env PLATFORM=alipay gulp watch -f build/compiler.js"
  },
  "devDependencies": {
    "@vant/cli": "^7.1.0",
    "@vant/eslint-config": "^4.0.0",
    "cross-env": "^10.1.0",
    "eslint": "^8.57.1",
    "gulp": "^5.0.1",
    "gulp-rename": "^2.1.0",
    "gulp-sass": "^6.0.1",
    "gulp-typescript": "^6.0.0-alpha.1",
    "miniprogram-api-typings": "^5.2.1",
    "prettier": "^3.9.5",
    "sass": "^1.101.0",
    "vue": "^3.5.40"
  },
  "eslintConfig": {
    "root": true,
    "extends": ["@vant"]
  },
  "prettier": {
    "singleQuote": true
  }
}
第三步:配置 vant.config.mjs
js 复制代码
export default {
  name: 'miniapp-ui',
  build: {
    css: { preprocessor: 'sass' },
    site: { publicPath: '/' },
    srcDir: 'src/weapp',
  },
  site: {
    defaultLang: 'zh-CN',
    locales: {
      'zh-CN': {
        title: 'miniapp-ui',
        description: '微信/支付宝小程序组件库',
        logo: 'https://fastly.jsdelivr.net/npm/@vant/assets/logo.png',
        langLabel: '中文',
        nav: [
          {
            title: '开发指南',
            items: [
              { path: 'home', title: '介绍' },
              { path: 'quickstart', title: '快速上手' },
            ],
          },
          {
            title: '基础组件',
            items: [
              { path: 'button', title: 'Button 按钮' },
              { path: 'badge', title: 'Badge 徽标' },
              { path: 'swiper', title: 'Swiper 轮播' },
            ],
          },
        ],
      },
    },
    versions: [],
  },
};

⚠️ 注意:nav 必须放在 locales['zh-CN'] 内部,不能放在顶层 site 下。

第四步:配置 tsconfig.json
json 复制代码
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "declaration": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "lib": ["ES2020"],
    "types": ["miniprogram-api-typings"]
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
第五步:创建 Gulp 构建脚本 build/compiler.js
js 复制代码
const gulp = require('gulp');
const rename = require('gulp-rename');
const sass = require('gulp-sass')(require('sass'));
const ts = require('gulp-typescript');
const path = require('path');
const fs = require('fs');

const PLATFORM = process.env.PLATFORM; // 'weapp' | 'alipay'
const ROOT = path.resolve(__dirname, '..');
const tsProject = ts.createProject(path.join(ROOT, 'tsconfig.json'));

const platformConfig = {
  weapp:  { templateExt: '.wxml', styleExt: '.wxss' },
  alipay: { templateExt: '.axml', styleExt: '.acss' },
};

const config = platformConfig[PLATFORM];
if (!config) {
  throw new Error(`未知平台: ${PLATFORM}`);
}

const srcDir = path.join(ROOT, 'src', PLATFORM);
const commonDir = path.join(ROOT, 'src', 'common');
const distDir = path.join(ROOT, 'dist', PLATFORM);

// 清理
gulp.task('clean', (done) => {
  if (fs.existsSync(distDir)) {
    fs.rmSync(distDir, { recursive: true, force: true });
  }
  done();
});

// 编译 TypeScript
gulp.task('compile-ts', () => {
  return gulp
    .src([`${srcDir}/**/*.ts`, `${commonDir}/**/*.ts`])
    .pipe(tsProject())
    .pipe(gulp.dest(distDir));
});

// 拷贝模板文件
gulp.task('copy-template', () => {
  return gulp.src(`${srcDir}/**/*${config.templateExt}`).pipe(gulp.dest(distDir));
});

// 拷贝 json 配置
gulp.task('copy-json', () => {
  return gulp.src(`${srcDir}/**/*.json`).pipe(gulp.dest(distDir));
});

// 编译 scss → wxss/acss
gulp.task('compile-scss', () => {
  return gulp
    .src(`${srcDir}/**/*.scss`)
    .pipe(sass().on('error', sass.logError))
    .pipe(rename({ extname: config.styleExt }))
    .pipe(gulp.dest(distDir));
});

// 拷贝 README
gulp.task('copy-readme', () => {
  return gulp.src(`${srcDir}/**/README.md`).pipe(gulp.dest(distDir));
});

// 拷贝静态资源
gulp.task('copy-assets', () => {
  return gulp.src(`${srcDir}/**/*.{png,jpg,jpeg,gif,svg}`).pipe(gulp.dest(distDir));
});

// Watch 监听
gulp.task('watch', () => {
  const watcher = gulp.watch(
    [`${srcDir}/**/*`, `${commonDir}/**/*`],
    gulp.series('build')
  );
  watcher.on('change', (filePath) => {
    console.log(`[${PLATFORM}] 文件变更: ${filePath}`);
  });
  console.log(`[${PLATFORM}] 开始监听文件变更...`);
  console.log(`[${PLATFORM}] 请在对应开发者工具中打开: dist/${PLATFORM}/`);
});

// 构建
gulp.task(
  'build',
  gulp.series(
    'clean',
    gulp.parallel('compile-ts', 'copy-template', 'copy-json', 'compile-scss', 'copy-readme', 'copy-assets')
  )
);

gulp.task('default', gulp.series('build'));
第六步:创建微信小程序组件(以 Button 为例)

模板 src/weapp/button/index.wxml

html 复制代码
<button
  class="custom-class {{type === 'primary' ? 'button--primary' : ''}} {{disabled ? 'button--disabled' : ''}}"
  disabled="{{disabled}}"
  bindtap="onClick"
>
  <view wx:if="{{loading}}" class="button__loading"></view>
  <slot></slot>
</button>

逻辑 src/weapp/button/index.ts

ts 复制代码
Component({
  properties: {
    type:     { type: String,  value: 'default' },
    size:     { type: String,  value: 'default' },
    disabled: { type: Boolean, value: false },
    loading:  { type: Boolean, value: false },
  },
  methods: {
    onClick(event: WechatMiniprogram.TouchEvent) {
      if (!this.data.disabled && !this.data.loading) {
        this.triggerEvent('click', event);
      }
    },
  },
});

组件配置 src/weapp/button/index.json

json 复制代码
{
  "component": true,
  "usingComponents": {}
}

样式 src/weapp/button/index.scss

scss 复制代码
.button {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 16px;
  height: 44px;
  font-size: 16px;
  border-radius: 4px;
  color: #323233;
  background-color: #f7f8fa;
  border: 1px solid #ebedf0;

  &--primary { color: #fff; background-color: #1989fa; border-color: #1989fa; }
  &--warn    { color: #fff; background-color: #ee0a24; border-color: #ee0a24; }
  &--disabled { opacity: 0.5; }
  &--mini { padding: 0 8px; height: 32px; font-size: 13px; }
}
第七步:创建支付宝小程序组件

模板 src/alipay/button/index.axml(注意语法差异):

html 复制代码
<button
  class="custom-class {{type === 'primary' ? 'button--primary' : ''}} {{disabled ? 'button--disabled' : ''}}"
  disabled="{{disabled}}"
  onTap="onClick"
>
  <view a:if="{{loading}}" class="button__loading"></view>
  <slot></slot>
</button>

逻辑 src/alipay/button/index.ts(API 差异):

ts 复制代码
Component({
  props: {
    type:     'default' as 'default' | 'primary' | 'warn',
    size:     'default' as 'default' | 'mini',
    disabled: false,
    loading:  false,
  },
  methods: {
    onClick() {
      if (!this.props.disabled && !this.props.loading) {
        this.props.onClick?.();
      }
    },
  },
});
第八步:启动
bash 复制代码
# 启动文档站点
npm run dev

# 构建微信小程序
npm run dev:weapp
# → 用微信开发者工具打开 dist/weapp/

# 构建支付宝小程序
npm run dev:alipay
# → 用支付宝 IDE 打开 dist/alipay/

# Watch 模式(修改源码自动构建)
npm run watch:weapp
npm run watch:alipay

3.4 最终目录结构

bash 复制代码
miniapp-ui/
├── vant.config.mjs
├── package.json
├── tsconfig.json
├── build/
│   └── compiler.js              # Gulp 构建脚本
├── src/
│   ├── weapp/                   # 微信小程序组件
│   │   ├── button/
│   │   │   ├── index.wxml       # 模板
│   │   │   ├── index.scss       # 样式 → .wxss
│   │   │   ├── index.ts         # 逻辑
│   │   │   ├── index.json       # 组件配置
│   │   │   ├── README.md        # 组件文档
│   │   │   └── demo/
│   │   │       └── index.vue    # 文档站点演示
│   │   ├── badge/
│   │   │   └── ... (同上结构)
│   │   └── swiper/
│   │       └── ... (同上结构)
│   ├── alipay/                  # 支付宝小程序组件
│   │   ├── button/
│   │   │   ├── index.axml       # 模板(支付宝语法)
│   │   │   ├── index.scss       # 样式 → .acss
│   │   │   ├── index.ts         # 逻辑(支付宝 API)
│   │   │   ├── index.json
│   │   │   └── README.md
│   │   ├── badge/
│   │   └── swiper/
│   └── common/
│       └── utils.ts             # 公共工具函数
├── dist/                        # 构建产物
│   ├── weapp/
│   └── alipay/
└── docs/
    └── zh-CN/
        ├── home.md
        └── quickstart.md

3.5 微信 vs 支付宝关键差异

项目 微信小程序 支付宝小程序
模板后缀 .wxml .axml
样式后缀 .wxss .acss
事件绑定 bindtap="onXxx" onTap="onXxx"
条件渲染 wx:if a:if
列表渲染 wx:for a:for
组件属性定义 properties: {} props: {}
属性访问 this.properties.xxx this.props.xxx
数据访问 this.data.xxx this.data.xxx
触发事件 this.triggerEvent('click', e) this.props.onXxx?.()
生命周期 lifetimes.attached() didMount()


四、技术对比总结

维度 Axi-UI miniapp-ui
目标平台 H5 移动端 微信 + 支付宝小程序
框架 Vue 3 小程序原生 + TypeScript
组件格式 .vue 单文件 .wxml/.axml + .scss + .ts
构建工具 Vant CLI 内置 Vite Gulp 自定义构建
CSS 预处理 Sass Sass
输出产物 es/ + lib/ dist/weapp/ + dist/alipay/
开发命令 npm run dev npm run watch:weapp
预览方式 浏览器 小程序开发者工具
文档站点 ✅ 支持 ✅ 支持(demo 用 .vue 模拟)
单元测试 Jest 待扩展

结语

Vant CLI 是一个优秀的组件库开发工具,既可以搭建标准的 Vue 3 H5 组件库,也可以通过自定义 Gulp 构建流程扩展为小程序跨平台组件库。两个项目(Axi-UI 和 miniapp-ui)展示了这两种典型场景的完整搭建过程。

希望本文能帮助到正在搭建组件库的开发者,欢迎交流讨论!

相关推荐
默_笙1 小时前
😭 我花了两小时找了一个数据结构 bug,才把"迷你 Cursor"跑起来,绝望(bushi)
前端·javascript
竹林8181 小时前
wagmi v2 监听合约事件踩坑记:从 `useContractEvent` 到 `watchContractEvent`,我重构了三版才搞定实时数据流
前端·javascript
天若有情6732 小时前
纯HTML+Tailwind单页作品集网站——零框架静态前端完整源码
前端·html
WebGirl2 小时前
H5唤起app前端实现方案
前端
heimeiyingwang2 小时前
【架构实战】SQL注入与XSS防御:常见Web漏洞的系统性修复
前端·sql·架构
xingcheng2 小时前
Vue 2 + JavaScript编码规范skill
javascript
Larcher2 小时前
从零实现一个可运行的 RAG - LangChain、内存向量库与检索增强生成
前端·javascript
mONESY2 小时前
Vue3流式调用大模型接口完整实践
javascript