Skip to content

如何设计一个 UI 组件库

  1. 工程化目录

整个工程的代码是否使用 Monorepo 的组织方式

  1. 组件测试
  • Vue-testing

  • jest

  1. 打包

  2. 文档站点

  3. 版本问题

  4. 哪些业务组件

  • 下拉树选择

  • 虚拟滚动树

    主要是结合 vue-virtual-scroller 去包裹 ElTreeVirtualNode

  • ProLayout

  • ProTable

  • ProForm

  • UfinChat

主要是一个实时的客服语音通话,在客户通话的时候提供语音转写功能,例外通过插件的形式对不同的语音赋能不同的功能

主要有: 辅助通话,AI 质检 ,语音回放,

后期也不短的进行优化, 如加入了前端分页 和

辅助通话:主要是对于用户通话中关键词匹配到知识库的会对关键词进行 标绿 ,那么客服鼠标浮动到上面的时候就通过弹框提升对应的问题链接列表,然后点击对应的问题显示标准答案

对于这个功能我们也优化了好几个版本,

  1. 一开始的时候就是提示的时候点击,然后展示在右边的知识库模块中
  2. 然后以为提供客户端的 mimi 模式,所以这个就提供多种方式展示,使用弹框展示
  3. 有些时候触发多个判断,是通过 Tab 去切换的

AI 质检插件

主要是对客服的,当客服话术触发关键词违规的时候,就会标红,同时在右边违规记录中加入违规信息

插件

这个是不断改进的

  1. 根据后端传入的数据中是否存在 aiQuality , knowlage 去渲染对应的组件 , 后面因为银行不需要知识库,因为内部不允许对接,然后就改造了一下 通过插件的方式

  2. 语音通话组件初始化的时候会遍历插件并初始化插件,插件去监听对应的生命周期 init , close , message

message : {
  type : "" , 客户/客服/组长,
  id : 消息的ID,
  content : "消息的内容",
  extra : {},   // 后端传入的消息内容
  linsters : [
    {
      start :
      end :
      fontStyle:
      trigger : "click" | hover,
      type : 'poppver | link'  弹框 | 点击
      callback:
    }
  ]
}
  1. 话术导航组件

这个跟业务结合的比较大,优化的比较多

第一版本: 获取后端的配置的话术导航数据 然后渲染并添加触发客服已经录入话术的录音播放 第二版本: 需要跟实时通话结合

脚手架搭建流程

  1. 代码规范支持
  • eslint
  • pritter
  • stylelint
  • husky
  1. CI/CD 工程相关的配置
  • gitignore : git 提交
  • docker 构建: 提供 dockerfile 文件
  1. 核心包的选择
  • UI 框架 elment-ui ele-plus...
  • CSS 相关 less、sass
  • 网络请求 ajax fetch axios
  • mock 支持
  • test 的支持 jest vue-test
  1. 工程目录划分
  • build: 构建配置

  • public: 公共资源

  • mock :

  • test

  • apis: http 请求相关

  • assets : 存放静态资源,这个文件夹下的文件会走 webpack 压缩流程

  • components :公共的业务组件

  • config :全局静态配置,不可更改项

  • layout :页面骨架布局

  • locales : 国际化

  • plugins :存放第三方插件

  • router 路由

  • store vuex

  • styles 全局样式,一句 ui 库主题样式

  • utils 常用函数以及其他有用工具

  • pages 页面级组件

  • types : 公共的 ts

    • shims-xx.d.ts 各文件类型的说明

然后进行不断的丰富:

  • 路由方面

    • 导航守卫的配置,如鉴权、页面加载进度条、页面 title 等
    • 怎么去配置路由: 使用自定义路由,约定式路由
    • 基础路由的维护
  • pinia 和 vuex 如何规范(提供基础的 module)

    • user
    • account 登录信息
    • menu 菜单数据
    • app : 公共数据
  • 全局样式划分

    • 基础样式 normalize.less
    • 组件样式
    • themes 主题相关配资
  • 请求工具的优化

    • 请求的转换 默认: application/json 格式
    • 统一鉴权的处理
    • 异常状态码的处理
    • 缓存的处理

如何保证代码质量的

主要从以下几个方面

  1. 规范化
  • 建立代码风格指南文档
  • 规范项目工程目录结构
  1. 自动化

配置对应的自动化检查工具

  • prettier
  • eslint
  • stylelint
  • husky
  • typescript 约束
  • CI/CD 构建的时候需要进行代码扫描(SonarQube)和跑测试用例

并在提交和构建的时候进行对应的 lint 检查

  1. 流程管理规范
  • 需求分支合并到主分支的时候需要进行 codereview
  • 在迭代周期后对发现的问题进行回顾 分享

package.json 中 main/module/browser/exports 字段有何区别

  1. main

main 指 npm package 的入口文件,当我们对某个 package 进行导入时,实际上导入的是 main 字段所指向的文件。

main 是 CommonJS 时代的产物,也是最古老且最常用的入口文件。

js
// package.json 内容
{
  name: 'midash',
  main: './dist/index.js'
}
// 关于如何引用 package
const midash = require('midash')

// 实际上是通过 main 字段来找到入口文件,等同于该引用
const midash = require('midash/dist/index.js')
  1. module

随着 ESM 且打包工具的发展,许多 package 会打包 N 份模块化格式进行分发,如 antd 既支持 ES,也支持 umd,将会打包两份。 所以我们使用 Import 导入的时候优先查找 module 的配置,如果没有才会使用 main

{
  name: 'midash',
  main: './dist/index.js',
  module: './dist/index.mjs'
}

// 以下两者等同
import midash from 'midash'
import midash from 'midash/dist/index.mjs'
  1. browser

browser 字段提供对浏览器环境更友好的模块入口。

  1. exports

exports 字段允许通过访问路径、运行环境(node/browser 等)、模块类型(require/import/types/css 等)组合确定最终的入口文件。

exports 是对外提供多个入口,还有另一个字段 imports 是对内修改依赖(有点像 browser)。

js
// package.json
{
  name: 'midash',
  main: './index.js',
  exports: {
    '.': './dist/index.js',
    'get': './dist/get.js'
  }
}

// 正常工作
import get from 'midash/get'

// 无法正常工作,无法引入
import get from 'midash/dist/get'

其同样还支持根据环境变量(NODE_ENV)、运行环境(nodejs/browser/electron) 导入不同的入口文件。

json
{
  "type": "module",
  "exports": {
    "electron": {
      "node": {
        "development": {
          "module": "./index-electron-node-with-devtools.js",
          "import": "./wrapper-electron-node-with-devtools.js",
          "require": "./index-electron-node-with-devtools.cjs"
        },
        "production": {
          "module": "./index-electron-node-optimized.js",
          "import": "./wrapper-electron-node-optimized.js",
          "require": "./index-electron-node-optimized.cjs"
        },
        "default": "./wrapper-electron-node-process-env.cjs"
      },
      "development": "./index-electron-with-devtools.js",
      "production": "./index-electron-optimized.js",
      "default": "./index-electron-optimized.js"
    },
    "node": {
      "development": {
        "module": "./index-node-with-devtools.js",
        "import": "./wrapper-node-with-devtools.js",
        "require": "./index-node-with-devtools.cjs"
      },
      "production": {
        "module": "./index-node-optimized.js",
        "import": "./wrapper-node-optimized.js",
        "require": "./index-node-optimized.cjs"
      },
      "default": "./wrapper-node-process-env.cjs"
    },
    "development": "./index-with-devtools.js",
    "production": "./index-optimized.js",
    "default": "./index-optimized.js"
  }
}

优先级

Node 环境

exports -> main

Node 不支持其他字段,所以非常简单。打包工具则是基于 Node 的标准进行扩展的。

Webpack

browser(对象) -> exports -> browser(字符串) -> module -> main

若构建目标不是 Web,则跳过 browser 字段。