教你搭建一个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你的代码
  • 规范化提交信息可以促进提交者提交有意义的、粒度合适的'提交' . 提交者要想好要怎么描述这个提交,这样被动促进了他们去把控提交的粒度

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

{
    "lint-staged": "^10.5.3",
    "yorkie": "^2.0.0"
}
复制代码

4.2 发布流程规范

4.2.1 编写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.2 验证是否发布成功

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

但是有些只提供下载,不提供发布作品。这里我们选择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.2
复制代码
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",
   }
}
复制代码

[五] 组件开发流程

一切准备工作就绪,那我们就来开始组件开发吧。

  • 前提当然要有一个组件开发测试环境
  • 1、base-ui 子项目
    • 新增组件具体实现
    • 新增样式
    • 新增测试用例
  • 2、website子项目
    • 新增组件使用文档

结语

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

拓展

猜你喜欢

转载自juejin.im/post/7018391913329852424