志非

V1

2022/08/23阅读:33主题:凝夜紫

Vue2.7 + Vite3.2 + Rollup 组件库开发指南

前言

随着业务增长越来越快,为了提高团队的开发效率,减少造轮子,部门领导希望搭建一个前端组件库,也方便沉淀前端业务组件物料。之前阅读过 element-ui 部分源码,很早之前希望搭建一个组件库,很开心的接下这个任务

技术栈

调研分析目前项目的诉求,有以下几点:

  • 产品项目要兼容 IE
  • 支持多主题换肤
  • 配套文档 + demo
  • 拥抱新技术、开发快
  • ……

为了减少投入和维护成本,基于 element-ui 这个优秀的组件库进行开发是一个很不错的选择

项目技术栈

目录结构介绍

项目 目录结构使用 mddir 插件生成,目录结构介绍如下

|-- douluo-ui
    |-- .cz-config.js           # cz-customizable commit message 规范
    |-- .editorconfig           # EditorConfig 配置
    |-- .eslintignore           # eslint 忽略检测文件
    |-- .eslintrc.js            # eslint 配置文件
    |-- .gitignore              # git 忽略提交
    |-- .ls-lint.yml            # ls-lint 文件命名规范 kebab-case 校验
    |-- .stylelintignore        # stylelint 忽略检测
    |-- .versionrc.js           # standa……rd-version 自动生成 CHANGELOG 配置
    |-- CHANGELOG.md            # standard-version 根据 commit 信息自动生成
    |-- README.md               # readme 项目介绍
    |-- babel.config.js         # babel 配置文件
    |-- commitlint.config.js    # commitlint 校验 comit message 配置
    |-- compoments.json         # 项目组件路径
    |-- components.d.ts         # vite ts 文件类型声明
    |-- gulpfile.js             # gulp 打包 css
    |-- index.html              # vite 入口文件 /examples/main.js
    |-- jsconfig.json           # js 配置文件
    |-- package.json
    |-- prettier.config.js      # prettier 格式配置
    |-- stylelint.config.js     # stylelint 格式配置
    |-- vite.config.js          # vite 配置
    |-- .husky                  # husky 配置
    |   |-- commit-msg          # commit 提交钩子
    |   |-- common.sh
    |   |-- lint-staged.config.js   # lint-staged 配置
    |   |-- pre-commit          # commit 之前钩子函数
    |-- .vscode                 # 配置 vscode 编辑器
    |   |-- extensions.json
    |   |-- setting.json
    |-- build                   # rollup 打包配置
    |   |-- rollup-build.js
    |   |-- rollup-creat-Config.js
    |   |-- utils.js
    |-- config                  # 项目构建配置
    |   |-- alias.js
    |   |-- rollup-config.js
    |-- docs                    # 文档目录
    |   |-- README.md
    |   |-- .vuepress
    |   |   |-- config.js       # vuepress 配置文件
    |   |   |-- enhanceApp.js   # vuepress 安装组件库
    |   |   |-- components      # vuepress 公共组件
    |   |   |-- dist            # vuepress 打包静态资源
    |   |   |-- sidebarRoutes   # 抽离的侧边栏路由文件
    |   |   |   |-- business.js # 业务组件路由路径
    |   |   |   |-- developer.js # 开发指南路由路径
    |   |   |   |-- element.js   # element 组件路由路径
    |   |   |   |-- guide.js     # 开发规范路由路径
    |   |   |-- styles           # vuepress 公共样式
    |   |       |-- index.styl
    |   |       |-- element-ui   # element-ui 文档样式
    |   |       |-- theme        # vuepress-theme-hope 主题样式
    |   |-- developer            # 开发指南文档
    |   |-- guide                # 开发规范文档
    |   |-- packages
    |       |-- business         # 业务组件文档
    |       |-- element          # element 文档
    |-- examples                 # 组件库开发项目
    |   |-- main.js              # vite 启动入口文件
    |   |-- assets
    |   |-- src
    |-- lib                      # rollup 打包组件库静态资源
    |   |-- xxx.common.js        # rollup 打包 common 规范
    |   |-- xxx.common.min.js    
    |   |-- xxx.es.js            # es 打包规范
    |   |-- xxx.es.min.js        
    |   |-- theme                # css 和 字体图标打包
    |-- packages                 # 组件库包
    |   |-- element-ui          # 基础组件包
    |   |-- business            # 业务组件包
    |-- play                    # vite 快速演示项目
    |-- scripts                 
    |   |-- docs.sh             # 文档发布脚步
    |-- src                     # 组件库项目
    |   |-- index.js            # 组件库入口文件
    |   |-- images
    |   |-- ui                  # 定义引入组件库文件
    |   |-- utils
    |       |-- themePicker.js  # 修改 element-ui 主题方法
    |       |-- use-namespace.js # 组件生成 bem 规范和前缀
    |-- styles                   # 统一存放组件库样式
        |-- src
        |   |-- iconfont.scss    # 字体样式
        |   |-- index.scss       # scss 入口文件
        |   |-- var.scss         # css 变量
        |   |-- common
        |   |   |-- var.scss     # scss 变量定义
        |   |-- element-ui      # element 组件 scss 目录
        |   |   |-- button.scss
        |   |-- fonts           # 字体图标
        |   |-- mixins
        |       |-- _var.scss   # 定义生成 css 变量方法
        |       |-- config.scss   # bem 规范符号
        |       |-- function.scss # 定义公用函数
        |       |-- mixins.scss     # bem 复用方法
        |       |-- utils.scss      # at-root 等复用mixin
        |-- theme               # gulp 打包 scss 目录,复制到 lib 

yarn 命令

开发和维护组件库,需要了解一些 yarn 基本操作

初始化一个项目

yarn init

添加依赖包

yarn add [package]
yarn add [package]@[version]
yarn add [package]@[tag]

此种添加方式默认添加到 dependencies 中,dependencies 是项目运行时所需要的依赖。由于添加到该目录中的依赖会让使用组件库的项目安装这些依赖,所以一定要分清楚依赖是否需要用户安装

将依赖项添加到不同依赖项类别中

分别添加到 devDependenciespeerDependenciesoptionalDependencies 类别中:

yarn add [package] --dev
yarn add [package] --peer
yarn add [package] --optional
  • devDependencies是开发时依赖,在构建运行之后不需要的依赖,默认情况下组件开发过程中均添加到该类别

  • peerDependencies是同伴依赖,是一种特殊的依赖,在发布包的时候需要。有这种依赖意味着安装包的用户也需要和包同样的依赖

  • optionalDependencies是可选依赖,意味依赖是可选的。这种依赖即便安装失败,Yarn 也会认为整个依赖安装过程是成功的。这种类型适用于那些即便没有成功安装可选依赖,也有后备方案的情况(比如 Watchman)。

升级依赖包

yarn upgrade [package]
yarn upgrade [package]@[version]
yarn upgrade [package]@[tag]

移除依赖包

yarn remove [package]

安装项目的全部依赖

yarn

yarn install

贡献代码流程

下载启动项目

fork 项目到自己的本地仓库,克隆项目到本地

git clone git@github.com:【账号】/douluo-ui.git

添加源关联源仓库,暂且命名源为 upstream

git remote add upstream git@github.com:JefferyXZF/douluo-ui.git

安装依赖,启动项目

yarn
yarn dev

查看或编写文档,执行下面命令

yarn docs:dev

命令脚步介绍

  "scripts": {
    "bootstrap""yarn || npm install"// 安装依赖(首推 yarn,yarn.lock会锁定依赖版本库)
    "dev""vite"// vite 启动,默认 vite.config.js,index.html 入口文件是 examples/main.js
    "docs:dev""vuepress dev docs"// vuepress 启动项目
    "docs:build""vuepress build docs"// vuepress 打包文档
    "docs:deploy""bash ./scripts/docs.sh"// 打包推送 gh-pages 文档
    "gulp-build:css""gulp build"// gulp 打包 scss 和 字体图标
    "build:lib""cross-env NODE_ENV=production rimraf ./lib && 
    node ./build/ rollup-build && npm run gulp-build:css"
// rollup 打包组件库
    "commit""git-cz"// commit message 提交规范
    "standard-version""standard-version"// 生成 changelog 文件(带图标)
    "changelog""npx conventional-changelog -p angular -i CHANGELOG.md -s -r 0"// 也是生成 changelog 文件
  }

开发组件

组件按照功能划分为基于 element-ui 基础组件和业务组件

在开发前,首先要对开发的组件进行设计,要实现什么功能,考虑功能拓展性,事先定义 propsevent 事件slot 插槽,为了组件样式灵活,可能要对外暴露样式接口,例如自定义 classstyle。同时,为了兼容 elemnt-ui API,需要考虑用 $attrs$listeners 、组件内定义插槽,条件允许,可以先参考 element-uielement-plus 源码实现

开发基础组件,在 packages/element-ui 目录下创建,参考 button 组件,然后在 src/ui/element-ui.js 和 根目录下 compoments.json 添加文件路径

开发业务组件,参考 element-ui 目录,在 packages/business 创建文件组件,然后引入路径和上面一样

注意:如果组件需要使用到 图片或第三方插件 考虑到包的大小,需要先出实现方案,讨论合适后再编码实现

以 button 组件为例

入口文件 index.js

import Button from "./src/button.vue";
import { createNamespace } from "/src/utils/use-namespace";

Button.install = (Vue, options = {}) => {
  Vue.component(createNamespace("button", { prefix: options.prefix }), Button);
};

export default Button;

此处将 Button 注册成一个 Vue 组件,外部引入时可以通过 Vue.use() 来安装该插件,该插件的功能就是全局注册 Button 组件

button src 下的单文件组件

为了兼容 element-ui buton 组件的功能,使用 $attrs$listeners 继承它的属性和事件

useNamespace 方法是参考 element-plus 实现,创建一个 bem 对象,调用封装的方法,可以生成符合 bem 规范的 class,避免命名容易冲突

<template>
  <el-button
    v-bind="$attrs"
    v-on="$listeners"
    :class="[
      ns.b()
    ]"
>

    <slot></slot>
  </el-button>
</template>
<script>
import { useNamespace } from 'src/utils/use-namespace'

export default {
  name'Button',

  data () {
    this.ns = useNamespace('button')
    return {}
  }
}
</script>

组件样式主题

阅读 button 单文件组件,你可能会发现没有样式代码。为了考虑样式的统一性和打包优化,放在了 styles 目录下

组件库的样式管理由 sass 来完成,因此你需要对 sass 的一些语法要特别熟悉,如变量声明、 @mixins@function 、还有一些常用的内建函数mapscolorsting等知识,详细学习请阅读 Vue组件库必备 Sass(Dart) 知识

样式编写使用了 Bem 命名规范,Bem 是块(block)、元素(element)、修饰符(modifier)的简写,由 Yandex 团队提出的一种前端 CSS 命名方法论,可以让前端代码更容易阅读和理解,方便协作和维护

BEM 命名约定的模式是:

.block {}

.block__element {}

.block--modifier {}

每一个块(block)名应该有一个命名空间(前缀),例如 el-block

  • block 代表了更高级别的抽象或组件。
  • block__element 代表 .block 的后代,用于形成一个完整的 .block 的整体。
  • block--modifier 代表 .block 的不同状态或不同版本。

使用两个连字符和下划线而不是一个,是为了让你自己的块可以用单个连字符来界定。如:

.sub-block__element {}

.sub-block--modifier {}

下面介绍一些 dou-ui 中主要用到的 mixin 模块及 bem 用法

1、bbemblock 的简称, 用来匹配 block 声明类,比如

// mixin.scss
@mixin b($block) {
  $B: $namespace + '-' + $block !global;

  .#{$B} {
    @content;
  }
}

// 使用
@include b(button) {
}
// 将会编译为
.dl-button {
}

2、ebemelement 的简称,用来匹配 element 声明类,比如

@include b(button) {
  @include e(header) {
  }
}
// 将会编译为
.qw-button__header {
}

3、mbemmodifier 的检查, 用来匹配 modifier 声明类,比如

@include b(button) {
  @include m(primary) {
  }
}
// 将会编译为
.dl-button--primary {
}

when 是额外增加的状态修饰符,用来表示当前块或子元素或子元素特定类型下的状态,,比如

@include b(button) {
  @include when(plain) {
  }
}
// 将会编译为
.dl-button.is-plain {
}

& 代表父级,& 会编译为父级,这个类也会被置于根目录,而不是放置在父级下。

Git Commit 提交

本地提交代码,使用 yarn commit 替代 git commit

如果不是,每次提交 commit message 都必须按照以下格式提交,且不得超过 72 个字符(避免自动换行影响美观)

<type>(<scope>): <subject>

type 用来说明 commit 的类别,例如

  • feat:新功能(feature)
  • fix:修补bug
  • docs:文档(documentation)
  • style: 代码风格,格式修复
  • refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  • perf:代码优化,改善性能
  • test:增加测试
  • chore:构建过程或辅助工具的变动
  • revert:代码回退
  • ci:对CI配置文件和脚本的更改
  • build:变更项目构建或外部依赖

如果 typefeatfix,则该 commit 必须要在记录 changelog(更新日志)中

scope 用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,一般情况下可以不写,如需要可以注明

subject 是 commit 目的的简短描述,不得超过 50 个字符

  • 尽量使用英文书写,不会的单词先翻译
  • 以动词开头,使用第一人称现在时,比如change,而不是changed或changes
  • 第一个字母小写
  • 结尾不加句号(.)

分支检出

组件库的开发主分支为 develop 分支,组件库开发者需要贡献代码时,请按照 Git Commit 提交规范的 type 类型来命名新分支,并进行开发。

例如,需要进行 Button 组件的开发时,拉取 develop 分支最新代码,并生成名为 feat-button 的新分支进行开发。同理,进行 bug 修补时新建分支名应为 fix-button

在新建分支开发完成之后,需要进行代码审核,审核完成之后才可以合并到 develop 分支

发起 PR 合并

push 之前,避免合并冲突,先合并远程的 develop 分支在推送,然后再发起合并,审查完代码后会合并你的代码到 develop 分支

git fetch upstream

git merge upstream/develop

git push origin xxx-button

版本发布

develop 分支开发一定功能时,判断是否需要发布新版本的依据为:

  • 是否有新功能上线
  • 是否有重大bug修复
  • 是否有样式规范的变更

打包更新后,修改版本好发布,执行下面命令

npm publish

验证和测试组件库,增加版本日志,文档和事例

分类:

前端

标签:

Vue.js

作者介绍

志非
V1