开发工具选型

  • 开发前端使用的包,主要的逻辑代码只包含js,所以选用rollup做打包工具。
  • webpack适合开发项目,项目中会包含图片/视频/css、vue等多种资源,更适合使用webpack。

开发流程

  • 初始化项目,创建合理的目录结构
  • 设置基于编辑器的代码统一规范 .editorconfig vscode的配置
  • 配置 eslint 和 pretter 统一代码风格
  • 配置 babel,处理新语法兼容型
  • 配置 git 提交的校验钩子
  • 规范化提交代码到 git 仓库
  • 设置开发和打包脚本
  • 添加单元测试jest,编写测试示例
  • 完善 package.json 必要字段
  • 配置合适的 npm script
  • 本地测试开发的 npm 包,使用yalc
  • 发布包到 npm

初始化项目

npm init -y

  1. {
  2. "name": "sumfunctionmethod",
  3. "version": "1.0.0",
  4. "description": "发布npm包,整套流程开发demo",
  5. "main": "src/index.js",
  6. "scripts": {
  7. "test": "npm run dev"
  8. },
  9. "author": "shenshuai89",
  10. "license": "ISC"
  11. }

创建目录结构

  1. ├── README.md // npm包说明,安装以及使用方法
  2. ├── examples // 使用测试的案例
  3. ├── package.json // 项目结构说明
  4. ├── scripts // 用于存在开发或打包的脚本文件
  5. └── src // 存放项目文件
  6. └── index.js

配置rollup开发环境

  • 根据开发环境区分不同的配置
  • 设置对应的 npm script
  • 输出不同规范的产物:umd、umd.min、cjs、esm、iife(global)

在scripts目录下创建rollup配置文件

  1. ├── rollup.config.base.js // 基础文件
  2. ├── rollup.config.dev.js // 开发环境的配置
  3. └── rollup.config.prod.js // 生产环境的配置

rollup.config.base.js

  1. // 安装以下 npm 包
  2. // npm install -D @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-alias @rollup/plugin-replace @rollup/plugin-eslint @rollup/plugin-babel rollup-plugin-terser rollup-plugin-clear @rollup/plugin-json
  3. import { nodeResolve } from '@rollup/plugin-node-resolve' // 解析 node_modules 中的模块
  4. import commonjs from '@rollup/plugin-commonjs' // cjs => esm
  5. import alias from '@rollup/plugin-alias' // alias 和 reslove 功能
  6. import replace from '@rollup/plugin-replace'
  7. import eslint from '@rollup/plugin-eslint'
  8. import { babel } from '@rollup/plugin-babel'
  9. import { terser } from 'rollup-plugin-terser'
  10. import clear from 'rollup-plugin-clear'
  11. import json from '@rollup/plugin-json' // 支持在源码中直接引入json文件,不影响下面的
  12. import { name, version, author } from '../package.json'
  13. const pkgName = 'sumfunctionmethods'
  14. // 打包处理的文件,添加的备注信息
  15. const banner =
  16. '/*!\n' +
  17. ` * ${name} v${version}\n` +
  18. ` * (c) 2022-${new Date().getFullYear()} ${author}\n` +
  19. ' * Released under the MIT License.\n' +
  20. ' */'
  21. export default {
  22. input: 'src/index.js',
  23. // 同时打包多种规范的产物
  24. output: [
  25. {
  26. file: `dist/${pkgName}.umd.js`,
  27. format: 'umd',
  28. name: pkgName,
  29. banner
  30. },
  31. {
  32. file: `dist/${pkgName}.umd.min.js`,
  33. format: 'umd',
  34. name: pkgName,
  35. banner,
  36. plugins: [terser()]
  37. },
  38. {
  39. file: `dist/${pkgName}.cjs.js`,
  40. format: 'cjs',
  41. name: pkgName,
  42. banner,
  43. plugins: [terser()]
  44. },
  45. {
  46. file: `dist/${pkgName}.esm.js`,
  47. format: 'es',
  48. name: pkgName,
  49. banner,
  50. plugins: [terser()]
  51. },
  52. {
  53. file: `dist/${pkgName}.js`,
  54. format: 'iife',
  55. name: pkgName,
  56. banner,
  57. plugins: [terser()]
  58. }
  59. ],
  60. // 注意 plugin 的使用顺序
  61. plugins: [
  62. json(),
  63. clear({
  64. targets: ['dist']
  65. }),
  66. alias(),
  67. replace({
  68. 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
  69. preventAssignment: true
  70. }),
  71. nodeResolve(),
  72. commonjs({
  73. include: 'node_modules/**'
  74. }),
  75. eslint({
  76. throwOnError: true, // 抛出异常并阻止打包
  77. include: ['src/**'],
  78. exclude: ['node_modules/**']
  79. }),
  80. babel({ babelHelpers: 'bundled' })
  81. ]
  82. }

rollup.config.dev.js

  1. // npm install -D rollup-plugin-serve rollup-plugin-livereload
  2. import baseConfig from './rollup.config.base'
  3. import serve from 'rollup-plugin-serve'
  4. import livereload from 'rollup-plugin-livereload'
  5. export default {
  6. ...baseConfig,
  7. plugins: [
  8. ...baseConfig.plugins,
  9. serve({
  10. port: 8080,
  11. contentBase: ['dist', 'examples/brower'],
  12. openPage: 'index.html',
  13. }),
  14. livereload({
  15. watch: 'examples/brower',
  16. })
  17. ]
  18. }

rollup.config.prod.js

  1. // npm install -D rollup-plugin-filesize
  2. import baseConfig from './rollup.config.base'
  3. import filesize from 'rollup-plugin-filesize'
  4. export default {
  5. ...baseConfig,
  6. plugins: [
  7. ...baseConfig.plugins,
  8. filesize()
  9. ]
  10. }

配置 babel 解析兼容

安装依赖 npm i -D @babel/core @babel/preset-env
添加文件.babelrc.js

  1. module.exports = {
  2. presets: [
  3. ['@babel/preset-env', {
  4. // rollupjs 会处理模块,所以设置成 false
  5. modules: false
  6. }]
  7. ],
  8. plugins: [
  9. ]
  10. }

设置eslint和pretter统一代码风格

eslint验证代码是否符合定义的规范

  • eslint-plugin-vue:vue.js的Eslint插件(查找vue语法错误,发现错误指令,查找违规风格指南)
  • eslint-plugin-prettier:运行更漂亮的Eslint,使prettier规则优先级更高,Eslint优先级低
  • eslint-config-prettier:让所有可能与prettier规则存在冲突的Eslint rules失效,并使用prettier进行代码检查
  • @babel/eslint-parser:该解析器允许使用Eslint校验所有babel code,仅支持最新的最终ECMAScript标准,不支持实验性语法,该编译器会将code解析为Eslint能懂的EsTree(ES2021语法等等)
  1. npm i -D eslint
  2. // 生成配置文件,.eslintrc.js
  3. npx eslint --init
  4. // 使用 standard 规范
  5. npm install --save-dev eslint-plugin-import eslint-plugin-node eslint-plugin-prettier eslint-config-prettier @babel/eslint-parser
  6. // .eslintrc.js 配置
  7. module.exports = {
  8. root: true,
  9. env: {
  10. browser: true,
  11. es2021: true,
  12. node: true,
  13. jest: true,
  14. },
  15. extends: [
  16. 'eslint:recommended', // eslint
  17. 'plugin:prettier/recommended', // plugin-prettier
  18. 'plugin:vue/vue3-recommended', // plugin-vue
  19. ],
  20. parserOptions: {
  21. parser: '@babel/eslint-parser', // 解析器
  22. ecmaVersion: 'latest',
  23. sourceType: 'module',
  24. },
  25. // rules: { //也可设置一个prettier验证规则
  26. // 'prettier/prettier': 'error', // Runs Prettier as an ESLint rule and reports differences
  27. // },
  28. rules: {
  29. 'space-before-function-paren': ['error', 'never'],
  30. semi: 0, // 结尾不要分号
  31. },
  32. }
  33. // .eslintignore 配置, 防止校验打包的产物
  34. dist
  35. node_modules

之后可以在package.json 中添加运行脚本

  1. "scripts": {
  2. "lint": "eslint src",
  3. "fix": "eslint src --fix",
  4. }

prettier格式化代码符合定义的规范

安装prettier包

  1. npm install -D eslint-plugin-prettier prettier eslint-config-prettier

添加配置文件

  1. {
  2. "singleQuote": true,
  3. "semi": false,
  4. "bracketSpacing": true,
  5. "htmlWhitespaceSensitivity": "ignore",
  6. "endOfLine": "auto",
  7. "trailingComma": "all",
  8. "tabWidth": 2
  9. }

使用lint-staged

对提交到暂存区的代码做校验,lint-staged 是一个在git暂存文件上运行linter的工具。可以设置对某些类型文件做特殊处理如eslint和prettier
安装和配置代码质量工具lint-staged

  1. // 安装依赖
  2. yarn add -D lint-staged
  1. // package.json中添加脚本,以及配置
  2. "scripts": {
  3. "lint-staged": "lint-staged",
  4. },
  5. "lint-staged": {
  6. // 匹配暂存区所有的js文件,并执行命令
  7. "src/*.{js}": [
  8. "prettier --write",
  9. "eslint --cache --fix",
  10. "git add"
  11. ]
  12. }

配置 git 提交的校验钩子

  • husky: git提交时触发hooks
  • commitlint: 对提交的内容做规范校验

husky,主要对pre-commit和commit-msg钩子做校验。

  1. // 安装husky
  2. yarn add husky -D
  3. npx husky-init // 初始化husky配置,在根目录新增.husky配置文件。初始化配置pre-commit
  4. npx husky add .husky/commit-msg // 另外新增一个hooks,commit-msg
  • pre-commit中添加 npm run lint-staged
  • commit-msg中添加 npm run commitlint

安装commitlint

  1. // 添加依赖文件
  2. yarn add @commitlint/config-conventional @commitlint/cli -D
  3. // 添加配置文件commitlint.config.js
  4. module.exports = { extends: ['@commitlint/config-conventional'] }; //基本设置

给commitlint.config.js自定义校验规则

  1. module.exports = {
  2. extends: ['@commitlint/config-conventional'],
  3. // 校验规则
  4. rules: {
  5. 'type-enum': [
  6. 2,
  7. 'always',
  8. [
  9. 'feat', //新功能(feature)
  10. 'fix', //修补bug
  11. 'docs', //文档(documentation)
  12. 'style', //格式(不影响代码运行的变动)
  13. 'refactor', //重构(即不是新增功能,也不是修改bug的代码变动)
  14. 'perf', //性能提升(提高性能的代码改动)
  15. 'test', //测试
  16. 'chore', // 不修改src或测试文件的其他更改
  17. 'revert', //撤退之前的commit
  18. 'build' //构建过程或辅助工具的变动(webpack等)
  19. ]
  20. ],
  21. 'type-case': [0],
  22. 'type-empty': [0],
  23. 'scope-empty': [0],
  24. 'scope-case': [0],
  25. 'subject-full-stop': [0, 'never'],
  26. 'subject-case': [0, 'never'],
  27. 'header-max-length': [0, 'always', 72]
  28. }
  29. }

使用 commitizen 做git规范化提交

由于添加了commitlint验证,对于不熟悉提交规范的新手同学会有一定影响,可以添加 commitizen 工具,手动生成规范化commit。
Commitizen是一个格式化commit message的工具。介绍

  1. // 工具安装
  2. yarn add -D commitizen

配置命令

  1. "script": {
  2. "commit": "git-cz"
  3. }

安装规则包

  1. npx commitizen init cz-conventional-changelog --yarn --dev --exact

使用 cz-conventional-changelog 做提交规则
可以在package中定义

  1. "config": {
  2. "commitizen": {
  3. "path": "cz-conventional-changelog"
  4. }
  5. }

单独创建.czrc文件

  1. {
  2. "path": "cz-conventional-changelog"
  3. }

以后进行commit提交时,可以采用npm run commit也就是使用了 git-cz 中的规则。

自定义 commitizen 规则

  1. 第一种方法可以直接修改package中的config
    1. "config": {
    2. "commitizen": {
    3. "path": "cz-conventional-changelog",
    4. "type": {
    5. "test": {
    6. "description": "测试自定义 commitizen 规则",
    7. "title": "测试自定义 commitizen"
    8. }
    9. }
    10. }
    11. }
  1. 使用 cz-customizable 工具

    • 安装依赖

      1. yarn add cz-customizable -D
    • 在package.json 中添加自定义commitizen,使用git-cz执行git commit命令

      1. "config": {
      2. "commitizen": {
      3. "path": "./node_modules/cz-customizable"
      4. }
      5. }
    • 在根目录创建的.cz-config.js, 自定义commit提示内容

      1. module.exports = {
      2. types: [
      3. { value: 'feat', name: 'feat: 新功能' },
      4. { value: 'fix', name: 'fix: 修复' },
      5. { value: 'docs', name: 'docs: 文档变更' },
      6. { value: 'style', name: 'style: 代码格式(不影响代码运行的变动)' },
      7. {
      8. value: 'refactor',
      9. name: 'refactor: 重构(既不是增加feature,也不是修复bug)'
      10. },
      11. { value: 'perf', name: 'perf: 性能优化' },
      12. { value: 'test', name: 'test: 增加测试' },
      13. { value: 'chore', name: 'chore: 构建过程或辅助工具的变动' },
      14. { value: 'revert', name: 'revert: 回退' },
      15. { value: 'build', name: 'build: 打包' }
      16. ],
      17. // 消息步骤
      18. messages: {
      19. type: '请选择提交类型:',
      20. // scope: '请输入文件修改范围(可选):',
      21. // used if allowCustomScopes is true
      22. customScope: '请输入修改范围(可选):',
      23. subject: '请简要描述提交(必填):',
      24. body: '请输入详细描述(可选,待优化去除,跳过即可):',
      25. // breaking: 'List any BREAKING CHANGES (optional):\n',
      26. footer: '请输入要关闭的issue(待优化去除,跳过即可):',
      27. confirmCommit: '确认使用以上信息提交?(y/n/e/h)'
      28. },
      29. allowCustomScopes: true,
      30. // allowBreakingChanges: ['feat', 'fix'],
      31. skipQuestions: ['body', 'footer'],
      32. // limit subject length, commitlint默认是72
      33. subjectLimit: 72
      34. }

添加jest测试工具

  • 选用 jest 做单元测试
  • 配置 eslint 的 jest 环境
  • 解决 jest 不支持 es module 的问题
  • test 目录下创建 [name].test.js(name 和 源码中的文件名保持一致)
  1. // 安装jest依赖
  2. npm i -D jest
  3. // 使得支持 `es module`
  4. npm i -D rollup-jest

在package.json中设置

  1. "scripts":{
  2. "test": "jest",
  3. "test:c": "jest --coverage",
  4. },
  5. "jest": {
  6. "preset": "rollup-jest"
  7. }

执行 npm run test 进行测试
执行 npm run test:c 查看测试覆盖率

完善 package.json 必要字段

  • version: 版本好
  • main: 主入口
  • module: cjs入口
  • exports: 配置多种类型的入口
  • unpkg: cdn地址
  • jsdelivr: cdn地址
  • files: 发布库时包含的文件
  • keywords: 关键测
  • homepage: github的readme
  • repository: 仓库地址
  • bugs: bug提交
  • dependencies: 库依赖文件
  • devDependencies: 开发时依赖的库

main module exports的区别

main 是npm包的主要入口文件,当导入一个包时,实际上就是使用的main指向的地址文件。
main是遵循commonjs规范的,使用module.exports 。
module是ESM规范,是前端使用比较多的规范,使用import/export。

如果使用import导入库,最开始匹配module的文件,找不到则使用main指向的文件。
rollup打包工具提供了多种打包格式,可以在output中进行设置。

  1. // rollup.config.js
  2. export default {
  3. ...,
  4. output: {
  5. file: 'bundle.js',
  6. format: 'iife', // 可以是amd、cjs、esm、iife、umd、system
  7. name: 'MyBundle'
  8. }
  9. };
  • amd – 异步模块定义,用于像RequireJS这样的模块加载器
  • cjs – CommonJS,适用于 Node 和 Browserify/Webpack
  • esm – 将软件包保存为 ES 模块文件,在现代浏览器中可以通过