创建vue3组件库

212 阅读19分钟

搜索并参考资料

  1. Vue Amazing UI | Amazing UI Components Library (themusecatcher.github.io) 好看 搜索不能使用
  2. pnpm monorepo+vue3+vite组件库搭建_pnpm 私有组件库-CSDN博客 基本
  3. Vue3+Vite+Element-plus搭建组件库并使用Vitepress编辑组件库文档且发布到 npm并且部署 github pages(vitepress文档渲染.vue组件-推荐使用第二种)_element-plus目录结构-CSDN博客 vue2组件库作者
  4. 如何写一个属于自己的vue3组件库 - 掘金 (juejin.cn)​ 搜索
  5. 手把手从零搭建一个 vue3 组件库 (二):为组件编写文档_组件库文档-CSDN博客 文档可以 搜索不行 存在bug
  6. 从零搭建VUE3组件库 - 掘金 (juejin.cn) 有点复杂
  7. vue3+vite+ts搭建组件库 - 掘金 (juejin.cn) 搜索可以
  8. 记Vue+vite+pnpm UI组件库搭建过程 - 掘金 (juejin.cn)

最终选择 5 3 1

搜索 4 7

经过验证第5个代码存在问题

因此使用第三个

其他资料:

juejin.cn/post/724663…

创建vue3项目

执行命令npm init vue@latest

PS E:\nasGit\demo> npm init vue@latest

Vue.js - The Progressive JavaScript Framework

√ 请输入项目名称: ... v3-elp-ui
√ 是否使用 TypeScript 语法? ... 否 / 是
√ 是否启用 JSX 支持? ... 否 / 是
√ 是否引入 Vue Router 进行单页面应用开发? ... 否 / 是
√ 是否引入 Pinia 用于状态管理? ... 否 / 是
√ 是否引入 Vitest 用于单元测试? ... 否 / 是
√ 是否要引入一款端到端(End to End)测试工具? » 不需要
√ 是否引入 ESLint 用于代码质量检测? ... 否 / 是
√ 是否引入 Prettier 用于代码格式化? ... 否 / 是
√ 是否引入 Vue DevTools 7 扩展用于调试? (试验阶段) ... 否 / 是

正在初始化项目 E:\nasGit\demo\v3-elp-ui...

项目初始化完成,可执行以下命令:

  cd v3-elp-ui
  npm install
  npm run format
  npm run dev

将项目中无用的代码删掉,只保留基本的文件

配置prettier​和eslint

.prettierrc.cjs

//配置参照 https://prettier.io/docs/en/options.html
module.exports = {
  tabWidth: 2, // tab 使用两个空格
  endOfLine: "auto", // 保持现有的行尾
  useTabs: false, // 不使用制表符缩进,使用空格缩进
  semi: true, // 代码需要分号结尾
  quotes: false,
  // 使用单引号而不是双引号,默认false
  singleQuote: false,
  bracketSpacing: true, // 对象左右两侧需要空格
  jsxBracketSameLine: false, // html 关闭标签换行
  jsxSingleQuote: false, //在JSX中使用单引号
  arrowParens: "avoid", // 单参数的箭头函数参数不需要括号
  proseWrap: "never", // markdown文档不换行
  trailingComma: "all", // 结尾处不加逗号
  quoteProps: "as-needed", //自定义引号配置
  $schema: "https://json.schemastore.org/prettierrc",
  printWidth: 100,
};

.eslintrc.cjs

/* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution");

module.exports = {
  root: true,
  extends: [
    "plugin:vue/vue3-essential",
    "eslint:recommended",
    "@vue/eslint-config-typescript",
    "@vue/eslint-config-prettier/skip-formatting",
  ],
  parserOptions: {
    ecmaVersion: "latest",
  },
  rules: {
    // 'no-console': import.meta.env.NODE_ENV === 'production' ? 'warn' : 'off',
    // 'no-debugger': import.meta.env.NODE_ENV === 'production' ? 'warn' : 'off',
    // 强制使用一致的缩进
    indent: ["error", 2],
    // 强制使用一致的换行风格
    // LF
    // "linebreak-style": ["error", "unix"],
    // CRLF
    "linebreak-style": [0, "error", "windows"],
    // 强制使用一致的反勾号、双引号或单引号
    quotes: [
      "error",
      "double", // backtick、double、single
    ],
    // 要求或禁止使用分号代替 ASI
    semi: ["error", "always"],

    // 禁止空格和 tab 的混合缩进
    "no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
    // 强制在 function的左括号之前使用一致的空格
    // 'space-before-function-paren': [2, 'always'],
    // 禁止或强制在单行代码块中使用空格(禁用)
    "block-spacing": [1, "never"],
    "no-cond-assign": 2,
    // 禁止 function 定义中出现重名参数
    "no-dupe-args": 2,
    // 禁止对象字面量中出现重复的 key
    "no-dupe-keys": 2,
    // 禁止重复的 case 标签
    "no-duplicate-case": 2,
    // 禁止空语句块
    "no-empty": 2,
    // 禁止对 catch 子句的参数重新赋值
    "no-ex-assign": 2,
    // 禁止不必要的布尔转换
    "no-extra-boolean-cast": 2,
    // 禁止不必要的括号 //(a * b) + c;//报错
    "no-extra-parens": 0,
    // 强制所有控制语句使用一致的括号风格
    curly: [2, "all"],
    // 禁止 catch 子句的参数与外层作用域中的变量同名
    "no-catch-shadow": 0,
    // 不允许标签与变量同名
    "no-label-var": 2,
    // 禁用特定的全局变量
    "no-restricted-globals": 2,
    // 禁止 var 声明 与外层作用域的变量同名
    "no-shadow": 0,
    // 禁止覆盖受限制的标识符
    "no-shadow-restricted-names": 2,
    // 禁止将变量初始化为 undefined
    "no-undef-init": 2,
    // 禁止将 undefined 作为标识符
    "no-undefined": 0,
    // 不允许在变量定义之前使用它们
    "no-use-before-define": 0,
    // ////////////
    // 风格指南 //
    // ////////////
    // 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
    "array-bracket-spacing": [2, "never"],
    // 强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
    // if while function 后面的{必须与if在同一行,java风格。
    "brace-style": [
      2,
      "1tbs",
      {
        allowSingleLine: true,
      },
    ],
    // 控制逗号前后的空格
    "comma-spacing": [
      2,
      {
        before: false,
        after: true,
      },
    ],
    // 控制逗号在行尾出现还是在行首出现 (默认行尾)
    // http://eslint.org/docs/rules/comma-style
    "comma-style": [2, "last"],
    // "SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平
    // 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
    "computed-property-spacing": [2, "never"],
    // 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
    // e.g [0,"self"] 指定只能 var that = this. self不能指向其他任何值,this也不能赋值给self以外的其他值
    "consistent-this": [2, "self", "that", "_self", "_that", "me", "_this"],
    // 强制使用命名的 function 表达式
    "func-names": 0,
    // 文件末尾强制换行
    "eol-last": 0,
    // 要求或禁止在函数标识符和其调用之间有空格
    "func-call-spacing": 2,
    // 强制在对象字面量的属性中键和值之间使用一致的间距
    "key-spacing": [
      2,
      {
        beforeColon: false,
        afterColon: true,
      },
    ],
    // 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
    "lines-around-comment": [
      0,
      {
        beforeBlockComment: true,
      },
    ],
    "func-style": 0,
    // 强制回调函数最大嵌套深度 5层
    "max-nested-callbacks": [2, 5],
    // 禁止使用指定的标识符
    "id-blacklist": 0,
    // 强制标识符的最新和最大长度
    "id-length": 0,
    // 要求标识符匹配一个指定的正则表达式
    "id-match": 0,
    // 强制在 JSX 属性中一致地使用双引号或单引号
    "jsx-quotes": 0,
    // 强制在关键字前后使用一致的空格 (前后腰需要)
    "keyword-spacing": 2,
    // 强制最大行数
    "max-lines": 0,
    // 强制 function 定义中最多允许的参数数量
    "max-params": [1, 5],
    // 强制 function 块最多允许的的语句数量
    "max-statements": [1, 200],
    // 强制每一行中所允许的最大语句数量
    "max-statements-per-line": 0,
    // 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。)
    "new-cap": [
      2,
      {
        newIsCap: true,
        capIsNew: false,
      },
    ],
    // 要求调用无参构造函数时有圆括号
    "new-parens": 2,
    // 要求或禁止 var 声明语句后有一行空行
    "newline-after-var": 0,
    // 禁止使用 Array 构造函数
    "no-array-constructor": 2,
    // 禁用按位运算符
    "no-bitwise": 0,
    // 要求 return 语句之前有一空行
    "newline-before-return": 0,
    // 要求方法链中每个调用都有一个换行符
    "newline-per-chained-call": 1,
    // 禁用 continue 语句
    "no-continue": 0,
    // 禁止在代码行后使用内联注释
    "no-inline-comments": 0,
    // 禁止 if 作为唯一的语句出现在 else 语句中
    "no-lonely-if": 0,
    // 禁止混合使用不同的操作符
    "no-mixed-operators": 0,
    // 不允许多个空行
    "no-multiple-empty-lines": [
      2,
      {
        max: 2,
      },
    ],
    // 不允许否定的表达式
    "no-negated-condition": 0,
    // 不允许使用嵌套的三元表达式
    "no-nested-ternary": 0,
    // 禁止使用 Object 的构造函数
    "no-new-object": 2,
    // 禁止使用一元操作符 ++ 和 --
    "no-plusplus": 0,
    // 禁止使用特定的语法
    "no-restricted-syntax": 0,
    // 禁止 function 标识符和括号之间出现空格
    "no-spaced-func": 2,
    // 不允许使用三元操作符
    "no-ternary": 0,
    // 禁用行尾空格
    "no-trailing-spaces": 2,
    // 禁止标识符中有悬空下划线_bar
    "no-underscore-dangle": 0,
    // 禁止可以在有更简单的可替代的表达式时使用三元操作符
    "no-unneeded-ternary": 2,
    // 禁止属性前有空白
    "no-whitespace-before-property": 2,
    // 要求或禁止在 var 声明周围换行
    "one-var-declaration-per-line": 0,
    // 要求或禁止在可能的情况下要求使用简化的赋值操作符
    "operator-assignment": 0,
    // 强制操作符使用一致的换行符
    "operator-linebreak": [
      2,
      "after",
      {
        overrides: {
          "?": "before",
          ":": "before",
        },
      },
    ],
    // 要求或禁止块内填充
    "padded-blocks": 0,
    // 要求对象字面量属性名称用引号括起来
    "quote-props": 0,
    // 要求使用 JSDoc 注释
    "require-jsdoc": 0,
    // 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
    // "semi": [2, "always"],
    // 强制分号之前和之后使用一致的空格
    "semi-spacing": 2,
    // 要求同一个声明块中的变量按顺序排列
    "sort-vars": 0,
    // 强制在块之前使用一致的空格
    "space-before-blocks": [2, "always"],
    // 强制在圆括号内使用一致的空格
    "space-in-parens": [2, "never"],
    // 要求操作符周围有空格
    "space-infix-ops": 2,
    // 强制在一元操作符前后使用一致的空格
    "space-unary-ops": [
      2,
      {
        words: true,
        nonwords: false,
      },
    ],
    // 强制在注释中 // 或 /* 使用一致的空格
    "spaced-comment": [
      2,
      "always",
      {
        markers: ["global", "globals", "eslint", "eslint-disable", "*package", "!"],
      },
    ],
    // 要求或禁止 Unicode BOM
    "unicode-bom": 2,
    // 要求正则表达式被括号括起来
    "wrap-regex": 0,
    // 禁止词法声明 (let、const、function 和 class) 出现在 case或default 子句中
    "no-case-declarations": ["warn"],
    // ////////////
    // ES6.相关 //
    // ////////////
    // 每个模块只能使用一个import
    "no-duplicate-imports": 2,
    // 要求箭头函数体使用大括号
    "arrow-body-style": 2,
    // 要求箭头函数的参数使用圆括号
    "arrow-parens": 0,
    "arrow-spacing": [
      2,
      {
        before: true,
        after: true,
      },
    ],
    // 强制 generator 函数中 * 号周围使用一致的空格
    "generator-star-spacing": [
      2,
      {
        before: true,
        after: true,
      },
    ],
    // 禁止修改类声明的变量
    "no-class-assign": 2,
    // 不允许箭头功能,在那里他们可以混淆的比较
    "no-confusing-arrow": 0,
    // 禁止修改 const 声明的变量
    "no-const-assign": 2,
    // 禁止类成员中出现重复的名称
    "no-dupe-class-members": 2,
    // 禁止 Symbolnew 操作符和 new 一起使用
    "no-new-symbol": 2,
    // 允许指定模块加载时的进口
    "no-restricted-imports": 0,
    // 禁止在构造函数中,在调用 super() 之前使用 this 或 super
    "no-this-before-super": 2,
    // 禁止不必要的计算性能键对象的文字
    "no-useless-computed-key": 0,
    // 要求使用 let 或 const 而不是 var
    "no-var": 1,
    // 要求或禁止对象字面量中方法和属性使用简写语法
    "object-shorthand": 0,
    // 要求使用箭头函数作为回调
    "prefer-arrow-callback": 0,
    // 要求使用 const 声明那些声明后不再被修改的变量
    "prefer-const": 0,
    // 要求在合适的地方使用 Reflect 方法
    "prefer-reflect": 0,
    // 要求使用扩展运算符而非 .apply()
    "prefer-spread": 0,
    // 要求使用模板字面量而非字符串连接
    "prefer-template": 0,
    // Suggest using the rest parameters instead of arguments
    "prefer-rest-params": 0,
    // 要求generator 函数内有 yield
    "require-yield": 2,
    // 要求或禁止模板字符串中的嵌入表达式周围空格的使用
    "template-curly-spacing": 1,
    // 强制在 yield* 表达式中 * 周围使用空格
    "yield-star-spacing": 2,
    // 要求或禁止使用拖尾逗号
    "comma-dangle": 0,
    // 禁止在条件中使用常量表达式
    "no-constant-condition": 0,
    // 强制一行的最大长度
    "max-len": [
      0,
      200,
      {
        ignoreUrls: true,
      },
    ],
    // 禁止出现未使用过的变量
    "no-unused-vars": [
      0,
      {
        vars: "all", // all 检测所有变量,包括全局环境中的变量。这是默认值。
        args: "none", // none - 不检查参数。
      },
    ],
    // 去除插槽报错
    "vue/no-deprecated-slot-attribute": "off",
    // 关掉命名规范
    "vue/multi-word-component-names": "off",
  },
};

创建组件

在项目根目录下面创建packages​用来存放组件,在packages​下面创建每个组件的文件夹

组件目录下面创建index.ts​、src文件夹​,在src​文件夹下面创建index.vue

  • src​用来存放组件
  • index.vue​是组件内容
  • index.ts​用来导出组件

packages​下面创建index.ts​用来导出全部组件或按需引入,创建withInstall.ts​用来注册组件,创建components.d.ts​支持Volar

withInstall.ts

import { App, Plugin } from 'vue'

type SFCWithInstall<T> = T & Plugin

export const withInstall = <T, E extends Record<string, any>>(
  main: T,
  extra?: E
) => {
  ;(main as SFCWithInstall<T>).install = (app: App) => {
    for (const comp of [main, ...Object.values(extra ?? {})]) {
      app.component(comp.name, comp)
    }
  }
  if (extra) {
    for (const [compName, comp] of Object.entries(extra)) {
      ;(main as Record<string, any>)[compName] = comp
    }
  }
  // 将 T 断言为具体的类型 T & plugin & Record<string, any>
  return main as SFCWithInstall<T> & E
}

index.ts

// 公共方法
/* import {
  debounce,
  throttle,
  formatNumber
} from './utils' */
import type { Component, App } from "vue";
import Btn from "./button";
import labelInput from "./labelInput";

// 存储组件列表
const components: {
  [propName: string]: Component;
} = {
  Btn,
  labelInput,
};
// 插件声明:声明所有插件
// 插件注册:在 Vue 项目的入口文件中,通过 ( app.use(插件) ) 进行注册
const installComponents: any = (app: App) => {
  // components.forEach((comp: any) => {
  //   app.component(comp.name as string, comp)
  // })
  //   app.use(ElementPlus, {
  //     locale // 语言设置
  //   // size: Cookies.get('size') || 'medium' // 设置默认尺寸
  // })
  for (const key in components) {
    app.component(key, components[key]);
  }
};
// vue插件
// - install:每个插件都有一个 install 方法
// - 参数:是通过 Vue.createApp() 创建的 app 实例
const install: any = (app: any, router?: any) => {
  // !router && installRouter(app);
  installComponents(app);
};

// 按需引入
export { Btn, labelInput };
/**
 * @description 公共方法
 */
/* export {
  throttle,
  debounce,
  formatNumber
} */

export default {
  // 导出的对象必须具有 install,才能被 Vue.use() 方法安装
  install,
};

安装npm i vite-plugin-dts -D​,作用是为打包的库里加入声明文件(即生成:.d.ts文件)

修改文件如下

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "noImplicitAny": false, // 隐式具有“any”类型
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "jsx": "preserve",
    "types": ["vite/client"],
    // 如果编译器无法根据变量的使用来判断类型时,将用 any 类型代替
    // "suppressImplicitAnyIndexErrors": true,
    // 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "lib": ["ESNext", "DOM"],
    // 解析非相对模块名的基准目录
    "baseUrl": "./",
    // 模块名到基于 baseUrl的路径映射的列表。
    "paths": {
      "@": ["packages"],
      "@vue/shared": ["./node_modules/@vue/shared"],
      "@/*": ["packages/*"]
    },
    "skipLibCheck": true
  },
  "include": ["packages/**/*.ts", "packages/**/*.d.ts", "packages/**/*.tsx", "packages/**/*.vue"],
  "exclude": ["node_modules"],
  "references": [
    {
      "path": "./tsconfig.node.json"
    }
  ]
}

tsconfig.node.json

{
  "extends": "@tsconfig/node20/tsconfig.json",
  "include": [
    "vite.config.*",
    "vitest.config.*",
    "cypress.config.*",
    "nightwatch.conf.*",
    "playwright.config.*"
  ],
  "compilerOptions": {
    "composite": true,
    "noEmit": true,
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",

    "module": "ESNext",
    "moduleResolution": "Bundler",
    "types": ["node"]
  }
}

vite.config.ts

import { fileURLToPath, URL } from "node:url";

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import dts from "vite-plugin-dts";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(), dts(), vueJsx()],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
  build: {
    outDir: "lib",
    rollupOptions: {
      // 请确保外部化那些你的库中不需要的依赖
      external: ["vue"],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: "Vue",
        },
      },
    },
    lib: {
      entry: "./packages/index.ts",
      name: "v3-elp-ui",
    },
  },
});

package.json

{
  "name": "v3-elp-ui",
  "version": "0.0.5",
  "type": "module",
  "private": false,
  "description": "描述",
  "author": "作者",
  "main": "lib/v3-elp-ui.umd.cjs",
  "module": "lib/v3-elp-ui.umd.cjs",
  "types": "lib/index.d.ts",
  "files": [
    "package.json",
    "README.md",
    "lib"
  ],
  "scripts": {
    "dev": "vite",
    "lib": " vite build",
    "build": "run-p type-check \"build-only {@}\" --",
    "preview": "vite preview",
    "build-only": "vite build",
    "type-check": "vue-tsc --build --force",
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
    "format": "prettier --write src/"
  },
  "dependencies": {
    "element-plus": "^2.7.5",
    "less": "^4.2.0",
    "vue": "^3.4.21"
  },
  "devDependencies": {
    "@rushstack/eslint-patch": "^1.8.0",
    "@tsconfig/node20": "^20.1.4",
    "@types/node": "^20.12.5",
    "@vitejs/plugin-vue": "^5.0.4",
    "@vitejs/plugin-vue-jsx": "^3.1.0",
    "@vue/eslint-config-prettier": "^9.0.0",
    "@vue/eslint-config-typescript": "^13.0.0",
    "@vue/tsconfig": "^0.5.1",
    "eslint": "^8.57.0",
    "eslint-plugin-vue": "^9.23.0",
    "npm-run-all2": "^6.1.2",
    "prettier": "^3.2.5",
    "typescript": "~5.4.0",
    "vite": "^5.2.8",
    "vite-plugin-dts": "^3.9.1",
    "vue-tsc": "^2.0.11"
  }
}

经过以上操作能够正常打包,命令npm run lib​,在项目中验证正常,但是需要项目全局安装element plus

支持全量引入和按需引入

需要再main.ts​中引入css​文件

import 'v3-elp-ui/lib/style.css'

组件设置name属性

在编写组件的时候需要将name​单独添加到一个script​中,使用不方便,安装vite-plugin-vue-setup-extend​可以直接在script​上添加name

npm i vite-plugin-vue-setup-extend -D

vite.config.ts​中引入

import vueSetupExtend from 'vite-plugin-vue-setup-extend' // 设置neme属性

plugins: [vue(), dts(), vueJsx(), vueSetupExtend()],

集成vitepress及完善其配置

参考资料:

blog.csdn.net/cwin8951/ar…

juejin.cn/post/724663…

  1. 安装vitepress(也看用yarn、npm)

    pnpm add vitepress -D
    
  2. 在根目录创建文件夹docs

  3. 创建文件夹及文件,结构如下

    docs
     ├── .vitepress
     │   ├── config
     │   │   ├── global.ts
     │   │   └── plugins.ts
     │   ├── config.ts
     │   ├── theme
     │   │   ├── index.ts
     │   │   └── useComponents.js
     │   ├── utils
     │   │   └── highlight.ts
     │   └── vitepress
     │       ├── components
     │       │   └── vp-demo
     │       ├── index.ts
     │       └── style
     │           ├── code.css
     │           ├── css-vars.scss
     │           ├── index.scss
     │           ├── mixins.scss
     │           └── vars.scss
     ├── components
     │   ├── button
     │   │   └── base.md
     │   └── index.md
     ├── demos
     │   └── button
     │       └── index.vue
     ├── index.md
     └── public
         ├── css
         │   └── index.css
         ├── favicon.ico
         └── img
             └── vue.jpg
    
    
    
  4. 添加 scripts

    "scripts": {
      "docs:dev": "vitepress dev docs",
      "docs:build": "vitepress build docs"
    },
    
    
  5. 首页配置(即:docs/index.md文件)如下修改:

    ---
    layout: home
    
    title: vue3 + element plus 组件库
    # titleTemplate: 选项卡描述
    editLink: true
    lastUpdated: true
    hero:
      name: v3-elp-ui
      text: vue3基础组件
      tagline: Vue3 中基于Element-plus二次封装基础组件文档
      image:
        src: /img/vue.jpg
        alt: v3-elp-ui
      actions:
        - theme: brand
          text: 安装指南
          link: /components/
        - theme: brand
          text: 组件预览
          link: /components/button/base.md
    features:
      - icon: 🔨
        title: 实际项目
        details: 实际项目中碰到的疑点、难点,致力于更优的自我。。
      - icon: 🧩
        title: 基础组件
        details: 基于Element-plus二次封装;使用组件 Demo 快速体验交互细节。。
      - icon: ✈️
        title: Vue驱动。
        details: 享受 Vue3 + vite3 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
    ---
    
    <p style="display: flex;
        justify-content: center;
        align-items: center;
        margin-top: 10px;">
      <a href="https://github.com/vuejs/vue" target="_blank">
        <img src="https://img.shields.io/badge/vue-3.2.36-brightgreen.svg" alt="vue3">
      </a>
       
       
    </p>
    
    
  6. 配置组件

    demos​目录下创建组件文件夹button​,里面在创建对应的vue​文件

    <template>
      <!-- 组件库中的包 -->
      <Btn />
    </template>
    

    components​文件夹中创建组件说明文档文件夹button​,里面在创建base.md

    # 按钮组件
    
    :::demo 
    button/index
    :::
    

    components​目录下创建index.md

    # 安装&使用
    
    ::: tip 提示
    
    v3ElpUi 是基于 vue3 + Element-plus 二次封装的基础组件
    
    :::
    
    ## 安装```js
    npm install v3-elp-ui
    &
    yarn add v3-elp-ui -S
    ‍```
    
    ## 使用```js
    // main.js
    import v3ElpUi from "v3-elp-ui";
    
    Vue.use(v3ElpUi);
    ‍```
    
    
    ## 说明
    
    需要项目全局安装`elemen-plus`
    
  7. 接下来开始配置vitepress​,主要设置docs\.vitepress​目录下的文件,代码较多就不写了

    .vitepress
     ├── config
     │   ├── global.ts # 设置文档库路径
     │   └── plugins.ts # 用于扩展Markdown解析器markdown-it的功能,以便在Markdown文档中支持一种特殊的自定义容器(以demo标记),该容器可以用来展示代码示例及其描述。
     ├── config.ts # vitepress配置文件  设置导航和侧边栏
     ├── theme
     │   ├── index.ts # 安装注册组件  注册了element-plus he ziji de zuianku 
     │   └── useComponents.js # 注册自定义组件
     ├── utils
     │   └── highlight.ts # 实现代码高亮
     └── vitepress
         ├── components
         │   └── vp-demo # 自定义组件 用来展示demo
         │       ├── index.vue
         │       ├── vp-example.vue # 注意 25行 的 demo表示组件demo文件夹
         │       └── vp-source-code.vue
         ├── index.ts # 导出组件
         └── style  #代码样式
             ├── code.css
             ├── css-vars.scss
             ├── index.scss
             ├── mixins.scss
             └── vars.scss
    
    • docs\.vitepress\config\plugins.ts

      1. 导入依赖:

        • 引入了Node.js的path​和fs​模块来处理文件路径和读取文件内容。
        • 引入了TypeScript类型忽略注释@ts-ignore​来规避Markdown解析器和容器插件的类型检查问题,因为这些包可能缺少直接的TypeScript声明文件。
        • 导入了markdown-it​库来解析Markdown文本,并且使用了markdown-it-container​插件以支持自定义的容器块。
        • 从本地highlight​函数和docRoot​变量导入,用于代码高亮和获取文档根目录路径。
      2. 定义类型:

        • ContainerOpts​接口定义了传递给mdContainer​插件的选项结构,包括验证函数、渲染函数和可选的标记。
      3. 创建Markdown解析实例:

        • 创建了一个MarkdownIt​的实例localMd​,用于渲染描述文本。
      4. 定义mdPlugin函数:

        • 此函数接收一个MarkdownIt​实例作为参数,用于扩展Markdown解析功能。

        • 使用mdContainer​插件定义名为"demo"的容器,该容器包含两部分逻辑:

          • validate​函数检查代码块是否以"demo"开头,并可选地捕获后续描述文本。
          • render​函数定义了如何渲染这种特殊类型的代码块。它读取指定的Vue组件文件,对组件源码进行高亮处理,然后构造一个<Demo>​标签来包裹这些信息,包括组件源代码、文件路径、未经处理的源码以及可选的描述。
      5. 用途示例:

        • 在Markdown文档中,用户可以通过以下方式插入组件示例:
           ::: demo 示例名称
               src/components/Button.vue
           :::
      
      • 解析器会将上述Markdown转换成HTML,其中包含一个<Demo>​元素,属性包括组件的源代码(经过高亮处理)、原始文件路径、未编码的源代码以及描述(如果有的话)。

      此代码片段极大地增强了Markdown文档的交互性和开发者的文档编写体验,特别是在需要频繁展示和文档化代码组件的项目中。

      注意第34行​的demos​代表组件demo​文件夹

    • docs\.vitepress\utils\highlight.ts

      主要实现了代码高亮功能,使用了prismjs​库来对特定语言的代码进行语法高亮处理。以下是代码的关键部分和功能解释:

      1. 导入依赖

        • chalk​:用于在控制台输出带有颜色的文本。
        • escapeHtml​:用于转义HTML特殊字符,防止XSS攻击。
        • prism​:代码高亮库,支持多种编程语言的语法高亮。
        • consola​:一个美观的日志记录库,常用于Node.js应用中。
        • loadLanguages​:动态加载prismjs​的语言包,确保需要的语言支持已加载。
      2. 加载语言包

        • 使用loadLanguages(["markup", "css", "javascript"])​预先加载了一些常用的语言包,确保基本的代码高亮功能可用。
      3. wrap函数

        • 接受一段代码和语言类型,如果语言类型是"text",则转义HTML特殊字符后包裹在<pre><code>...</code></pre>​标签内返回。否则,直接返回原始代码包裹在相同的标签内。
      4. highlight函数

        • 是主要的代码高亮处理函数,接受要高亮的字符串和语言类型作为参数。
        • 首先,如果没有指定语言类型,则默认按照纯文本处理。
        • 然后,根据语言类型调整为prismjs​支持的标准语言标识符(如将vue​调整为markup​)。
        • 接着,尝试加载对应语言的高亮规则,如果规则不存在,则尝试动态加载,并在加载失败时通过consola.warn​输出警告信息。
        • 最后,如果成功加载了语言规则,使用prism.highlight​方法对代码进行高亮处理,并返回包裹好的HTML代码;否则,按纯文本处理返回。

      综上所述,这段代码提供了一个灵活且健壮的代码高亮工具,能够适应多种编程语言,并且在遇到不支持的语言时提供了友好的警告提示。

  8. 最后安装依赖

    # highlight.ts
    npm i chalk escape-html prismjs consola -D
    # plugins.ts
    npm i markdown-it markdown-it-container -D
    # demo文件
    npm i @vueuse/core
    

说明文档添加搜索

查看代码没有找到4、7​参考链接的搜索设置

经过上网搜索发现VitePress支持使用浏览器内置索引进行模糊全文搜索,查看4、7​参考链接的确具有相同的设置

.vitepress/config.ts​文件中将themeConfig.search.provider​选项设置为local​即可

结束

最后就是对组件库进行打包上传并部署组件库文档了

仓库地址:banmag.cn:6080/npmbag/v3-e…