«

vue组件库如何开发使用

时间:2024-4-3 09:16     作者:韩俊     分类: Javascript


本文小编为大家详细介绍“vue组件库如何开发使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue组件库如何开发使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

yarn workspace + lerna: 管理组件库及其生态项目

考虑到组件库整体需要有多边资源支持,比如组件源码,库文档站点,color-gen等类库工具,代码规范配置,vite插件,脚手架,storybook等等,需要分出很多packages,package之间存在彼此联系,因此考虑使用monorepo的管理方式,同时使用yarn作为包管理工具,lerna作为包发布工具。

在monorepo之前,根目录就是一个workspace,我们直接通过yarn add/remove/run等就可以对包进行管理。但在monorepo项目中,根目录下存在多个子包,yarn 命令无法直接操作子包,比如根目录下无法通过yarn run dev启动子包package-a中的dev命令,这时我们就需要开启yarn的workspaces功能,每个子包对应一个workspace,之后我们就可以通过

yarn workspace package-a run dev

启动package-a中的dev命令了。


你可能会想,我们直接cd到package-a下运行就可以了,不错,但yarn workspaces的用武之地并不只此,像auto link,依赖提升,单.lock等才是它在monorepo中的价值所在。


启用yarn workspaces

我们在根目录packge.json中启用yarn workspaces:


{
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}


packages目录下的每个直接子目录作为一个workspace。由于我们的根项目是不需要发布出去的,因此设置private为true。

安装lerna并初始化

不得不说,yarn workspaces已经具备了lerna部分功能,之所以使用它,是想借用它的发布工作流以弥补workspaces在monorepo下在这方面的不足。下面我们开始将lerna集成到项目中。

首先我们先安装一下lerna:


# W指workspace-root,即在项目根目录下安装,下同
yarn add lerna -D -W
# 由于经常使用lerna命令也推荐全局安装
yarn global add lerna
or
npm i lerna -g


执行

lerna init

初始化项目,成功之后会帮我们创建了一个

lerna.json

文件



lerna init
// lerna.json
{
  "$schema": "node_modules/lerna/schemas/lerna-schema.json",
  "useWorkspaces": true,
  "version": "0.0.0"
}



  • $schema
    指向的lerna-schema.json描述了如何配置lerna.json,配置此字段后,鼠标悬浮在属性上会有对应的描述。注意,以上的路径值需要你在项目根目录下安装lerna。



  • useWorkspaces
    定义了在
    lerna bootstrap
    期间是否结合yarn workspace。


  • 由于lerna默认的工作模式是固定模式,即发布时每个包的版本号一致。这里我们修改为

    independent
    独立模式,同时将npm客户端设置为
    yarn
    。如果你喜欢
    pnpm
    ,just do it!



// lerna.json
{
  "version": "independent",
  "npmClient": "yarn"
}


至此

yarn workspaces

搭配

lerna

的monorepo项目就配置好了,非常简单!


额外的lerna配置

By the way!由于项目会使用

commitlint

对提交信息进行校验是否符合Argular规范,而

lerna version

默认为我们commit的信息是"Publish",因此我们需要进行一些额外的配置。



// lerna.json
{
  "command": {
    "version": {
      "message": "chore(release): publish",
      "conventionalCommits": true
    }
  }
}


可以看到,我们使用符合Argular团队提交规范的

"chore(release): publish"

代替默认的"Publish"。



conventionalCommits

表示当我们运行

lerna version

,实际上会运行

lerna version --conventional-commits

帮助我们生成CHANGELOG.md。


小结

在lerna刚发布的时候,那时的包管理工具还没有可用的

workspaces

解决方案,因此lerna自身实现了一套解决方案。时至今日,现代的包管理工具几乎都内置了

workspaces

功能,这使得lerna和yarn有许多功能重叠,比如执行包pkg-a的dev命令

lerna run dev --stream --scope=pkg-a

,我们完全可以使用

yarn workspace pkg-a run dev

代替。lerna bootstrap --hoist将安装包提升到根目录,而在yarn workspaces中直接运行yarn就可以了。


Anyway, 使用

yarn

作为软件包管理工具,

lerna

作为软件包发布工具,是在

monorepo

管理方式下一个不错的实践!


集成Lint工具规范化代码

很无奈,我知道大部分人都不喜欢Lint,但对我而言,这是必须的。


集成eslint

packages目录下创建名为@argo-design/eslint-config(非文件夹名)的package

1. 安装eslint


cd argo-eslint-config
yarn add eslint
npx eslint --init


注意这里没有-D或者--save-dev。选择如下:

安装完成后手动将

devDependencies

下的依赖拷贝到

dependencies

中。或者你手动安装这一系列依赖。


2. 使用


// argo-eslint-config/package.json
{
  scripts: {
    "lint:script": "npx eslint --ext .js,.jsx,.ts,.tsx --fix --quiet ./"
  }
}


运行

yarn lint:script

,将会自动修复代码规范错误警告(如果可以的话)。


3. VSCode保存时自动修复

安装VSCode Eslint插件并进行如下配置,此时在你保存代码时,也会自动修复代码规范错误警告。


// settings.json
{
  "editor.defaultFormatter": "dbaeumer.vscode-eslint",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
}


4. 集成到项目全局

argo-eslint-config

中新建包入口文件index.js,并将.eslintrc.js的内容拷贝到index.js中



module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true
  },
  extends: ['plugin:vue/vue3-essential', 'standard-with-typescript'],
  overrides: [],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module'
  },
  plugins: ['vue'],
  rules: {}
}


确保package.json配置

main

指向我们刚刚创建的index.js。



// argo-eslint-config/package.json
{
   "main": "index.js"
}


根目录package.json新增如下配置


// argo-eslint-config/package.json
{
  "devDependencies": {
    "@argo-design/eslint-config": "^1.0.0"
  },
  "eslintConfig": {
    "root": true,
    "extends": [
      "@argo-design"
    ]
  }
}


最后运行yarn重新安装依赖。

注意包命名与extends书写规则;root表示根配置,对eslint配置文件冒泡查找到此为止。

集成prettier

接下来我们引入formatter工具

prettier

。首先我们需要关闭eslint规则中那些与prettier冲突或者不必要的规则,最后由

prettier

代为实现这些规则。前者我们通过

eslint-config-prettier

实现,后者借助插件

eslint-plugin-prettier

实现。比如冲突规则尾逗号,

eslint-config-prettier

帮我们屏蔽了与之冲突的eslint规则:



{
  "comma-dangle": "off",
  "no-comma-dangle": "off",
  "@typescript-eslint/comma-dangle": "off",
  "vue/comma-dangle": "off",
}


通过配置eslint规则

"prettier/prettier": "error"

让错误暴露出来,这些错误交给

eslint-plugin-prettier

收拾。


prettier配置我们也新建一个package

@argo-design/prettier-config


1. 安装


cd argo-prettier-config
yarn add prettier
yarn add eslint-config-prettier eslint-plugin-prettier


2. 使用


// argo-prettier-config/index.js
module.exports = {
  printWidth: 80, //一行的字符数,如果超过会进行换行,默认为80
  semi: false, // 行尾是否使用分号,默认为true
  trailingComma: 'none', // 是否使用尾逗号
  bracketSpacing: true // 对象大括号直接是否有空格
};


完整配置参考官网 prettier配置

3. 配置eslint

回到argo-eslint-config/index.js,只需新增如下一条配置即可


module.exports = {
   "extends": ["plugin:prettier/recommended"]
};



plugin:prettier/recommended

指的

eslint-plugin-prettier

package下的recommended.js。该扩展已经帮我们配置好了



{
  "extends": ["eslint-config-prettier"],
  "plugins": ["eslint-plugin-prettier"],
  "rules": {
    "prettier/prettier": "error",
    "arrow-body-style": "off",
    "prefer-arrow-callback": "off"
  }
}


4. 集成到项目全局

根目录package.json新增如下配置


{
  "devDependencies": {
    "@argo-design/prettier-config": "^1.0.0"
  },
  "prettier": "@argo-design/prettier-config"
}


运行yarn重新安装依赖。

5. VSCode安装prettier扩展并将其设置成默认格式化工具


// settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode"
}


集成stylelint

stylelint配置我们也新建一个package

@argo-design/stylelint-config


1. 安装


cd argo-stylelint-config
yarn add stylelint stylelint-prettier stylelint-config-prettier stylelint-order stylelint-config-rational-order postcss-html postcss-less
# 单独postcss8
yarn add postcss@^8.0.0


对于结合

prettier

这里不在赘述。



stylelint-order

允许我们自定义样式属性名称顺序。而

stylelint-config-rational-order

为我们提供了一套合理的开箱即用的顺序。


值得注意的是,stylelint14版本不在默认支持less,sass等预处理语言。并且stylelint14依赖postcss8版本,可能需要单独安装,否则vscode 的stylellint扩展可能提示报错TypeError: this.getPosition is not a function at LessParser.inlineComment....

2. 使用


// argo-stylelint-config/index.js
module.exports = {
  plugins: [
    "stylelint-prettier",
  ],
  extends: [
    // "stylelint-config-standard",
    "stylelint-config-standard-vue", 
    "stylelint-config-rational-order",
    "stylelint-prettier/recommended"
  ],
  rules: {
    "length-zero-no-unit": true, // 值为0不需要单位
    "plugin/rational-order": [
      true,
      {
        "border-in-box-model": true, // Border理应作为盒子模型的一部分 默认false
        "empty-line-between-groups": false // 组之间添加空行 默认false
      }
    ]
  },
  overrides: [
    {
      files: ["*.html", "**/*.html"],
      customSyntax: "postcss-html"
    },
    {
      files: ["**/*.{less,css}"],
      customSyntax: "postcss-less"
    }
  ]
};


3. 集成到项目全局

根目录package.json新增如下配置


{
  "devDependencies": {
    "@argo-design/stylelint-config": "^1.0.0"
  },
  "stylelint": {
    "extends": [
      "@argo-design/stylelint-config"
    ]
  }
}


运行yarn重新安装依赖。

4. VSCode保存时自动修复

VSCode安装Stylelint扩展并添加配置


// settings.json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
    "source.fixAll.stylelint": true
  },
  "stylelint.validate": ["css", "less", "vue", "html"],
  "css.validate": false,
  "less.validate": false
}


修改settings.json之后如不能及时生效,可以重启一下vscode。如果你喜欢,可以将eslint,prettier,stylelint配置安装到全局并集成到编辑器。

集成husky

为防止一些非法的

commit

push

,我们借助

git hooks

工具在对代码提交前进行 ESLint 与 Stylelint的校验,如果校验通过,则成功commit,否则取消commit。


1. 安装


# 在根目录安装husky
yarn add husky -D -W


2. 使用


npm pkg set scripts.prepare="husky install"
npm run prepare
# 添加pre-commit钩子,在提交前运行代码lint
npx husky add .husky/pre-commit "yarn lint"


至此,当我们执行

git commit -m "xxx"

时就会先执行lint校验我们的代码,如果lint通过,成功commit,否则终止commit。具体的lint命令请自行添加。


集成lint-staged: 仅校验staged中文件

现在,当我们git commit时,会对整个工作区的代码进行lint。当工作区文件过多,lint的速度就会变慢,进而影响开发体验。实际上我们只需要对暂存区中的文件进行lint即可。下面我们引入

·lint-staged

解决我们的问题。


1. 安装

在根目录安装

lint-staged



yarn add lint-staged -D -W


2. 使用

在根目录

package.json

中添加如下的配置:



{
  "lint-staged": {
    "*.{js,ts,jsx,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{less,css}": [
      "stylelint --fix",
      "prettier --write"
    ],
    "**/*.vue": [
      "eslint --fix",
      "stylelint --fix",
      "prettier --write"
    ]
  }
}


在monorepo中,

lint-staged

运行时,将始终向上查找并应用最接近暂存文件的配置,因此我们可以在根目录下的package.json中配置lint-staged。值得注意的是,每个glob匹配的数组中的命令是从左至右依次运行,和webpack的loder应用机制不同!


最后,我们在.husky文件夹中找到

pre-commit

,并将

yarn lint

修改为

npx --no-install lint-staged



#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no-install lint-staged


至此,当我们执行

git commit -m "xxx"

时,

lint-staged

会如期运行帮我们校验staged(暂存区)中的代码,避免了对工作区的全量检查。


集成commitlint: 规范化commit message

除了代码规范检查之后,Git 提交信息的规范也是不容忽视的一个环节,规范精准的 commit 信息能够方便自己和他人追踪项目和把控进度。这里,我们使用大名鼎鼎的

Angular团队提交规范


commit message格式规范

commit message 由

Header

Body

Footer

组成。其中Herder时必需的,Body和Footer可选。


Header

Header 部分包括三个字段

type

scope

subject



<type>(<scope>): <subject>


type

其中type 用于说明 commit 的提交类型(必须是以下几种之一)。

描述
feat Feature) 新增一个功能
fix Bug修复
docs Documentation) 文档相关
style 代码格式(不影响功能,例如空格、分号等格式修正),并非css样式更改
refactor 代码重构
perf Performent) 性能优化
test 测试相关
build 构建相关(例如 scopes: webpack、gulp、npm 等)
ci 更改持续集成软件的配置文件和 package 中的 scripts 命令,例如 scopes: Travis, Circle 等
chore 变更构建流程或辅助工具,日常事务
revert git revert

scope

scope 用于指定本次 commit 影响的范围。

subject

subject 是本次 commit 的简洁描述,通常遵循以下几个规范:

  • 用动词开头,第一人称现在时表述,例如:change 代替 changed 或 changes

  • 第一个字母小写

  • 结尾不加句号

    .


Body(可选)

body 是对本次 commit 的详细描述,可以分成多行。跟 subject 类似。

Footer(可选)

如果本次提交的代码是突破性的变更或关闭Issue,则 Footer 必需,否则可以省略。

集成commitizen(可选)

我们可以借助工具帮我们生成规范的message。

1. 安装


yarn add commitizen -D -W


2. 使用

安装适配器


yarn add cz-conventional-changelog -D -W


这行命令做了两件事:

  • 安装

    cz-conventional-changelog
    到开发依赖


  • 在根目录下的package.json中增加了:


"config": {
  "commitizen": {
    "path": "./node_modules/cz-conventional-changelog"
  }
}


添加npm scripts

cm



"scripts": {
  "cm": "cz"
},


至此,执行

yarn cm

,就能看到交互界面了!跟着交互一步步操作就能自动生成规范的message了。


集成commitlint: 对最终提交的message进行校验

1. 安装

首先在根目录安装依赖:


yarn add commitlint @commitlint/cli @commitlint/config-conventional -D -W


2. 使用

接着新建

.commitlintrc.js

:



module.exports = {
  extends: ["@commitlint/config-conventional"]
};


最后向husky中添加

commit-msg

钩子,终端执行:



npx husky add .husky/commit-msg "npx --no-install commitlint -e $HUSKY_GIT_PARAMS"


执行成功之后就会在.husky文件夹中看到commit-msg文件了:


#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no-install commitlint -e


至此,当你提交代码时,如果

pre-commit

钩子运行成功,紧接着在

commit-msg

钩子中,commitlint会如期运行对我们提交的message进行校验。


关于lint工具的集成到此就告一段落了,在实际开发中,我们还会对lint配置进行一些小改动,比如ignore,相关rules等等。这些和具体项目有关,我们不会变更package里的配置。

千万别投机取巧拷贝别人的配置文件!复制一时爽,代码火葬场。

图标库

巧妇难为无米之炊。组件库通常依赖很多图标,因此我们先开发一个支持按需引入的图标库。

假设我们现在已经拿到了一些漂亮的svg图标,我们要做的就是将每一个图标转化生成.vue组件与一个组件入口index.ts文件。然后再生成汇总所有组件的入口文件。比如我们现在有foo.svg与bar.svg两个图标,最终生成的文件及结构如下:

相应的内容如下:

// bar.ts
import _Bar from "./bar.vue";

const Bar = Object.assign(_Bar, {
  install: (app) => {
    app.component(_Bar.name, _Bar);
  }
});

export default Bar;
// foo.ts
import _Foo from "./foo.vue";

const Foo = Object.assign(_Foo, {
  install: (app) => {
    app.component(_Foo.name, _Foo);
  }
});

export default Foo;
// argoIcon.ts
import Foo from "./foo";
import Bar from "./bar";

const icons = [Foo, Bar];

const install = (app) => {
  for (const key of Object.keys(icons)) {
    app.use(icons[key]);
  }
};

const ArgoIcon = {
  ...icons,
  install
};

export default ArgoIcon;
// index.ts
export { default } from "./argoIcon";

export { default as Foo } from "./foo";
export { default as Bar } from "./bar";

之所以这么设计是由图标库最终如何使用决定的,除此之外

argoIcon.ts

也将会是打包

umd

的入口文件。


// 全量引入import ArgoIcon from "图标库";
app.use(ArgoIcon); 

// 按需引入import { Foo } from "图标库";
app.use(Foo);

图标库的整个构建流程大概分为以下3步:


1. svg图片转.vue文件

整个流程很简单,我们通过glob匹配到.svg拿到所有svg的路径,对于每一个路径,我们读取svg的原始文本信息交由第三方库svgo处理,期间包括删除无用代码,压缩,自定义属性等,其中最重要的是为svg标签注入我们想要的自定义属性,就像这样:


<svg 
  :class="cls" 
  :style="innerStyle"
  :stroke-linecap="strokeLinecap"
  :stroke-linejoin="strokeLinejoin"
  :stroke-width="strokeWidth">
  <path d="..."></path>
</svg>


之后这段

svgHtml

会传送给我们预先准备好的摸板字符串:



const template = `
<template>
  ${svgHtml}
</template>

<script setup>
defineProps({
    "stroke-linecap": String;
    // ...
  })
  // 省略逻辑代码...
</script>
`


为摸板字符串填充数据后,通过fs模块的writeFile生成我们想要的.vue文件。

2. 打包vue组件

在打包构建方案上直接选择vite为我们提供的lib模式即可,开箱即用,插件扩展(后面会讲到),基于rollup,能帮助我们打包生成ESM这是按需引入的基础。当然,

commonjs

umd

也是少不了的。整个过程我们通过Vite 的

JavaScript API

实现:



import { build } from "vite";
import fs from "fs-extra";

const CWD = process.cwd();
const ES_DIR = resolve(CWD, "es");
const LIB_DIR = resolve(CWD, "lib");

interface compileOptions {
  umd: boolean;
  target: "component" | "icon";
}

async function compileComponent({
  umd = false,
  target = "component"
}: compileOptions): Promise<void> {
  await fs.emptyDir(ES_DIR);
  await fs.emptyDir(LIB_DIR);
  const config = getModuleConfig(target);
  await build(config);

  if (umd) {
    await fs.emptyDir(DIST_DIR);
    const umdConfig = getUmdConfig(target);
    await build(umdConfig);
  }
}
import { InlineConfig } from "vite";
import glob from "glob";
const langFiles = glob.sync("components/locale/lang/*.ts");

export default function getModuleConfig(type: "component" | "icon"): InlineConfig {
  const entry = "components/index.ts";
  const input = type === "component" ? [entry, ...langFiles] : entry;
  return {
    mode: "production",
    build: {
      emptyOutDir: true,
      minify: false,
      brotliSize: false,
      rollupOptions: {
        input,
        output: [
          {
            format: "es", // 打包模式
            dir: "es", // 产物存放路径
            entryFileNames: "[name].js", // 入口模块的产物文件名
            preserveModules: true, // 保留模块结构,否则所有模块都将打包在一个bundle文件中
            /*
             * 保留模块的根路径,该值会在打包后的output.dir中被移除
             * 我们的入口是components/index.ts,打包后文件结构为:es/components/index.js
             * preserveModulesRoot设为"components",打包后就是:es/index.js
            */
            preserveModulesRoot: "components" 
          },
          {
            format: "commonjs",
            dir: "lib",
            entryFileNames: "[name].js",
            preserveModules: true,
            preserveModulesRoot: "components",
            exports: "named" // 导出模式
          }
        ]
      },
      // 开启lib模式
      lib: {
        entry,
        formats: ["es", "cjs"]
      }
    },
    plugins: [
      // 自定义external忽略node_modules
      external(),
      // 打包声明文件
      dts({
        outputDir: "es",
        entryRoot: C_DIR
      })
    ]
  };
};
export default function getUmdConfig(type: "component" | "icon"): InlineConfig {
  const entry =
    type === "component"
      ? "components/argo-components.ts"
      : "components/argo-icons.ts";
  const entryFileName = type === "component" ? "argo" : "argo-icon";
  const name = type === "component" ? "Argo" : "ArgoIcon";
  return {
    mode: "production",
    build: {
      target: "modules", // 支持原生 ES 模块的浏览器
      outDir: "dist", // 打包产物存放路径
      emptyOutDir: true, // 如果outDir在根目录下,则清空outDir
      sourcemap: true, // 生成sourcemap 
      minify: false, // 是否压缩
      brotliSize: false, // 禁用 brotli 压缩大小报告。
      rollupOptions: { // rollup打包选项
        external: "vue", // 匹配到的模块不会被打包到bundle
        output: [
          {
&am

标签: javascript vue

热门推荐