coverPiccoverPic

从零到一搭建一个前端工具函数库

🎈前言

工具函数库为开发人员提供高效、便捷的工具函数,简化开发流程,减少模板代码,提高代码质量和可维护性。在团队协作或长期维护的项目中,工具函数库是提升开发效率的关键环节。

本文将手把手教你如何从零搭建一个强类型、易维护的前端工具函数库。这篇博客以我的开源项目 Parsnip-Kit 为例,解析搭建前端工具函数库的技术。

Parsnip-Kit 这个库主要是把开发中遇到的较常用的函数进行一个整合,形成的一个工具库。它是一个零依赖、多功能、模块化的 JavaScript 工具库,支持 TypeScript。

Github:https://github.com/shika-space/parsnip-kit
文档站点:https://shika-space.github.io/parsnip-kit/

🎇项目架构全景

问了一下 AI,一个优秀的工具函数库应该具备:

  • 模块化设计:按功能领域划分模块
  • 类型友好:完善的 TypeScript 类型声明
  • 高质量保障:单元测试覆盖率 > 95%
  • 开箱即用:支持多种模块化方案(ESM/CJS/UMD)
  • 开发友好:完整的文档和示例代码

Parsnip-Kit 也符合这些要求,Parsnip-Kit 项目基于现代构建工具 Vite 构建,结合了 Vitest 单元测试和 ESLint + Prettier 代码规范,保证项目迭代更新中保持代码质量。

除此之外,我使用了 VitePress 搭建文档站点,基于 inquirer 搭建了新建模块脚手架,基于 comment-parser 搭建的文档生成工具,减少了迭代更新中的重复操作。

---
title: 
config:
  theme: default
---
graph TB
O[项目架构]---A[类型安全]---TypeScript
O---C[构建方案]---Vite
C---dts-bundle-generator
O---D[单元测试]---Vitest
O---E[代码规范]---Eslint
E---Prettier
O---F[Git 工作流]---Husky
F---Lint-Staged
O---G[脚手架]---Inquirer
O---H[文档生成]---Comment-Parser
O---I[文档站点]---VitePress

linkStyle 0 stroke:#9370DB,stroke-width:2px
linkStyle 1 stroke:#9370DB,stroke-width:2px
linkStyle 2 stroke:#9370DB,stroke-width:2px
linkStyle 3 stroke:#9370DB,stroke-width:2px
linkStyle 4 stroke:#9370DB,stroke-width:2px
linkStyle 5 stroke:#9370DB,stroke-width:2px
linkStyle 6 stroke:#9370DB,stroke-width:2px
linkStyle 7 stroke:#9370DB,stroke-width:2px
linkStyle 8 stroke:#9370DB,stroke-width:2px
linkStyle 9 stroke:#9370DB,stroke-width:2px
linkStyle 10 stroke:#9370DB,stroke-width:2px
linkStyle 11 stroke:#9370DB,stroke-width:2px
linkStyle 12 stroke:#9370DB,stroke-width:2px
linkStyle 13 stroke:#9370DB,stroke-width:2px
linkStyle 14 stroke:#9370DB,stroke-width:2px
linkStyle 15 stroke:#9370DB,stroke-width:2px
linkStyle 16 stroke:#9370DB,stroke-width:2px
linkStyle 17 stroke:#9370DB,stroke-width:2px
linkStyle 18 stroke:#9370DB,stroke-width:2px

🧨类型安全:TypeScript

TypeScript 作为 JavaScript 的超集,为开发带来了诸多优势。它通过添加静态类型系统,在编译阶段就能对代码进行类型检查,有效预防错误,减少运行时出现的bug。TS 提供的类型约束、类型推导,也为使用者提供了清晰的指引。

✨构建方案:Vite

Vite 是一个现代的前端构建工具,对 TypeScript 有很好的支持,提供了极快的开发体验。

作为一个工具库,我们需要的是库模式打包,Parsnip-Kit 的项目打包配置如下,它支持了 ESModule、CommonJs、UMD 3 种模块规范,使得库可以用在不同模块规范的场景中:

ts
  1. import { dirname, resolve } from 'node:path'
  2. import { fileURLToPath } from 'node:url'
  3. const __dirname = dirname(fileURLToPath(import.meta.url))
  4. export default defineConfig({
  5. // ...
  6. build: {
  7. lib: {
  8. entry: resolve(__dirname, 'packages/main.ts'),
  9. name: 'parsnip-kit',
  10. fileName: 'parsnip-kit',
  11. formats: ['es', 'umd', 'cjs']
  12. },
  13. },
  14. })

生成 dts 文件一般是用 vite-plugin-dts,在 Vite 5 上,这个插件跑不起来了,这里改成用 dts-bundle-generator。

sh
  1. npm i dts-bundle-generator -D

在根目录新建 dts-bundle-generator.config.json

json
  1. {
  2. "compilationOptions": {
  3. "preferredConfigPath": "./tsconfig.json"
  4. },
  5. "entries": [
  6. {
  7. "filePath": "./packages/main.ts",
  8. "outFile": "./dist/parsnip-kit.d.ts"
  9. }
  10. ]
  11. }

构建的命令这么写就好了:

json
  1. {
  2. "scripts": {
  3. "build": "vite build && npx dts-bundle-generator -config dts-bundle-generator.config.json"
  4. }
  5. }

🎉单元测试:Vitest

Vitest 是一个轻量级的测试框架,它与 Vite 无缝集成,能够快速运行测试用例。重要的是 Vitest API 设计与 Jest 保持一致,整合了 v8、Istanbul 等测试覆盖率统计工具,拥有多种报告输出器,使得我们可以一站式地完成单元测试配置。

下面是 Parsnip-Kit 的项目的 Vitest 配置,它使用了默认的 v8 统计测试覆盖率,并且使用了 JSON 报告器输出,以便后续在文档中展示测试覆盖率:

ts
  1. import { defineConfig } from 'vitest/config'
  2. export default defineConfig({
  3. test: {
  4. reporters: ['json', 'default'],
  5. outputFile: 'coverage/coverage.json',
  6. exclude: ["node_modules", "dist", "script"],
  7. coverage: {
  8. exclude: ["node_modules", "dist", "script", "packages/common/types.ts"],
  9. include: ["packages"],
  10. }
  11. },
  12. // ...
  13. })

🎊代码规范体系:ESLint + Prettier

ESLint 主要是代码质量的代码检查工具。它在 v8.53.0 版本后,移除了所有代码风格的检查,我们也不用过多考虑它和 Prettier 的冲突问题了。Prettier 专注于对代码风格进行处理。

参考这篇博客:ts 项目如何从 ESlint8 升级至 ESlint9 并集成 Prettier 的最新写法,Parsnip-Kit 项目使用了 ESLint + Prettier,并且让它们支持 TypeScript 的检查。

先安装这些依赖:

sh
  1. npm i eslint eslint-config-prettier eslint-plugin-prettier typescript-eslint -D

在根目录新建 eslint.config.js,这是项目的配置:

js
  1. import js from "@eslint/js"
  2. import globals from "globals"
  3. import tseslint from "typescript-eslint"
  4. import eslintConfigPrettier from "eslint-config-prettier"
  5. import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"
  6. export default tseslint.config({
  7. extends: [
  8. js.configs.recommended,
  9. ...tseslint.configs.recommended,
  10. eslintConfigPrettier,
  11. eslintPluginPrettierRecommended,
  12. ],
  13. files: ["**/*.{ts,js}"], // eslint 检测的文件,根据需要自行设置
  14. ignores: ["dist", "node_modules"],
  15. languageOptions: {
  16. ecmaVersion: 2020,
  17. globals: globals.browser,
  18. },
  19. rules: {
  20. "prettier/prettier": ["warn", {
  21. "semi": false,
  22. "singleQuote": true,
  23. 'trailingComma': 'none'
  24. }], // 默认为 error
  25. "arrow-body-style": "off",
  26. "prefer-arrow-callback": "off",
  27. "@typescript-eslint/no-explicit-any": "off", // allow any type
  28. "@typescript-eslint/no-unsafe-function-type": "off",
  29. "prefer-rest-params": "off",
  30. "@typescript-eslint/no-this-alias": "off",
  31. "@typescript-eslint/ban-ts-comment": "off"
  32. },
  33. })

然后就加入检测的命令到 package.json

json
  1. {
  2. "scripts": {
  3. "lint": "npx eslint packages/",
  4. "lint:fix": "npx eslint packages/ --fix",
  5. }
  6. }

🔧Git 工作流:Husky

Husky 是一个可以在 git 钩子中执行代码的工具,我们可以在 git 钩子中,执行代码检查,保证提交代码的质量。

sh
  1. npm install husky -D
  2. npx husky init

在项目根目录 .husky 文件夹中可以看到一个 pre-commit 文件,这里可以填入在提交之前执行的命令。

我们如果直接 npm run lint:fix 势必会涉及到未提交内容在内的所有文件。为了只检查已添加到 Git 中的文件,我们需要 lint-staged。

sh
  1. npm install lint-staged -D

之后在 package.json 添加以下配置:

json
  1. {
  2. "lint-staged": {
  3. "*.{js,ts}": [
  4. "eslint --fix",
  5. "git add"
  6. ]
  7. }
  8. }

这样子就可以在提交时对代码进行检查和修复了。

🎁脚手架:Inquirer

inquirer 是用于搭建命令行交互式问答的工具,利用它,我们可以通过简单的选项和输入,在项目中新增一个模块或者函数。举个简单的例子:


在用户输入完后,拿到回答再去新建相应的文件就好了。

下面就是这段对话的关键代码,详情请看这里:init.js

js
  1. const getSelectedModule = async () => {
  2. const modules = getPackageModules();
  3. const choices = modules.map(module => ({
  4. name: module,
  5. value: module
  6. }));
  7. choices.push({
  8. name: 'Create a new module',
  9. value: 'new'
  10. });
  11. const answers = await inquirer.prompt([{
  12. type: 'list',
  13. name: 'module',
  14. message: 'Please select or create a module:',
  15. choices: choices
  16. }]);
  17. if (answers.module === 'new') {
  18. const newModuleAnswer = await inquirer.prompt([{
  19. type: 'input',
  20. name: 'newModuleName',
  21. message: 'Please enter the name of the new module:',
  22. validate: input => {
  23. if (/^[a-zA-Z]+$/.test(input)) {
  24. return true;
  25. }
  26. return 'Module name must be in English';
  27. }
  28. }]);
  29. return newModuleAnswer.newModuleName;
  30. }
  31. return answers.module;
  32. };

📚文档生成:Comment-Parser

Parnip-Kit 的文档生成是借助 comment-parser 解析函数上的 JSDoc 注释,然后结合借助上文中脚手架生成的 markdown 模板文件完成的。

这个思路主要来自掘金上的这篇博客:开源一个可以将带有JSDoc注释的JS或TS文件转换为markdown的工具,作者把 JSDoc 转换为了 markdown 文档,极大简便了文档的维护。

但是这种方式也有不满足 Parsnip-Kit 场景的地方,我们的文档支持了多语言,如果按照这种方式,需要在注释中写一大堆多语言的东西,比较影响维护。后面参考了 Element-Plus 等组件文档的方法,函数的入参、返回以及简单的描述写在 JSDoc 注释中,在 markdown 模板中去嵌入它们。多语言和文档的更多详细描述转移到在 markdown 模板文件中完成。详见 generateMd.js

举个例子:

函数文件:

ts
  1. /**
  2. * Splits array `arr` into sub-arrays with the length of `length`.
  3. * @template {} T Type of elements of array to split
  4. * @param {T[]} arr The array to split
  5. * @param {length} length The length of sub-arrays
  6. * @returns {T[][]}
  7. * @version 0.0.2
  8. */
  9. export function chunk<T>(arr: T[], length: number) {
  10. // ...
  11. }

中文的 markdown 模板:

md
  1. # chunk
  2. [[[desc chunk
  3. 将数组 `arr` 分割成长度为 `length` 的子数组。
  4. ]]]
  5. [[[version chunk]]]
  6. ### Usage
  7. ```ts
  8. import { chunk } from 'parsnip-kit'
  9. const arr = [1, 2, 3, 4, 5]
  10. chunk(arr, 2)
  11. // [[1, 2], [3, 4], [5]]
  12. \`\`\`
  13. // 实际文件中 \`\`\` 没有 \ 符号,主要是因为不这样写在博客上 md 解析会出问题
  14. ### API
  15. #### Type Parameter
  16. [[[template chunk
  17. T: 要分割的数组的元素类型
  18. ]]]
  19. #### Arguments
  20. [[[params chunk
  21. arr: 要分割的数组
  22. length: 子数组的长度
  23. ]]]
  24. #### Returns
  25. [[[returns chunk]]]

🎗文档站点:VitePress

VitePress 是 Vite 官方推出的一个静态站点生成器,特别适合用于构建技术文档站点。支持 Markdown 和 Vue 组件,允许我们在文档中嵌入交互式的示例和组件,增强文档的可读性和实用性。

这里内容比较多,下次再展开说,这是 Parsnip-Kit 的效果,站点:https://shika-space.github.io/parsnip-kit/

🧸结语

通过本文的完整技术方案,您也可以打造属于自己的工具库。

Parsnip-Kit 最早是为了减少自己开发中的模板代码而设计的,现在开源并且持续迭代,欢迎 Star ⭐ 和贡献代码!

Github:https://github.com/shika-space/parsnip-kit
文档站点:https://shika-space.github.io/parsnip-kit/

0 条评论未登录用户
Ctrl or + Enter 评论
© 2023-2025 LittleRangiferTarandus, All rights reserved.
🌸 Run