【Nova UI】九、打造组件库第一个组件-图标组件(中):属性、功能与样式的完美融合

序言

在上一篇文章中,我们成功搭建起 SVG 图标组件包的基础架构🏗️,这如同为组件库这座大厦奠定了稳固的基石。如今,我们将目光聚焦于图标组件样式的雕琢工作,思考如何巧妙运用已掌握的 SASS 全局变量管理技巧,赋予图标组件丰富多样且统一协调的外观风格🌈。这不仅是提升组件库视觉一致性的关键🔑,更是增强用户体验的重要一环。让我们一同开启这场图标组件的样式优化之旅🚶‍♂️。

Icon组件

在组件库的持续建设中,Icon 组件的开发是极为关键的一步🌟。为实现对图标组件属性及类型的精准把控,我们在packages/components/icon/src/目录下,新增了icon.tsicon.js文件。其中,icon.ts肩负着定义组件属性与 TypeScript 类型的重任。

icon.ts 剖析

ts 复制代码
import { ExtractPropTypes, PropType } from 'vue'
import { svgs } from '@nova-ui/svgs'

export const iconProps = {
  name: {
    type: String as PropType<(typeof svgs)[number]>,
    required: true,
  },
  color: {
    type: String,
  },
  size: {
    type: [Number, String] as PropType<number | string>,
  },
} as const

export type IconType = ExtractPropTypes<typeof iconProps>

这段代码构建了 Icon 组件的基础属性体系📋。name属性指定图标名称,其取值限定在svgs数组内,确保只有预定义图标可被调用,避免非法名称的使用,就像给图标选择设置了一个 "白名单"🛂。color属性允许开发者通过字符串形式的颜色值对图标进行个性化上色🎨,轻松满足不同的视觉需求。size属性设计灵活,既支持数字形式(默认单位像素),也接受字符串形式的尺寸描述,满足不同场景下对图标大小的精确调整需求,无论是在小巧的移动端界面还是宽大的电脑屏幕上,都能完美适配📱🖥️。

PropType

是 Vue 中用于精确定义组件props类型的工具,通过它可指定复杂属性类型,增强 TypeScript 开发时的类型安全性。\

ts 复制代码
const MyComponent = defineComponent({
  props: {
    value: {
      type: [String, Number] as PropType<string | number>,
      required: false
    }
  },
  setup(props) {
    return () => <div>{props.value}</div>;
  }
});

在上述代码中,value属性可接受stringnumber类型的值,就像给组件的这个 "输入口" 明确了可接收的 "货物种类"🛒。
ExtractPropTypes

用于从props定义对象中提取属性类型信息,同样增强组件的类型安全性。\

ts 复制代码
const props = {
  message: {
    type: String,
    required: true
  }
};
const MessageComponent = defineComponent({
  props,
  setup(props) {
    return () => {
      return <div>{props.message}</div>;
    };
  }
});
type Props = ExtractPropTypes<typeof props/>;
ts 复制代码
const App = defineComponent({
  setup() {
    const myMessage = 'Hello, Vue 3!';
    return () => {
      return <MessageComponent message={myMessage} />;
    };
  }
});

在此例中,TypeScript依据ExtractPropTypes提取的Props类型,检查传递给MessageComponentmessage属性是否符合要求,确保数据传递的准确性和稳定性📡。

icon.vue 解析

icon.vue文件在 Icon 组件整体构建中,承担着实现具体功能的核心任务🧠。

html 复制代码
<template>
  <i
    :class="ns.b()"
    :style="style"
  >
    <component :is="svg" />
  </i>
</template>

template 里,<i> 标签作为图标容器,通过 :class 绑定 ns.b() 生成的特定类名,利用 useNamespace 避免样式冲突。:style 绑定 style 计算属性,用于动态调整图标样式。而 <component :is="svg" /> 依据 svg 计算属性,能动态渲染出对应 SVG 图标组件,增强了复用性与灵活性🎭。

ts 复制代码
<script lang="ts" setup>
  import { computed, CSSProperties } from 'vue'
  import { useNamespace } from '@nova-ui/hooks'
  import { isUndefined, addUnit } from '@nova-ui/utils'
  import * as Svgs from '@nova-ui/svgs'
  import { iconProps } from './icon'

  const ns = useNamespace('icon')
  defineOptions({
    name: 'NIcon',
  })
  const { name, color, size } = defineProps(iconProps)

  const svg = computed(() => {
    return Svgs[name]
  })

  const style = computed<CSSProperties>(() => {
    if (!color && !size) return {}

    return {
      fontSize: isUndefined(size) ? undefined : addUnit(size),
      color: color,
    }
  })
</script>

script部分,通过导入各种必要的模块与工具🛠️,为组件的功能实现提供了有力支持。computed用于创建计算属性,CSSProperties定义样式类型,useNamespace生成命名空间类名,isUndefinedaddUnit分别判断值是否为undefined及添加单位。同时,还导入了 SVG 图标组件与组件属性。

ns 通过 useNamespace 生成 Icon 组件的命名空间实例📍。defineOptions 定义组件名称为 NIcon。通过解构 defineProps(iconProps) 获取父组件传来的 namecolorsize 属性🍱。

svg 计算属性依据 nameSvgs 获取对应图标组件,style 计算属性根据 colorsize 生成样式对象。若无这两个属性传入则返回空对象,否则为 size 添加单位并设置 fontSizecolor 样式✨。

icon.vue 以简洁代码实现高效功能,为 Icon 组件在库中的应用与扩展筑牢根基。部分工具函数实现可在下方提及到的仓库中查看 。

样式

packages/theme-chalk/src目录下,新增icon.scss,用于编写 Icon 组件的样式。再在当前目录下新增index.scss统一导入所有组件样式。

scss 复制代码
@use 'mixins' as *;

@include b(icon) {
  display: inline-flex;
  width: 1em;
  height: 1em;
}

这段样式代码为 Icon 组件设定了基础的展示样式,使其以inline-flex的布局方式呈现,宽度和高度均为1em,确保图标在页面上能够整齐、美观地展示🛍️。

到此,图标组件已经实现完成了🎉。通过从属性定义到功能实现,再到样式设计的一系列精心打造,我们成功地为组件库增添了一个实用且美观的 Icon 组件,为后续的开发工作奠定了更加坚实的基础。

🦀🦀感谢看官看到这里,如果觉得文章不错的话🙌,点个关注不迷路⭐。

诚邀您加入我的微信技术交流群🎉,群里都是志同道合的开发者👨‍💻,大家能一起交流分享摸鱼🐟。期待与您在群里相见🚀,咱们携手在开发路上共同进步✨ ! 👉点我
感谢各位大侠一路相伴,实在感激! 不瞒您说,在下还有几个开源项目 📦,它们就像精心培育的幼苗 🌱,急需您的浇灌。要是您瞧着还不错,麻烦动动手指,给它们点亮几颗 Star ⭐,您的支持就是它们成长的最大动力,在此谢过各位大侠啦!

相关推荐
今日无bug7 小时前
大模型的回答是怎么一个字一个字蹦出来的?前端流式输出解析
前端·llm·agent
计算机魔术师7 小时前
Anthropic 复盘 Claude 模型越权访问真实系统事件并改进对齐与安全措施
前端
掘金者阿豪7 小时前
Seedance 2.0/2.5 虚拟素材能跨 Key 共用吗?一次讲清 Asset ID、账号隔离与 SaaS 素材架构
前端·后端
Jacky19997 小时前
Electron + Vue 3 桌面打字游戏实战:从 VSCode 扩展到独立应用的架构改造
javascript
用户50093768390398 小时前
热敏小票 Web 打印:我在 58/80mm 上折腾的两周
前端·vue.js
XGM8 小时前
一个前端新手的"顿悟"时刻:DOM树、渲染原理和JS动态渲染
前端
东方小月8 小时前
一篇文章带你深入拆解Skill的本质与工程实现,让你不再滥用Skill
前端·人工智能·后端
xy34538 小时前
Axure9.0 中继器遮罩的核心应用场景(精准适配列表交互)
前端·ui·html·原型·产品设计·axure9.0
天道kabuto9 小时前
记一次 UnoCSS 样式离奇失效的踩坑复盘
前端
前端小张同学9 小时前
AI全栈开发最佳实践💐
前端·后端·架构