阅读 770

教你搭建一个Vue 的 UI框架

前言

有幸读到一文,醍醐灌顶:if 我是前端团队 Leader,怎么制定前端协作规范?。有所启发,想写一篇文章如何一步步走完一个项目完善的过程。

以前公司项目也有类似实践经历,本文以搭建一个 UI 框架为引子,从新梳理记录这一过程实践心得。

  • 至少要准备两个项目 一个项目用于管理UI框架,一个项目用于UI使用文档

知识准备

Monorepo

Monorepo 是管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。它的主要使用场景如下。

  • 我想看一个 pacakge 的代码、了解某段逻辑,不需要找它的 repo,直接就在当前 repo,直接在当前 repo 修改,统一测试、统一发版
  • 当某个需求要修改多个 pacakge 时,不需要分别到各自的 repo 进行修改、测试、发版或者 npm link

方式一:yarn workspace

这里参考的是 element3 原理:自动在当前node_modules 下生成子项目的引用

方式二: lerna管理package

目前社区中最主流的方案,也是yarn官方推荐的方案,是集成yarn workspace和lerna。使用yarn workspace来管理依赖,使用lerna来管理npm包的版本发布。

接下来项目目录结构处理按方式一处理

[一] 项目组织规范

├─base-ui
│  └─packages #
│  │  └─base-ui # UI组件框架
│  │  └─md-loader # 作为.md文件加载的库
│  │  └─website # UI组件使用文档网站
│  └─scripts # 
│  │  └─verifyCommit # 校验提交信息是否符合规范
│  └─package.json # 校验提交信息是否符合规范
│  └─README.md # 项目说明及贡献规范
复制代码

1.1 初始化根项目

# 初始化根项目

vue create base-ui # 选择vue3 和 yarn进行包管理 
cd base-ui
mkdir packages # 用于存放子项目
复制代码

之后配置private与workspaces,删除name

// base-ui package.json
{
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}
复制代码

1.2 初始化子项目一(website)

cd packages
mkdir website
npm init -y
复制代码

由于项目选用vue作为主框架,因此

  • 将src 与 public 目录移动到该项目下
  • 根据需求修改scripts
  • 执行脚本检验服务是否正常

1.3 初始化子项目二(base-ui)

cd packages
mkdir base-ui
cd base-ui
npm init -y
复制代码

1.4 初始化子项目二(md-loader)

cd packages
mkdir md-loader
cd md-loader
npm init -y
复制代码

接下来在根目下执行yarn命令就可以将所有子项目依赖安装上,开发过程中可以相互引用。

[二] 梳理子项目目录结构及依赖

这里参考的是 element3

2.1 子项目一(webs-site)

2.1.1 依赖管理

引入base-ui md-loader 依赖,以及vue 项目一些基础依赖

// package.json
{
    "dependencies":{
       "base-ui":"0.0.1",
       "md-loader": "1.0.0"
    }
}
复制代码

2.1.2 目录规范安排

// 目录结构大致跟vue项目类似,根据自己需求安排
├─webs-site
│  └─public #
│  └─src # 
│  │  └─docs # 各个UI组件使用.md文档说明,通过md-loder 引入(需要在vue.config.js处理)
│  │  └─其它
复制代码

2.2 子项目二(base-ui)

// 目录安排
├─base-ui
│  └─scripts #
│  └─packages # theme-chalk
│  │  └─Alert # UI 组件
│  │  │  └─__test__ # UI 组件测试用例
│  │  │  │  └─Alert.spec.js
│  │  └─theme-chalk # 组件样式文件 scss
│  └─src # 
│  └─tests # 工具类测试用例
、
复制代码

梳理需要实现功能功能

  • rollup 打包
  • gulp 构建处理scss样式

2.2.1 使用rollup实现组件打包

2.2.1.1 安装rollup依赖
{
    "@rollup/plugin-babel": "^5.2.2",
    "@rollup/plugin-commonjs": "^17.0.0",
    "@rollup/plugin-json": "^4.1.0",
    "@rollup/plugin-node-resolve": "^11.0.0",
    "@rollup/plugin-replace": "^2.3.4",
    "rollup": "^2.56.3",
    "rollup-plugin-peer-deps-external": "^2.2.4",
    "rollup-plugin-scss": "^2.6.1",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-typescript2": "^0.29.0",
    "rollup-plugin-vue": "^6.0.0",
}
复制代码

安装完成,配置rollup.config.js,这里省略可以参考element3配置

2.2.1.2 babel相关插件

如需支持babel转换,需要安装一下依赖,还需要配置.babelrc

{
    "@babel/core": "^7.12.10",
    "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
    "@babel/plugin-proposal-optional-chaining": "^7.12.7",
    "@babel/preset-env": "^7.12.10",
    "@babel/preset-typescript": "^7.12.7",
    "@vue/babel-plugin-jsx": "^1.0.0-rc.4",
}
复制代码
2.2.1.3 打包验证

配置scripts命令,执行npm run build:lib打包验证dist输出文件是否正常

  "scripts": {
    "build:lib": "rollup -c"
  }
复制代码

2.2.2 使用 gulp 编译scss

2.2.2.1 安装gulp相关依赖
{
    "cp-cli": "^2.0.0",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^7.0.1",
    "gulp-cssmin": "^0.2.0",
    "gulp-sass": "^4.1.0",
}
复制代码
2.2.2.2 配置gulpfile
  • 用gulpfile.js 编译转换css
  • 执行build:theme命令验证lib目录是否生成css 文件
  "scripts": {
    "build:theme": "gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk"
  }
复制代码

3.1 子项目 md-loader

这里不做展开直接使用 element3 源码

[三] 搭建测试框架

3.1 单元测试(jest)

这里选择jest作为单元测试框架

3.1.1 安装jest依赖

{
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/vue": "^5.8.2",
    "@vue/babel-plugin-jsx": "^1.0.0-rc.4",
    "babel-jest": "26.3.0",
    "typescript": "^4.1.2",
    "@types/jest": "^27.0.1",
    "@vue/test-utils": "^2.0.0-rc.13",
    "@vue/cli-plugin-unit-jest": "^4.5.13",
    "ts-jest": "26.4.4",
    "vue-jest": "^5.0.0-alpha.10",
    "jest": "26.0.0",
    "jest-environment-jsdom-sixteen": "^2.0.0"
}
复制代码

3.1.2 jest配置

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jest-environment-jsdom-sixteen',
  setupFilesAfterEnv: ['./scripts/setupJestEnv.js'],
  roots: ['<rootDir>/src', '<rootDir>/packages', '<rootDir>/tests'],
  transform: {
    '^.+\\.vue$': 'vue-jest',
    '^.+\\js$': 'babel-jest'
  },
  moduleFileExtensions: ['vue', 'js', 'json', 'jsx', 'ts', 'tsx', 'node'],
  testMatch: [
    '**/tests/**/?(*.)+(test).[jt]s?(x)',
    '**/tests/**/*spec.[jt]s?(x)',
    '**/__tests__/**/*.spec.js'
  ],
  moduleNameMapper: {
    '^element-ui(.*)$': '<rootDir>$1',
    '^main(.*)$': '<rootDir>/src$1',
    '^lodash-es$': 'lodash'
  },
  transformIgnorePatterns: ['<rootDir>/node_modules/(?!lodash-es)']
}
复制代码

3.1.3 验证jest测试用例

配置scripts命令,在__test__目录下写几个测试用例验证是否正常

  "scripts": {
    "test": "jest --runInBand"
  },
复制代码

3.2 UI自动化测试(cypress)

这里我们选择自动化测试框架是cypress 具体用法可以参考 github.com/cypress-io/…

{
    "cypress": "^8.3.1",
    "@cypress/vite-dev-server": "^2.0.8",
    "@vitejs/plugin-vue": "^1.2.2",
    "vite": "^2.2.3",
    "local-cypress": "^1.2.2"
}
复制代码

配置

{
    "scripts":{
        "cy": "cypress open-ct"
    }
}
复制代码

具体业务中如何编写测试用例可以参考 cypress-example-recipes

[四] 规范工作流

4.1 开发规范

4.1.1 编码规范

使用ESLint+Prettier来统一前端代码风格,相关配置如下:.prettierrc.eslintrc.js

安装相关依赖

{
    "@typescript-eslint/eslint-plugin": "^4.11.0",
    "@typescript-eslint/parser": "^4.11.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/eslint-config-prettier": "^6.0.0",
    "@vue/eslint-config-typescript": "^7.0.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^7.15.0",
    "eslint-plugin-json": "^2.1.2",
    "eslint-plugin-prettier": "^3.2.0",
    "eslint-plugin-vue": "^7.2.0",
    "prettier": "^2.2.1",
}
复制代码

4.1.2 提交信息规范

组织好的提交信息, 可以提高项目的整体质量. 至少具有下面这些优点:

  • 格式统一的提交信息有助于自动化生成CHANGELOG
  • 版本库不只是存放代码的仓库, 它记录项目的开发日志, 它应该要清晰表达这次提交的做了什么. 这些记录应该可以帮助后来者快速地学习和回顾代码, 也应该方便其他协作者review你的代码
  • 规范化提交信息可以促进提交者提交有意义的、粒度合适的'提交' . 提交者要想好要怎么描述这个提交,这样被动促进了他们去把控提交的粒度
4.1.2.1 通过git-cz 和 commitizen 规范提交信息

Commitizen插件简介:使用Commitizen提交时,系统将提示您在提交时填写所有必需的提交字段。不需要再等到稍后git提交钩子函数来检测提交内容从而拒绝您的提交请求。

# 借助提供的  git cz  命令替代我们之前的  git commit  命令, 帮助我们生成符合规范的 commit message
npm install -g git-cz commitizen
# 初始化项目以使用cz-conventional-changelog适配器
commitizen init cz-conventional-changelog --save-dev --save-exact
# 或手动安装 
npm install --save-dev cz-conventional-changelog
复制代码

image.png

4.1.2.2 通过yorkie 和 commitlint 检验提交信息
  • yorkie 功能与husky类似,通过触发commit-msgpre-commit两个钩子,检验提交信息是否规范和检验代码是否符合编码规范。

  • 使用commitlint工具检验commit message格式是否符合规范,防止有人越过第一步不使用git cz提交,而使用了git commit -m 'xxxx'同事message不规范

安装依赖:

npm i @commitlint/cli @commitlint/config-conventional --save-dev
复制代码

配置钩子:

"gitHooks": {
    "commit-msg": "commitlint -E GIT_PARAMS",
}

# 如果用的是husky,参数不一样
{
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
}
# 当然钩子含有eslint prettier 一些检查由于篇幅原因,这里就省略了。
复制代码
4.1.2.3 通过conventional-changelog-cli生成变更日志

conventional-changelog 是一款可以根据项目的commit 和 metadata信息自动生成 changelogs 和 release notes的系列工具。并且在辅助 standard-version 工具的情况下,可以自动帮你完成生成version、打tag, 生成CHANGELOG等系列过程。本文项目暂时未引入standard-version

npm install conventional-changelog-cli -g
复制代码

配置npm 脚本命令用于生成日志信息

# 此命令不会覆盖以前的 Change log,只会在`CHANGELOG.md`的头部加上自从上次发布以来的变动
{
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
}
复制代码

如果这是您第一次使用此工具,并且想要生成所有以前的变更日志,则可以执行:

conventional-changelog -p angular -i CHANGELOG.md -s -r 0
复制代码

最后再来查看package.json有哪些变动?

{
   scripts:{
    "cz": "npx git-cz",
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
   },
 "devDependencies": {
    "@commitlint/cli": "^9.1.2",
    "@commitlint/config-conventional": "^9.1.2",
    "cz-conventional-changelog": "^3.3.0",
    "lint-staged": "^10.5.3",
    "yorkie": "^2.0.0"
 },
 "gitHooks": {
    "commit-msg": "commitlint -E GIT_PARAMS",
    "pre-commit": "lint-staged"
 },
 "lint-staged": {
    "*.{js,vue}": [
      "eslint",
      "prettier --write"
    ],
    "*.ts?(x)": [
      "eslint"
    ]
  },
 "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
 }
}
复制代码

4.2 发布流程规范

4.2.1 推荐流程

1. Make changes
2. Commit those changes
3. Make sure Travis turns green

4. Bump version in package.json
5. conventionalChangelog
6. Commit package.json and CHANGELOG.md files
7. publish (这个环节也可以放到最后)

8. Tag
9. Push
复制代码

4.2.2 编写release脚本

这个脚本主要完成以下几个事情:

  • 运行组件内单元测试(npm run test)
  • 打包组件(npm run build)
  • 更新版本号
  • git add & git commit & git push
  • git publish
  • git tag & git push

配置脚本执行命令

{
    "release": "node scripts/release.js"
}
复制代码

4.2.3 验证是否发布成功

通常来说我们使用的镜像链接有以下这些:

但是有些只提供下载,不提供发布作品。这里我们选择npm这个渠道

npm login --scope=@OWNER --registry=https://registry.npmjs.org

# OWNER 为个人用户名
# username 请填入你的npm用户名(不一定是github用户名,是你要发布包管理服务的账号)
# password 请填入上面生成的
# email 请填入邮箱地址

# 当然你也可以发布到 github,具体细节不做展开
# npm login --scope=@OWNER --registry=https://npm.pkg.github.com

复制代码
4.2.2.1 发布前准备

发布之前得先注册一个npm 官网账号,也可以命令行注册

# 注意仓库地址是否官网 : --registry https://registry.npmjs.org
# (注意这里跟GitHub账号不是同一个,输入usename 、password 、email)
# 需要邮箱验证后才可以使用
npm addUser 或 npm login
复制代码
4.2.2.2 登录 npm 账号

如果直接执行npm run lease,会出现如下问题:

// 未登录
UnhandledPromiseRejectionWarning: Error: Command failed with exit code 1: yarn publish --new-version 0.0.2 --registry https://registry.npmjs.org --access public
error No token found and can't prompt for login when running with --non-interactive
复制代码
 Couldn't publish package: "https://registry.npmjs.org/base-ui: You do not
have permission to publish \"base-ui\". Are you logged in as the correct user?"
复制代码

注意:淘宝镜像只是提供下载,如果你要 npm login 、npm publish 登陆发布自己的作品, 你必须要切换到【官方货源】。先登录再发布:

  • npm login --registry https://registry.npmjs.org
4.2.2.3 发布包

执行release命令,并制定发布版本号

npm run release -- --v 0.0.3
复制代码
4.2.2.4 验证成果

等待脚本执行完

  • 1、查看 npm 官网 是否有你的包,由于目前npm规定必须带有私有域名的包,上面需要把包名统一改为 @geek-tim/base-ui
  • 2、查看GitHub上是否生成对应release 版本

4.3 生成 changelog

发布版本之后,记得更新changelog

{
  "scripts": {
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
   },
  "devDependencies": {
    "conventional-changelog-cli": "^2.1.1",
   }
}
复制代码

[五] 组件开发流程

一切准备工作就绪,那我们就来开始组件开发吧。为了简化流程,这里不涉及分支概念。

5.1 base-ui 子项目编写组件

- 新增组件具体实现,同时引入
- 新增样式
- 新增测试用例
复制代码

更新目录结构如下: image.png

5.2、website 子项目撰写组件文档

- 使用新组件前重新构建base-ui 子项目 :`yarn build`
- 新增组件使用文档
- 配置文档路由
复制代码

image.png

缺点: 每次更新组件需要在website子项目使用的话,需要手动重新构建base-ui子项目

5.3 提交代码

1. git cz 
2. 选择 feat(scope),填写commit-msg
3. git push
复制代码

5.4 发布代码

npm login
# 输入账号、密码、邮箱

npm run release -- --v 0.0.3 
# 一系列动作:单元测试,打包,自动打生成changelog,发布到npm仓库并打tag推送到github,
复制代码

5.5 检查发布结果

image.png

image.png

结语

本文主要参考 element3 源码大致实现一个简单UI框架搭建。涉及的知识点比较多,如Monorepo,yarn workspace,learn 。对一些框架或概念不熟悉的请自行查阅。

至此,小到每个工具(螺丝)的作用,大到组件从开发到使用文档撰写流程,再到整个组件库的如何更新发布流程,都尽可能详细的说明了。

文档梳理编写不易,看完帮忙点个赞^^ !!!

拓展

文章分类
前端
文章标签