Posted in

自己动手实现一个无头组件库_AI阅读总结 — 包阅AI

包阅导读总结

1. 关键词:Headless UI、无头组件库、Vue3、单元测试、文档

2. 总结:本文介绍了手动实现 Vue3 的 Headless UI 无头组件库的过程,包括初始化项目、配置相关工具、搭建核心库、进行单元测试、配置文档和 playground 等,还提到了打包构建、发布及安装使用,强调这只是基本搭建,后续将完善更详细配置。

3. 主要内容:

– 初始化项目

– 生成目录并初始化

– 创建 pnpm-workspace.yaml 文件

– 新增目录和依赖

– 新增 README.md 和 LICENSE 文件

– 安装配置工具

– 配置 eslint 和相关基本配置

– 配置 simple-git-hooks 等

– 配置 package/vue 核心库

– 初始化并安装库

– 配置工具库和 tsconfig

– 安装 vite 相关插件

– 新建 src 和 build 并测试

– 配置 exports

– 单元测试

– 安装 vitest 并配置

– 新建测试文件并运行

– 配置 docs 文档

– 安装配置 vitepress

– 配置 playground

– 新建项目并引入核心库

– 打包构建

– 配置统一管理和安装 rimraf

– 进行打包

– 发布与安装使用

– 登录 npm 并处理相关配置

– 安装 changesets 并配置

– 发布与安装使用

思维导图:

文章地址:https://mp.weixin.qq.com/s/-xN9F5ni66NOcD-u6z3tAQ

文章来源:mp.weixin.qq.com

作者:易师傅

发布时间:2024/6/15 7:27

语言:中文

总字数:3809字

预计阅读时间:16分钟

评分:86分

标签:无头组件库,Vue3,组件库,TypeScript,ESLint


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

前言

前面咱们已经介绍了,什么是 Headless UI 无头组件库了,以及如何去使用它,我相信同学们看完了之后能够已经在实际项目中运用自如了;

ps:作者之前的文章可以点击文末阅读原文,进入作者主页进行查看。

但是我的目的是带领大家能实现一个属于自己的,一个属于公司的、甚至属于公司和个人 KPI 的产物,那么接下来将会手摸手的一步一步指引大家去实现一个真正意义上的 Headless UI 无头组件库

让我们一起开始动手吧~

说明:为了更好符合国内的大部分用户群体,所以主要实现一个 vue3 的 Headless UI 无头组件库

一、初始化项目

1. 生成目录 & 初始化

#创建目录
mkdirmy-project

#进入
cdmy-project

#初始化
pnpminit

2. 创建 pnpm-workspace.yaml 文件

touchpnpm-workspace.yaml

3. 修改 pnpm-workspace.yaml

packages:
- packages/*
- playground
- docs

4. 新增 packages/vue 目录

mkdirpackages

mkdirpackages/vue

cdpackages/vue

pnpminit

5. 新增 typescript 依赖

#安装到my-project根目录下
pnpmitypescript@types/node-Dw

#初始化
npxtsc--init

6. 新增 README.mdLICENSE 文件

touchREADME.mdLICENSE

二、安装 eslintsimple-git-hooks + commitlint 等基本配置

1. 配置 eslint@antfu/eslint-config

  1. 安装

    pnpmieslint@antfu/eslint-config-Dw

    @antfu/eslint-config 是一个由 Anthony Fu 创建的 ESLint 配置包,它包含了 Vue 和 Vanilla JS 项目中常见的最佳实践规则,实际项目可安可不安。

  2. 编辑 eslint.config.js 文件:

    import antfu from '@antfu/eslint-config'

    export default antfu({
    ignores: ['/dist', '/node_modules', '/packages/**/dist', '/packages/**/node_modules'],
    rules: {
    '@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
    '@typescript-eslint/ban-ts-comment': 'warn',
    '@typescript-eslint/consistent-type-definitions': 'off',
    'import/first': 'off',
    'import/order': 'off',
    'symbol-description': 'off',
    'no-console': 'warn',
    'max-statements-per-line': ['error', { max: 2 }],
    'vue/one-component-per-file': 'off',
    },
    })

  3. package.jsonscript添加脚本

    "lint":"eslint.",
    "lint:fix":"eslint.--fix",
  4. 测试

    image.png

2. 配置 simple-git-hookslint-staged

  1. 安装

    pnpmisimple-git-hookslint-staged-Dw
  2. package.json 中添加脚本

    script:{
    "prepare":"npxsimple-git-hooks",
    },
    "lint-staged":{
    "*":"eslint--fix"
    }

3. 配置 @commitlint/config-conventional@commitlint/cli

  1. 安装

    pnpmi@commitlint/config-conventional@commitlint/cli-Dw
  2. package.json 中添加配置和脚本

    "commitlint":{
    "extends":[
    "@commitlint/config-conventional"
    ]
    },
    "simple-git-hooks":{
    "pre-commit":"pnpmlint-staged",
    "commit-msg":"pnpmcommitlint--edit${1}"
    },

4.执行

根据上面的配置,我们在每次修改文件 git 提交后,都会按照以下顺序执行:

  1. npx simple-git-hooks:执行 simple-git-hooks 命令;
  2. pnpm lint-staged:执行 lint-staged 命令;
  3. eslint --fix:执行 eslint 方法,检查所有的代码是否合格;

比较 simple-git-hookshusky

1. 共同点:

  • 都是用于管理 Git 钩子(Git hooks)的工具,它们可以帮助开发团队在代码提交、推送等操作时运行预定义的脚本或命令。
  • 可以用来执行代码格式化、静态代码分析、单元测试等任务,以确保代码质量和一致性。
  • 都提供了对本地 Git 钩子的支持,包括但不限于pre-commitpost-commitpre-push等钩子。

2. 差异点:

    • husky提供了更多的功能和灵活性,可以在提交前、提交后、推送前等不同的 Git 钩子上运行任务,并且支持与其他工具(如 lint-staged)的集成。
    • simple-git-hooks则专注于简单的 Git 钩子管理,功能相对较少,主要用于运行基本的脚本任务。
    • husky提供了更丰富的配置选项和定制能力,可以根据项目的需求定义更复杂的钩子行为。
    • simple-git-hooks更注重简单易用,配置相对简单,适合对 Git 钩子管理要求不高的项目。
    • husky在社区中有着更广泛的应用和支持,拥有更丰富的生态系统和插件,可以满足不同需求。
    • simple-git-hooks的用户群体相对较小,生态系统相对简单。

总结:

选择使用simple-git-hooks还是husky取决于项目的具体需求和团队的偏好。根据项目的规模、复杂度以及对 Git 钩子管理的需求,选择适合的工具可以提高开发流程的效率和代码质量。

三、配置package/vue核心库

1. 初始化 package/vue

#进入目录
cdpackage/vue

#初始化
pnpminit

2. 安装用到的基本库

  • vue-tsc: vue-tsc 是对TypeScript 自身命令行界面 tsc 的一个封装。它的工作方式基本和 tsc 一致。
pnpminstallvuevue-tsc-D

3. 配置工具库(可选)

  • @vueuse/core:一个针对 Vue.js 生态系统的工具库,旨在提供一组通用的、经过测试的 Vue 3 组合式 API,帮助开发者更轻松地构建 Vue 应用程序。
pnpminstall@vueuse/core-D

4. 配置 tsconfig

  • @tsconfig/node18:是 TypeScript 中的一个预定义的配置文件,它适用于 Node.js 18 的项目。在 TypeScript 中,可以使用预定义的配置文件来简化项目的配置过程,而不必手动指定所有的编译选项。
  • @vue/tsconfig:一个 TypeScript 配置文件的包装库,用于简化在 Vue.js 项目中配置 TypeScript 的过程。这个包装库提供了一组预定义的 TypeScript 配置,旨在帮助开发者轻松地配置 TypeScript 在 Vue.js 项目中的使用。如果你们不需要它的配置,可以自己写。

1. 安装 typescript@tsconfig/node18@vue/tsconfig

pnpminstalltypescript@tsconfig/node18@vue/tsconfig-D

2. 添加 tsconfig 配置文件

  • tsconfig.json:ts 配置,不解释

    {
    "files":[],
    "extends":["./tsconfig.app.json"]
    }
  • tsconfig.app.json:定义项目中所需文件的基本ts编译规则

    {
    "extends":"@vue/tsconfig/tsconfig.dom.json",
    "include":[
    "env.d.ts",
    "src/**/*",
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue"
    ],
    "compilerOptions":{
    "paths":{
    "@/*":["src/*"],
    },
    "target":"esnext",
    "module":"esnext",
    "moduleResolution":"node",
    "strict":true,
    "jsx":"preserve",
    "sourceMap":true,
    "resolveJsonModule":true,
    "esModuleInterop":true,
    "declaration":false,
    "lib":["esnext","dom"],
    "baseUrl":".",
    "skipLibCheck":true,
    "outDir":"dist"
    }
    }

  • tsconfig.build.json:主要用来打包所用的 ts 编译规则,执行 pnpm build 所需规则(代码与 tsconfig.app.json 类似)
  • tsconfig.node.json:专门用来配置vite.config.ts文件的编译规则

    {
    "extends":"@tsconfig/node18/tsconfig.json",
    "include":[
    "vite.config.*",
    "vitest.config.*",
    "cypress.config.*",
    "nightwatch.conf.*",
    "playwright.config.*"
    ],
    "compilerOptions":{
    "composite":true,
    "module":"ESNext",
    "types":["node"]
    }
    }

5. 安装 vite 相关

  • @vitejs/plugin-vue:Vite 的一个插件,用于处理 Vue 单文件组件(SFC)。
  • vite-plugin-dts:一个 Vite 插件,用于自动生成 TypeScript 类型声明文件(.d.ts 文件)并将其输出到构建目录中。
pnpminstallvite@vitejs/plugin-vuevite-plugin-dts-D

配置 vite.config.ts

import{resolve}from'node:path'
import{defineConfig}from'vite'
importvuefrom'@vitejs/plugin-vue'
importdtsfrom'vite-plugin-dts'

//https://vitejs.dev/config/
exportdefaultdefineConfig({
plugins:[
vue(),
dts({
tsconfigPath:'tsconfig.build.json',
cleanVueFileName:true,
exclude:['src/test/**'],
}),
],

build:{
lib:{
name:'yi-ui',
fileName:'index',
entry:resolve(__dirname,'src/index.ts'),
},
},
})

6. 新建 src & build 测试

创建 src

mkdirsrc

touchsrc/index.ts

编辑 src/index.ts

consta='1111'

constb='2222'

constfn=()=>{
console.log('fn')
}

constadd=(a:number,b:number)=>{
returna+b
}

export{a,b,fn,add}

添加 package.json 脚本

"build":"vitebuild",

build 构建

pnpmbuild
image.png

结果就会生成了 dist目录,如下图的文件

image.png

因为我们要的是能开箱即用,所以 esm、cjs 的文件格式就要配置好

7. 配置 exports 默认模块

"exports":{
".":{
"types":"./dist/index.d.ts",
"require":"./dist/index.umd.js",
"import":"./dist/index.mjs"
}
},
"main":"./dist/index.umd.js",
"module":"./dist/index.mjs",
"types":"./dist/index.d.ts",
"typings":"./dist/index.d.ts",

这样我们就可以直接在项目中使用 import 或者 require 来使用库了;

例如:

import{add}from'@yi-ui/vue'

add(1,2)

在下面讲解的 docs文档 和 playground 也会使用到;


到这里一个最最基本的核心库就完成了最基本的搭建,下一步我们考虑的就是测试问题了

四、单元测试

为什么要用单元测试

因为我们写的是一个上层的工具库,所以单元测试是必不可少的;

毕竟单元测试可以验证库的每个功能模块是否按照预期工作;

在开发阶段就能发现和修复问题,而不是等到系统集成测试甚至上线后才发现,这样可以显著降低修复成本。

等等一系列的原因我们都必须得安排上;

因为咱们主要是开发一个 vue 相关的无头组件库,为了更好的适配,所以咱们的选择就必须是 vitest 了 。

vitest 配置

1. 安装

cspackage/vue

pnpminstallvitest-D

2.新建 vitest.config.ts

import{resolve}from'node:path'
import{defineConfig}from'vitest/config'
importVuefrom'@vitejs/plugin-vue'

constr=(p:string)=>resolve(__dirname,p)

exportdefaultdefineConfig({
plugins:[Vue()],
resolve:{
alias:{
'@':r('./src'),
},
},
})

3. 配置运行脚本:package.json

"script":{
...
"test":"vitest",
...
}

4. 新建一个 src/index.test.ts 文件

import{describe,expect,test,it}from'vitest'
import{add}from'./index'

describe('测试',()=>{
test('函数返回值',()=>{
expect(add(1,2)).toBe(3)
})
})

5.执行 pnpm test

image.png

五、配置 docs 文档

为什么要用到文档

  • 详细的文档能够帮助开发者快速上手,减少试错的时间,提升开发效率。
    • 文档应当包含安装指南、依赖说明、编译和构建步骤,确保任何技术水平的开发者都能顺利将其集成到他们的项目中。
    • 包含示例代码和教程的文档能够直观展示组件库的功能如何实际应用,对于初学者尤为重要。
    • 文档应包含版本更新日志、迁移指南等内容,方便开发者跟踪库的最新变化并适应升级过程。
    • 文档还应包含贡献指南、代码规范、提交PR和issue的流程等,鼓励社区成员参与到开源库的开发与维护工作中来。

    安装配置 vitepress

    新建 docs 目录

    mkdirdocs

    cddocs

    pnpminit

    安装 vitepress & tailwindcss

    pnpmivitepresstailwindcss-D

    配置 package.json

    "scripts":{
    "docs:dev":"vitepressdev",
    "docs:build":"vitepressbuild",
    "docs:preview":"vitepresspreview"
    },

    运行 pnpm docs:dev

    image.png

    到这里其实文档就基本配置好了~

    但是还要与我们的核心库做关联,还要有一些基本config配置和样式等等,这些暂时不表,因为涉及的点太多,待后续完善。

    六、配置playground

    为什么要配置 playground

    一句话概括就是:配置 playground 的主要目的是为了提供给开发者一个交互式的、即时反馈的环境,以更加便捷和直观的方式探索和学习该库的功能。

    新建 vue3 项目

    mkdirplayground

    cdplayground

    初始化 vue3 项目


    pnpmcreatevite

    新建 nuxt 项目


    pnpmcreatevite

    #选择nuxt
    image.png

    引入package/vue核心库

    配置 playground/vue3 和 nuxt 项目的 package.json

    "dependencies":{
    "@yi-ui/vue":"link:../../packages/vue",
    },

    使用 @yi-ui/vue

    import{add}from'@yi-ui/vue'

    add(1,2)

    七、打包构建

    1. 配置

    其实我们在上面配置 package/vue 核心库的时候有添加了一个 build 命令,但是在子项目中 build 不是很方便;

    所以为了统一多包管理,需要在根目录的 package.json 下配置一下

    "scripts":{
    "clear":"rimrafpackages/**/dist",
    "build":"pnpmrunclear&&pnpm-r--filter=./packages/**runbuild",
    },

    另外,我们还需要额外安装一下 rimraf,来删除打包的产物 dist 等目录;

    pnpminstallrimraf-Dw

    rimraf:一个在 Node.js 环境中常用的 npm 包,用于递归删除文件和文件夹。其名称来源于 “rm -rf” 命令,这是在 Unix/Linux 系统中用于递归删除文件和文件夹的命令。

    2.build 打包

    pnpmbuild
    image.png

    八、发布 & 安装使用

    1. 登录 npm(按照提示输入用户名密码邮箱即可)

    npmlogin

    注意:

    • 如果发布的 npm 包名为:@xxx/yyy 格式,需要先在 npm 注册名为:xxx 的 organization,否则会出现提交不成功;
    • 发布到 npm group 时默认为 private,所以我们需要手动在每个 packages 子包中的 package.json 中添加如下配置;"publishConfig": { "access": "public" },

    2. 安装 changesets

    因为我们的项目是一个 monorepo 多包项目,所以我们使用普通的办法显然不能了;

    那么这时候搭配 pnpm workspace 的工具 changesets 就出现了

    1. 安装 changesets

    pnpm i @changesets/cli -Dw

    2. 初始化 changesets

    pnpm changeset init

    3. 完成后项目会出现一个.changeset的文件夹

    |-- my-project
    |-- .changeset
    |-- config.json
    |-- README.md
    |-- ...

    4. 配置 .changeset/config.json

    {
    "$schema":"https://unpkg.com/@changesets/config@3.0.0/schema.json",
    "changelog":"@changesets/cli/changelog",
    "commit":false,
    "fixed":[],
    "linked":[],
    "access":"public",
    "baseBranch":"main",
    "updateInternalDependencies":"patch",
    "ignore":[
    "@yi-ui/playground",
    "@yi-ui/docs"
    ]
    }

    5. 配置 package.json 的发布脚本

    {
    "script": {
    // 1. 开始交互式填写变更集,每次发布版本的时候执行,生成对应的 md 文件
    "changeset": "changeset",
    // 2. 用来统一提升版本号以及对应的md文档
    "vp": "changeset version",
    // 3. 构建产物后发版
    "release": "pnpm build && pnpm release:only",
    "release:only": "changeset publish"
    }
    }

    3.发布

    1. 随便修改 package/vuesrc/index.ts 的代码

    2. 按照顺序执行

  • 至此就会在子包中生成你每次发布版本的 md 文档说明了,如下图:

    image.png

    3. 运行 pnpm release 的最终发布

    image.png

    Tips:

    playgrounddocs 目录下的包需要在 package.json 中设置 "private": true,否则每次 pnpm release 会把队友的包 `publish 至 npm,从而导致 release 失败。

    4.安装使用

    安装:

    pnpminstall@yi-ui/vue

    使用:

    import{add}from'@yi-ui/vue'

    add(1,2)

    总结

    其实上述的流程,只是一个很基本的搭建,还有更详细配置,例如文档、单元测试等等配置其实不止上述这一点,因为要和核心库做深度绑定;

    但是为了不显得文章臃肿,咱们只是一笔带过的间接了解下其最基本的配置。

    当然我们不可能就这样抛弃了,所以接下来的文章,将会实现一个最基本无头组件,以及如何耦合单元测试、文档等等。

    Headless UI 往期相关文章:

    1. 在 2023 年屌爆了一整年的 shadcn/ui 用的 Headless UI 到底是何方神圣?
    2. 实战开始 🚀 在 React 和 Vue3 中使用 Headless UI 无头组件库

    感谢大家的支持,码字实在不易,其中如若有错误,望指出,如果您觉得文章不错,记得 点赞关注加收藏 哦 ~

    关注我,带您一起搞前端 ~