【有价值】组件库搭建-vue3+TS+monorepo

一、BEM:css类名命名规范

通过将页面拆分为独立的块(Block)、元素(Element)和修饰符(Modifier),来提高代码的可读性、可维护性和重用性

块(Block):块是页面上的独立功能单元,可以在页面上独立存在,也可以包含其他元素。块的类名使用连字符(-)连接,例如.block。

元素(Element):元素是块的一部分,只能在块的上下文中使用。元素的类名由块的类名作为前缀,使用双下划线(__)连接,例如.block__element。

修饰符(Modifier):修饰符用于修改块或元素的外观、状态或行为。修饰符的类名由块或元素的类名作为前缀,使用双连字符(--)连接,例如.block--modifier或.block__element--modifier。

BEM规范的优势
1、可读性强:通过使用清晰的命名约定,代码的含义更加明确和直观,任何人都能轻松理解样式规则所针对的目标元素。
2、可维护性好:BEM提供了一种结构化的方式来组织CSS代码,使其易于扩展和维护。每个块、元素和修饰符都有自己的作用范围和含义,避免了全局样式的混乱。
3、重用性高:块和元素可以在不同的场景中重复使用,促进了代码的可重用性和模块化。
4、避免选择器冲突:通过将块和元素的类名限制在其父级作用域内,BEM规范有效地避免了选择器冲突的问题。

// create.ts,定义BEM公共方法
function _bem(prefixName: string, blockSuffix: string, element: string, modifier: string) {
    if (blockSuffix) {
      prefixName += `-${blockSuffix}`
    }
    if (element) {
      prefixName += `__${element}`
    }
    if (modifier) {
      prefixName += `--${modifier}`
    }
    return prefixName
  }
  function createBEM(prefixName: string) {
    // 创建模块 el-switch
    const b = (blockSuffix: string = '') => _bem(prefixName, blockSuffix, '', '')
    // 创建元素 el-switch__input
    const e = (element: string = '') => element ? _bem(prefixName, '', element, '') : ''
    // 创建块修饰 el-switch--large
    const m = (modifier: string = '') => modifier ? _bem(prefixName, '', '', modifier) : ''
    // 创建元素块 el-switch-on-color
    const be = (blockSuffix: string = '', element: string = '') =>
      blockSuffix && element ? _bem(prefixName, blockSuffix, element, '') : ''
    // 创建块修饰 el-switch-on-color--large
    const bm = (blockSuffix: string = '', modifier: string = '') =>
      blockSuffix && modifier ? _bem(prefixName, blockSuffix, '', modifier) : ''
    // 创建元素修饰 el-switch__input--large
    const em = (element: string = '', modifier: string = '') =>
      element && modifier ? _bem(prefixName, '', element, modifier) : ''
    // 创建块元素修饰 el-switch-on-color__input--large
    const bem = (blockSuffix: string = '', element: string = '', modifier: string = '') =>
      blockSuffix && element && modifier ? _bem(prefixName, blockSuffix, element, modifier) : ''
    // 创建状态 is-success
    const is = (name: string, state?: string | boolean) => {
      return name && (state ?? true) ? `is-${name}` : ''
    }
  
    return {
      b,
      e,
      m,
      be,
      bm,
      em,
      bem,
      is
    }
  }
  // 用于创建BEM命名空间
  export function createNamespace(name: string) {
    // 默认命名前缀
    const prefixName = `hia-${name}`
    return createBEM(prefixName)
  }

使用样例

// 使用样例
<template>
  <div :class="bem.b()">
    <el-table
      :data="tableData"
      style="width: 100%"
      :class="bem.be('root')"
      :border="border"
      :stripe="stripe"
      :highlight-current-row="true"
      @selection-change="handleSelectionChange"
    >
      .........
    </el-table>
    <el-pagination
      :class="bem.e('pagination')"
      v-if="pagination"
      class="mt-10"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="currentPage"
      :page-sizes="pageSizes"
      :page-size="myPageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="total"
      background
    >
    </el-pagination>
  </div>
</template>

<script setup lang="ts">
import { createNamespace } from './utils/create.ts'
const bem = createNamespace('table')
</script>

// 公共样式

// 公共样式mixins.scss
@use "config" as *; // @import可能有多次引用的问题
@forward "config"; 

// .hia-button
@mixin b($block) {
  $B: $namespace + "-" + $block;
  .#{$B} {
    // @include后 样式会替换@content
    @content;
  }
}
// hia-button.is-xxx
@mixin when($state) {
  // 输出到全局作用域
  @at-root {
    &.#{$state-prefix + $state} {
      @content;
    }
  }
}
// hia-button__header
@mixin e($element) {
  @at-root {
    #{& + $element-separator + $element} {
      @content;
    }
  }
}
// hia-button--primary
@mixin m($modifier) {
  @at-root {
    #{& + $modifier-separator + $modifier} {
      @content;
    }
  }
}

样式文件

@use "./mixins/mixins.scss" as *;
@use "common/var.scss" as *;

@include b(table) {
  @include e(pagination) {
    margin-top: 16px;
    display: flex;
    justify-content: end;
  }
}

效果如下:


image.png

二、monorepo:单体仓库,项目管理策略

Monorepo是一种项目管理方式,单个仓库中管理多个项目,所有模块、服务、工具和配置文件都在同一个版本控制系统中进行管理。这种方式有助于简化代码共享、版本控制、构建和部署的复杂性,并提供更好的可重用性和协作性。

monorepo演进过程
1、单体巨石应用:代码多,业务复杂,构建效率低
2、多仓库多模块应用:每个模块独立开发、编译,从业务上完成解耦,项目工程管理难度增加:代码共享效率低、底层修改后上层要统一修改、多个工程构建慢
3、单仓库多模块应用

monorepo应用场景
中大型、多模块项目,更适合用 MonoRepo 方式管理代码,在开发、协作效率、代码一致性方面都能受益

monorepo的优势
1、代码共享:在同一个仓库中,项目之间的代码共享更加便捷,减少了重复代码,提高了代码复用率。不需要发布即可使用,编辑完即可在其他项目中测试
2、版本一致性:所有模块使用相同的版本控制,避免了多个仓库的依赖冲突和版本不一致问题
3、提升协作效率:团队成员可以在同一个仓库中协作,代码审查、问题跟踪和变更管理更加方便
4、统一构建和发布流程:构建、测试、部署流程在同一个仓库内可以统一管理,提高了开发效率

学习Element Plus组件库(一):采用 monorepo 的方式管理代 - 掘金

vue3组件库搭建 - 知乎

三、vite:vue3默认支持的脚手架

1、生成各种js框架的工程的开发模板,相当于vue-cli
2、Vite 天然支持引入 .ts 文件。
3、npm 依赖解析和预构建
4、自动依赖搜寻
5、Monorepo 和链接依赖。在一个 monorepo 启动中,该仓库中的某个包可能会成为另一个包的依赖。Vite 会自动侦测没有从 node_modules 解析的依赖项,并将链接的依赖视为源码。它不会尝试打包被链接的依赖,而是会分析被链接依赖的依赖列表

Worker 选项 | Vite 官方中文文档

四、changeset:自增版本号,并添加变更日志

管理版本控制中变更集的工具,主要用于帮助开发者跟踪和管理代码库中的变更,确保每次提交都是有意义且易于回溯的。Changeset提供了一种简洁的方式来描述代码的变更,使得团队协作更加高效

// 安装
npm install @changesets/cli -D
// 初始化,生成配置文件,可以指定git分支和版本号更新界别
npx changeset init
// 自动修改pacakge.josn中的
npm run changeset
// 发布
npm run changeset publish

changeset与monorepo结合,能一把更改全部子工程的版本号

五、Commitlint:代码提交规范

六、哈士奇

用于在Git提交钩子中运行脚本,帮助开发团队在代码提交前执行一些预定义的操作,以确保代码的质量和一致性

Husky的基本功能

  1. 代码规范检查:在提交代码前运行ESLint或其他代码风格检查工具,确保代码遵循一致的编码风格。
  2. 单元测试:在提交前运行单元测试,确保代码没有破坏现有功能。
  3. 提交消息验证:检查提交消息是否符合规范,确保提交信息的清晰和有用

Husky的优势和适用场景

  1. 自动化任务:通过Husky,可以自动化执行一系列任务,如代码格式化、类型检查、单元测试等,确保每次提交或推送前代码都符合项目标准。
  2. 减少错误:自动化的检查可以减少代码中的常见错误,提高代码质量。
  3. 提高协作效率:团队成员在提交代码之前可以确保代码符合团队的编码规范,避免在代码合并时出现冲突或问题。
    安装
// 安装
npm install husky --save-dev

// package.json中增加脚本
"scripts": {
  "prepare": "husky install"
}

// 运行
npm run prepare

七、Design Tokens:设计变量,用有意义的变量名称代替颜色值,确保系统颜色准确性和一致性

共同开发一套组件库,约定主题色的色值是#409eff。按钮选中、多选框选中、日期选中时,要求不同组件选中时的颜色值一致,这三个组件分给不同的团队或个人实现时,靠相互之间来回的沟通,在复杂场景下是比较费事的,而且后期核对色值也比较麻烦,开发人员也容易出现拼写错误的问题,不利于大家一起写作。


image.png

如果再复杂一些,要求每个组件在鼠标悬浮上去时,颜色要比这个颜色浅一个色号;再复杂一些,按钮除了主题色之外,还有成功状态、错误状态,每个状态在鼠标滑动上去后都要比原来的色值浅一个颜色。这个内容和要求,在设计团队、多个开发团队之间沟通时,成本较大,且无法快速达成一致。

image.png

这时,如果用一个有意义的字符串变量代替这个色值,相当于给色值起一个通俗易懂的名字,所有人一看这个名字就知道该如何使用,而不需要关注具体色号值,这样所有人沟通和实现起来,就比较容易达成一致,且比较高效,后期维护也容易,这就是Design Tokens的意义。

Design Tokens 是一种设计和技术理念,用于存储和管理界面设计中使用的视觉设计属性,如颜色、字体、间距、尺寸等。这些属性以数据结构的形式存在,可以被设计系统自动引用和更新,确保用户界面(UI)在不同平台和设备上具有一致性和可扩展性。

Design Tokens相当于将设计组件进一步拆解,使其原子化,将组件的每一种属性都转变为一个前端变量,统一样式和前端语言,从而快速管理设计系统

Design Tokens的价值

  1. 设计语义-更易理解
    每一个组件的基础元素都可以用 Token 进行语义化的命名,帮助设计师和开发建立统一的描述语言。例如#91d5ff 这个色值按照传统的设计规范命名的方式,它可能叫 Blue-3。在实际应用的时候,设计师和开发并不能直接通过 Blue-3 来理解这个颜色到底是用在什么具体场景当中。而当我们通过 Token 语义的方式让它达到组件级别的精度时,它会叫:color-primary-brand-light-disable,不同的设计师和开发就能迅速的理解这个颜色应用在什么具体场景当中。


    image.png
  2. 设计方案-更加一致
    当使用组件库实际运用到项目当中时,我们有时候会有不同设计师合作产出一个项目的情况。对于一些不熟悉设计规范或新加入的设计师来说,就会困惑,当使用二级文本色的时候,究竟是用 Gray2、还是 Gray3、Gray4。而这个时候,我们定义一个 Token 名称叫:color-text-secondary,这个 Token 嵌套的颜色是 :Gray3,这样我们设计师在使用的时候,只需要选择 color-text-secondary 这个 Token 变量即可,能选择的颜色就是唯一的,这就能在一定程度上确保不同设计师在同一个场景当中的设计稿保持高度一致性。


    image.png
  3. 主题皮肤-一键替换
    主题皮肤的替换可以用在两个维度,一是浅色模式和暗色模式的替换,二是不同品牌色之间的替换。我们可以将不同主题的同一个场景下的颜色使用同一个 Token 命名,例如变量名都叫:color-primary-brand-light-default,蓝色皮肤下对应的值为:#165DFF;红色皮肤对应的值为:#F53F3F。然后通过插件面板的一键操作即可完美切换。同时这种切换模式也可以带入 tokn.josn 代码(后面会具体讲如何输出 json 文件交付开发)中,与开发进行同步。

  4. 设计变更 高效维护
    还是上面的例子,当我们的二级文字颜色 color-text-secondary 需要进行变更,从 Gray-600 变为 Gray-500。如果没有“color-text-secondary”这个 Token,我们可能需要手动去选中所有用了 Gray-600 的二级文字的图层,一个一个地将它们改为 Gray-500,而当我们有了“color-text-secondary”这么一个 Token 时,我们只需要将 color-text-secondary 的值一键从 Gray-600 变为 Gray-500 便可以完成产品全局的颜色变更。进而设计师可以将 token.json 代码(后面会具体讲如何输出 json 文件交付开发)同步更新给开发 ,开发直接一键替换,线上的界面就能半自动地迅速应用到变更后的色值。

  5. 设计成果-精准还原
    设计稿能否被开发精准还原,这几乎是每一个设计师在实际项目中会遇到的问题。我们在进行设计验收的时候,即便花了很多时间进行走查,在表格上列举了很多细节问题,但最终的还原效果并不能得到保障。甚至在一些时候会感觉做验收比重新做一遍设计稿还要费劲「emo」,有的时候甚至会直接按 F12 在网页上改代码给开发提示

Design Tokens参考地址:
详解|Design Tokens 在设计系统中的意义与应用|插件|design|组件库|设计稿|tokens_网易订阅

大厂都在用! 万字干货带你读懂并应用 Design Token - 脉脉

专业B端设计师要懂的DesignToken概念解析 - 哔哩哔哩

色彩 - Ant Design

在线工具

Semi Design 设计系统管理

定义可参考:
Theme / Colors - Docs ⋅ Storybook

成熟的项目:
https://semi.design/zh-CN/basic/tokens
https://github.com/Tencent/tdesign-common/blob/develop/style/web/theme/_light.less

八、Vitest(发音为 "veetest") 是由 Vite 驱动的下一代测试框架

Vitest依赖jsdom,jsdom 的最新版本需要 Node.js v18 或更新版本

测试工程地址:

https://gitee.com/renshaw-shi/hia-ui.git

问题记录

1、导入vue文件,会出现不识别的情况

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容