OpenTiny NEXT 从入门到精通·第 2 篇

OpenTiny NEXT 从入门到精通·第 2 篇:组件篇------TinyVue 核心组件库深度实战

组件库是前端应用的"乐高积木"。TinyVue 作为 OpenTiny 生态的核心 UI 组件库,拥有 130+ 企业级组件,覆盖中后台开发的绝大部分场景。但会用组件只是第一步,理解其 Renderless 无渲染架构 、掌握 主题定制 、学会 封装自定义组件,才是从普通使用者到进阶开发者的关键一跃。本篇将带你深度实战 TinyVue,解锁它的真正威力。

在日常开发中,你是否遇到过这些问题:

  • 同一个项目里,既要支持 Vue 2,又要支持 Vue 3,组件库需要维护两套代码,API 还不一致?
  • 想封装一个业务组件,但为了适配不同框架(Vue / React),要写好几套实现?
  • 组件样式需要根据品牌色动态切换,但传统 CSS 方案改起来非常痛苦?

TinyVue 的 Renderless 架构 正是为了解决这些问题而生的。它将组件的逻辑模板样式三者分离,让核心逻辑与框架解耦,一套代码可以同时运行在 Vue 2 / Vue 3 / React 等多个框架中。

本篇文章将从以下几个方面展开:

  1. TinyVue 组件库全景概览与特色组件介绍
  2. Renderless 无渲染架构深度解析
  3. 主题定制与样式系统
  4. 从零封装一个自定义组件(含 Vue 2/3 适配)

一、TinyVue 组件库概览

1.1 130+ 组件的全景介绍

TinyVue 的组件按照功能可以分为以下几大类:

分类 组件示例 使用场景
基础组件 Button、Icon、Link、Typography 页面基础元素
布局组件 Grid、Layout、Divider、Space 页面结构搭建
表单组件 Input、Select、Checkbox、Radio、DatePicker、Upload、Form 数据录入与校验
数据展示 Table、Tree、Pagination、Tag、Badge、Card、Carousel 数据列表与详情展示
反馈组件 Modal、Message、Notification、Popconfirm、Progress 用户交互反馈
导航组件 Menu、Tabs、Breadcrumb、Steps、Dropdown 页面导航与路由
高阶组件 Split、IpAddress、CalendarView、Crop、RichText 特殊业务场景

每个组件都遵循 OpenTiny Design 设计规范,保证视觉一致性和交互体验。

1.2 高频组件特性速览

Table 表格组件

  • 支持固定表头、固定列、多级表头
  • 内置虚拟滚动,轻松处理 10 万+ 行数据
  • 行内编辑、行拖拽排序、列拖拽调整宽度
  • 树形表格、可展开行

Tree 树形控件

  • 支持懒加载、拖拽节点、复选框
  • 自定义节点内容、节点过滤
  • 异步加载大数据量时自动启用虚拟滚动

Select 选择器

  • 支持远程搜索、分组、创建新条目
  • 虚拟滚动支持上千条选项不卡顿
  • 多选、可清空、可禁用选项

Form 表单

  • 支持表单校验(内置多种校验规则,可自定义)
  • 动态表单项、表单联动
  • 支持布局模式(inline、block)

1.3 OpenTiny 特色组件详解

除了常规组件,TinyVue 还提供了一些业界少见的特色组件,这些组件来源于华为内部业务的实际需求,经过大量生产验证。

Split 面板分隔器

可拖拽调整左右或上下两个面板大小的组件,常用于后台管理系统的左右布局。

vue 复制代码
<template>
  <tiny-split v-model="splitSize" mode="horizontal">
    <template #left>左侧面板(可拖拽调整)</template>
    <template #right>右侧面板</template>
  </tiny-split>
</template>
<script setup>
import { ref } from 'vue'
import { TinySplit } from '@opentiny/vue'
const splitSize = ref(0.3) // 左侧占比 30%
</script>
IpAddress IP 地址输入框

专门用于 IPv4 地址输入的组件,自动分隔四段,支持键盘快速跳转。

vue 复制代码
<tiny-ip-address v-model="ip" />
CalendarView 日历视图

比常规的 DatePicker 更强大的日历组件,支持月/周/日视图切换,可自定义渲染内容(如日程安排)。

vue 复制代码
<tiny-calendar-view v-model="currentDate" :events="calendarEvents" />
Crop 图片裁切

提供可视化裁剪框,支持固定比例、自由裁剪,输出 base64 或 Blob,便于上传。

vue 复制代码
<tiny-crop :src="imageUrl" :aspectRatio="16/9" @crop="handleCrop" />

二、Renderless 无渲染架构深度解析

2.1 组件与框架分离的设计理念

传统组件库的代码组织方式通常是"框架 + 组件逻辑"强耦合的。以 Vue 组件为例,模板(template)、逻辑(script)、样式(style)都写在同一个 .vue 文件中,这种结构直接绑定了 Vue 框架。

TinyVue 采用的 Renderless 架构 则将组件拆分为三个独立层:

复制代码
┌─────────────────────────────────────┐
│           框架适配层                  │
│    (Vue2 适配器 / Vue3 适配器 / React适配器)   │
├─────────────────────────────────────┤
│          无渲染逻辑层 (Renderless)    │
│    (状态管理、事件处理、生命周期、API)     │
├─────────────────────────────────────┤
│           样式层 (Theme)             │
│    (CSS Variables / 主题变量)        │
└─────────────────────────────────────┘
  • 逻辑层(Renderless):纯 TypeScript 实现,包含组件的状态、计算属性、方法、生命周期等,完全不依赖任何 UI 框架。这部分代码可以跨框架复用。
  • 模板层(Template):框架特定的模板代码,负责将逻辑层的数据和事件绑定到 DOM 上。不同框架有不同的模板语法,但调用的都是同一套逻辑层 API。
  • 样式层(Style):CSS 样式,使用 CSS Variables 实现主题变量,与框架无关。

2.2 一套代码同时支持 Vue 2 和 Vue 3------版本适配器如何抹平差异

Vue 2 和 Vue 3 在响应式原理、生命周期、API 等方面存在差异。TinyVue 通过 版本适配器(Version Adapter) 来抹平这些差异。

适配器核心思路

  • 将 Vue 2 的 Options API 和 Vue 3 的 Composition API 封装成统一调用接口。
  • 逻辑层代码只调用适配器提供的统一方法(如 useReactiveuseMounted),不直接依赖 Vue。
  • 适配器根据当前 Vue 版本动态选择底层实现。

简化示例(Button 组件)

typescript 复制代码
// renderless/button.ts ------ 纯逻辑层,无框架依赖
export function useButton(props, { emit }) {
  const handleClick = (event) => {
    if (!props.disabled) {
      emit('click', event);
    }
  };
  return { handleClick };
}
vue 复制代码
<!-- vue3/Button.vue ------ Vue 3 模板层 -->
<template>
  <button :class="classes" @click="handleClick">
    <slot />
  </button>
</template>
<script setup>
import { useButton } from '../renderless/button'
const props = defineProps(['disabled'])
const emit = defineEmits(['click'])
const { handleClick } = useButton(props, { emit })
</script>
vue 复制代码
<!-- vue2/Button.vue ------ Vue 2 模板层 -->
<template>
  <button :class="classes" @click="handleClick">
    <slot />
  </button>
</template>
<script>
import { useButton } from '../renderless/button'
export default {
  props: ['disabled'],
  setup(props, { emit }) {
    return useButton(props, { emit })
  }
}
</script>

通过这种方式,Button 的核心逻辑(useButton)只写一次,Vue 2 和 Vue 3 的模板各自引用同一份逻辑,保证了 API 和行为的完全一致。

2.3 一套代码同时支持 PC 和移动端------多端同源的技术实现

传统组件库往往分 PC 版和移动版(如 Ant Design 和 Ant Design Mobile),需要维护两套代码。TinyVue 通过 响应式断点 + 适配器 实现了多端同源。

实现原理

  1. 组件内部通过 useBreakpoint 检测当前屏幕宽度,返回 'pc''mobile'
  2. 模板层根据端类型渲染不同的 DOM 结构和交互模式(如 PC 端是弹窗选择器,移动端是底部抽屉)。
  3. 样式层通过媒体查询自动适配不同屏幕。

示例:DatePicker 组件的多端适配

  • PC 端:弹出浮层日历面板,鼠标交互。
  • 移动端:底部弹出滚动选择器,触摸优化。

开发者只需引入同一个组件,无需关心端差异,TinyVue 会自动适配。

三、主题定制与样式系统

3.1 基于 CSS Variables 的动态主题系统

TinyVue 的主题系统基于 CSS Variables(自定义属性) 构建,而不是传统的 SCSS 变量。CSS Variables 的一大优势是 运行时动态切换,无需重新编译。

主题变量定义示例

css 复制代码
/* 亮色主题 */
:root {
  --ti-button-primary-bg: #1890ff;
  --ti-button-primary-hover-bg: #40a9ff;
  --ti-button-primary-active-bg: #096dd9;
  --ti-button-border-radius: 4px;
}

/* 暗色主题 */
[data-theme="dark"] {
  --ti-button-primary-bg: #1f3a5f;
  --ti-button-primary-hover-bg: #2a4b7a;
  --ti-button-primary-active-bg: #0f2a4a;
}

组件样式使用变量

css 复制代码
.tiny-button--primary {
  background-color: var(--ti-button-primary-bg);
  border-radius: var(--ti-button-border-radius);
}
.tiny-button--primary:hover {
  background-color: var(--ti-button-primary-hover-bg);
}

动态切换主题

javascript 复制代码
// 切换到暗色主题
document.documentElement.setAttribute('data-theme', 'dark');

// 切回亮色主题
document.documentElement.removeAttribute('data-theme');

3.2 主题生成器与自定义主题包导出

TinyVue 提供了 在线主题生成器,开发者可以可视化调整颜色、圆角、间距等设计 token,实时预览效果,最后导出自定义主题 CSS 文件,在项目中引入即可。

自定义主题包导入

javascript 复制代码
import '@opentiny/vue/theme/index.css'       // 默认主题
import './my-custom-theme.css'                // 自定义主题覆盖

3.3 Shadow DOM 样式隔离机制------微前端场景下的天然优势

TinyVue 组件可以选择性地启用 Shadow DOM 封装。启用后,组件的样式会被隔离在 shadow root 内部,不会影响外部页面,也不会被外部样式污染。

这对于微前端场景尤为重要:主应用和子应用可能使用不同的 UI 库或主题,传统 CSS 容易发生样式冲突。而 TinyVue 的 Shadow DOM 方案从根本上解决了这个问题。

启用 Shadow DOM

vue 复制代码
<template>
  <tiny-button shadow-dom>隔离样式</tiny-button>
</template>

💡 资深提示:Shadow DOM 虽然解决了样式冲突,但也会带来一些限制(如全局弹窗需要挂载到 body)。TinyVue 内部已做了处理,Modal、Message 等浮层组件会自动挂载到外部,保证功能正常。

四、组件开发实战------从零封装一个自定义组件

下面我们以一个 评分组件(Rate) 为例,演示如何基于 TinyVue 的 Renderless 架构,开发一个支持 Vue 2 和 Vue 3 的跨框架组件。

4.1 理解组件 API 规范与设计模式

首先定义组件的 Props 和 Events:

typescript 复制代码
// types.ts
export interface RateProps {
  value: number;           // 当前评分值
  max?: number;            // 最大分数,默认5
  disabled?: boolean;      // 是否禁用
  allowHalf?: boolean;     // 是否允许半星
}

export interface RateEmits {
  (e: 'change', value: number): void;
  (e: 'update:value', value: number): void;
}

4.2 编写组件逻辑层(Renderless 核心)

typescript 复制代码
// renderless/rate.ts
import { ref, computed } from '../adapter'  // 统一适配器

export function useRate(props: RateProps, { emit }) {
  const currentValue = ref(props.value);
  const max = computed(() => props.max ?? 5);
  
  const stars = computed(() => {
    return Array.from({ length: max.value }, (_, i) => i + 1);
  });
  
  const getStarClass = (index: number) => {
    const starValue = index + 1;
    const isFull = currentValue.value >= starValue;
    const isHalf = props.allowHalf && 
                   currentValue.value > starValue - 1 && 
                   currentValue.value < starValue;
    return {
      'full': isFull,
      'half': isHalf,
      'active': isFull || isHalf
    };
  };
  
  const handleClick = (index: number) => {
    if (props.disabled) return;
    const newValue = index + 1;
    currentValue.value = newValue;
    emit('update:value', newValue);
    emit('change', newValue);
  };
  
  const handleMouseMove = (event: MouseEvent, index: number) => {
    if (!props.allowHalf || props.disabled) return;
    const rect = (event.target as HTMLElement).getBoundingClientRect();
    const half = event.clientX - rect.left < rect.width / 2;
    const starValue = index + (half ? 0.5 : 1);
    currentValue.value = starValue;
  };
  
  return {
    stars,
    currentValue,
    getStarClass,
    handleClick,
    handleMouseMove
  };
}

4.3 适配 Vue 2 和 Vue 3 的版本适配层

Vue 3 模板vue3/Rate.vue):

vue 复制代码
<template>
  <div class="tiny-rate" :class="{ 'is-disabled': disabled }">
    <span
      v-for="(star, index) in stars"
      :key="index"
      class="tiny-rate__star"
      :class="getStarClass(index)"
      @click="handleClick(index)"
      @mousemove="(e) => handleMouseMove(e, index)"
    >
      <i class="tiny-icon-star"></i>
    </span>
  </div>
</template>

<script setup lang="ts">
import { useRate } from '../renderless/rate'
const props = defineProps(['value', 'max', 'disabled', 'allowHalf'])
const emit = defineEmits(['change', 'update:value'])
const { stars, getStarClass, handleClick, handleMouseMove } = useRate(props, { emit })
</script>

<style scoped>
/* 样式略,使用 CSS Variables */
</style>

Vue 2 模板vue2/Rate.vue):

vue 复制代码
<template>
  <div class="tiny-rate" :class="{ 'is-disabled': disabled }">
    <span
      v-for="(star, index) in stars"
      :key="index"
      class="tiny-rate__star"
      :class="getStarClass(index)"
      @click="handleClick(index)"
      @mousemove="(e) => handleMouseMove(e, index)"
    >
      <i class="tiny-icon-star"></i>
    </span>
  </div>
</template>

<script>
import { useRate } from '../renderless/rate'
export default {
  props: ['value', 'max', 'disabled', 'allowHalf'],
  setup(props, { emit }) {
    return useRate(props, { emit })
  }
}
</script>

4.4 组件文档与示例编写

组件开发完成后,需要编写使用文档和示例,方便团队成员使用。TinyVue 的文档基于 Markdown + Vue 组件演示,示例代码可直接运行。

文档示例

markdown 复制代码
# Rate 评分组件

用于快速评级操作。

## 基础用法

```vue
<tiny-rate v-model="score" />

半星支持

vue 复制代码
<tiny-rate v-model="score" allow-half />

禁用状态

vue 复制代码
<tiny-rate v-model="score" disabled />

API

Props

参数 说明 类型 默认值
value 当前评分 number 0
max 最大分数 number 5
disabled 是否禁用 boolean false
allowHalf 是否允许半星 boolean false
复制代码
至此,一个完整的跨框架自定义组件就开发完成了。整个过程中,核心逻辑只编写了一次,Vue 2 和 Vue 3 的适配成本极低。


## 总结

本篇我们深入剖析了 TinyVue 组件库的核心能力:

1. **组件全景**:130+ 组件覆盖中后台全场景,特色组件(Split、IpAddress、CalendarView 等)解决特定业务痛点。
2. **Renderless 架构**:逻辑、模板、样式三层分离,一套代码同时支持 Vue 2 / Vue 3,甚至可扩展至 React。
3. **主题系统**:基于 CSS Variables 的运行时主题切换,支持 Shadow DOM 样式隔离,微前端场景天然友好。
4. **组件开发实战**:从 API 设计到逻辑层实现,再到 Vue 2/3 适配,完整演示了跨框架组件的开发流程。

掌握这些内容,你不仅能够熟练使用 TinyVue 开发企业级应用,还能根据业务需求扩展自定义组件,甚至为 OpenTiny 社区贡献代码。

**下篇预告:** 《数据篇------表格、表单与图表的高级应用》将聚焦企业级中后台最核心的数据处理场景,带你深度掌握 TinyGrid、TinyForm、TinyChart 组件的高阶用法与性能优化技巧,敬请期待!


**如果觉得本文对你有帮助,欢迎点赞、收藏、评论,你的支持是我持续创作的动力!**
相关推荐
sg_knight2 小时前
如何实现“秒传”与“断点续传”?MinIO + Java 实战进阶篇
java·开发语言·文件管理·minio·ftp·oss·文件传输
William Dawson2 小时前
Java 后端高频 20 题超详细解析 ②
java·开发语言
夜珀2 小时前
OpenTiny NEXT 从入门到精通·第 4 篇
开发语言
小樱花的樱花2 小时前
1 项目概述
开发语言·c++·qt·ui
我命由我123452 小时前
在 React 项目中,可以执行 npm start 命令,但是,无法执行 npm build 命令
前端·javascript·vue.js·react.js·前端框架·json·ecmascript
551只玄猫2 小时前
【数学建模 matlab 实验报告10】插值
开发语言·数学建模·matlab·课程设计·插值·实验报告
I疯子2 小时前
2026-04-08 打卡第 5 天
开发语言·windows·python
游乐码3 小时前
c#ArrayList
开发语言·c#
C+++Python3 小时前
Python MCP Server 最简实现
开发语言·python