如何寫一個屬於自己的 Vue3 組件庫

前言

大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心。

環境搭建

目前流行的組件庫搭建方式都是使用 monorepo 的方式,好處很多,可以在一個代碼倉庫中管理多個項目,可以達到項目之間的資源共享。這裏也是使用這種方式。

以 pnpm 構建 monorepo

首先全局安裝 pnpm

npm install pnpm -g
複製代碼

pnpm 初始化

pnpm init
複製代碼

得到 package.json 的初始內容後刪除 package.json 中的 name ,添加 "private": true 屬性,因爲這個作爲一個整體是不需要發佈的。組件都是寫在packages中。

{
  "private": true,
  "version""1.0.0",
  "description""",
  "main""index.js",
  "scripts"{
    "test""echo "Error: no test specified" && exit 1"
  },
  "keywords"[],
  "author""",
  "license""ISC"
}
複製代碼

配置 pnpm 的 monorepo 工作區

這是我目前的結構目錄,docs用來存放組件庫文檔,examples用於調試編寫好的組件,packages裏的compnents用於存放自己的組件。

比如我使用到packagesexamples這兩個,那麼就將這兩個都配置進去, 在pnpm-workspace.yaml中配置

packages:
  - 'packages/**'
  - 'examples'
複製代碼

每個子模塊都有屬於自己的package.json文件

以 components 包的package.json舉例,其中"@uv-ui/hooks": "workspace:^1.0.0"和 "@uv-ui/utils": "workspace:^1.0.0"是依賴於其他目錄的包,如果這個 components 包需要用到這兩個包,需要在這個目錄下也安裝上,之後發佈 npm 包的時候將workspace:^去掉即可,這兩個依賴的包也需要獨立發佈 npm 包,這些在發佈組件中有詳細描述。

{
  "name""uv-ui",
  "private": false,
  "version""1.0.11",
  "main""dist/es/index.js",
  "module""dist/es/index.js",
  "style""dist/es/style.css",
  "description""基於vue3的移動端組件庫",
  "scripts"{
    "build""vite build",
    "lint""eslint ./src/**/*.{js,jsx,vue,ts,tsx} --fix"
  },
  "repository"{
    "type""git",
    "url""https://github.com/monsterxwx/uv-ui",
    "directory""packages/uv-ui"
  },
  "keywords"[
    "uv-ui",
    "vue3組件庫",
    "mobile",
    "frontend",
    "components"
  ],
  "files"[
    "dist"
  ],
  "author""coderxwx",
  "license""MIT",
  "dependencies"{
    "@uv-ui/hooks""workspace:^1.0.0",
    "@uv-ui/utils""workspace:^1.0.0"
  }
}
複製代碼

其他兩個的包名則分別爲:@uv-ui/hooks 和 @uv-ui/utils,創建過程同上。

倉庫項目內的包相互調用

如何對hooksutilscomponents這三個包進行互相調用呢?我們只需要把這三個包都安裝到倉庫根目錄下的 node_modules 目錄中即可。所有的依賴都在根目錄下安裝,安裝到根目錄需要加上-w ,表示安裝到公共模塊的 packages.json 中。

pnpm install uv-ui -w
pnpm install @uv-ui/hooks -w
pnpm install @uv-ui/utils -w
複製代碼

根目錄的package.json將會出現如下依賴,這三個就是我們剛剛安裝的包

組件編寫

如果我們的組件庫包需要支持按需引入,那麼每個組件都需要進行 vue 的註冊app.component(comp.name,comp)。每個都寫一遍會比較麻煩,可以封裝成一個函數。

export const withInstall = (comp) ={
  comp.install = (app) ={
    // 註冊組件
    app.component(comp.name, comp)
  }
  return comp
}
複製代碼

以 button 組件爲例,首先目錄結構如下,在 src 目錄下編寫各個組件,在 components 目錄下有一個 index 用於將所有的組件導出

buttonindex.js代碼如下:將編寫的 button 組件導進來,然後導出去即可實現組件每個都是按需加載

import Button from './button.vue'
import {withInstall} from '@uv-ui/utils'

const UvButton = withInstall(Button)

export default UvButton

複製代碼

再來看看 components 目錄下的index.js文件,寫多少個組件就引入多少個,然後全部導出去,這麼做的目的是在全量導入的時候,可以直接引入組件,然後vue.use(uv-ui)即可將全部的組件註冊

import uvButton from './button'

const components = [
  uvButton
]

const install = (Vue) ={
  components.forEach(component ={
    Vue.component(component.name, component)
  })
}

export {
  uvButton
}

export default install

複製代碼

再來看看button.vue文件, 由於寫的比較多,這裏直接省略部分代碼,只保留關鍵代碼。

可以看到每個組件都需要給他一個名字方便在 vue 中註冊,export default { name: 'UvButton' }, 其次樣式通過 css 變量抽離出來,方便樣式的更改,樣式不用加scoped作用域,只要命名好樣式名稱即可。其他組件同理。

<template>
  <div class="uv-button">
    
  </div>
</template>

<script setup>
// 代碼
</script>
<script>
export default {
  name: 'UvButton'
}
</script>

<style lang="scss">
:root {
  --uv-button-primary: #409eff;
  --uv-button-success: #67c23a;
  --uv-button-warning: #e6a23c;
  --uv-button-error: #f56c6c;
  --uv-button-info: #909399;
  --uv-button-text: #303133;
}

$primary: var(--uv-button-primary);
$success: var(--uv-button-success);
$warning: var(--uv-button-warning) ;
$error: var(--uv-button-error) ;
$info: var(--uv-button-info) ;
$text: var(--uv-button-text) ;
.uv-button {
  font-size: var(--uv-button-font-size);
  border: 0;
  border-radius: var(--uv-button-border-radius);
  white-space: nowrap;
  color: #ffffff;
  background: none;
  outline: none;
  cursor: pointer;
}

</style>

複製代碼

組件打包

組件打包採用的是 vite 的庫模式打包,沒有使用 gulp 這些工具,是比較簡單的打包方式,只需要簡單配置即可實現每個組件都單獨打包,目前打包後所有組件樣式會合成一個style.css文件,樣式需要全部引入,組件是按需引入的方式,如果樣式也想做分離就需要使用到 gulp 等這些工具,將樣式全部拆分出來作爲一個包theme-chalk, 然後引入就類似 element-ui 那種方式,比較麻煩這裏就不詳細描述了,可以找找相關的文章瞭解。

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
  build: {
    target: 'modules',
    // 壓縮
    minify: true,
    rollupOptions: {
      // 忽略打包vue文件
      external: ['vue'],
      input: ['src/index.js'],
      output: [
        {
          format: 'es',
          entryFileNames: '[name].js',
          preserveModules: true,
          // 配置打包根目錄
          dir: 'dist/es',
          preserveModulesRoot: 'src'
        },
        {
          format: 'cjs',
          entryFileNames: '[name].js',
          preserveModules: true,
          dir: 'dist/lib',
          preserveModulesRoot: 'src'
        }
      ]
    },
    lib: {
      entry: './index.js',
      formats: ['es''cjs']
    }
  },
  plugins: [
    vue()
  ]
})
複製代碼

下面是打包後的結構,分別拆成 es 和 lib,樣式在 es 目錄下的 style.css

使用方式就比較簡單了

第一種:全量使用 main.js 中導入

import uvUI from 'uv-ui'
import 'uv-ui/dist/es/style.css'

app.use(uvUI)
複製代碼

第二種:按需導入 在 main.js 中引入樣式文件

import 'uv-ui/dist/es/style.css'
複製代碼

其他用到的地方引入相關組件

<template>
  <div>
    <uvButton type="primary">切換</uvButton>
  </div>
</template>

<script setup>
import { uvButton } from 'uv-ui'

</script>
複製代碼

組件發佈

編寫 package.json

package.json 重要字段說明:

package.json完整代碼

{
  "name""uv-ui",
  "private": false,
  "version""1.0.11",
  "main""dist/es/index.js",
  "module""dist/es/index.js",
  "style""dist/es/style.css",
  "description""基於vue3的移動端組件庫",
  "scripts"{
    "build""vite build",
    "lint""eslint ./src/**/*.{js,jsx,vue,ts,tsx} --fix"
  },
  "repository"{
    "type""git",
    "url""https://github.com/monsterxwx/uv-ui",
    "directory""packages/uv-ui"
  },
  "keywords"[
    "uv-ui",
    "vue3組件庫",
    "mobile",
    "frontend",
    "components"
  ],
  "files"[
    "dist"
  ],
  "author""coderxwx",
  "license""MIT",
  "dependencies"{
    "@coderxwx/uv-ui-hooks""1.0.0",
    "@coderxwx/uv-ui-utils""1.0.0"
  }
}
複製代碼

添加 LICENSE

Copyright (c) 2023 代表年份,coderxwx 替換成自己的名字

MIT License

Copyright (c) 2023 coderxwx

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
複製代碼

npm 賬號註冊登錄

要發佈到 npm 上首先就要註冊 npm 賬號,通過官網進行賬號註冊:www.npmjs.com/[1] 。

npm login
複製代碼

登錄 npm, 輸入賬號和密碼,密碼輸入不會有顯示,正常輸入即可,然後輸入自己的郵箱,郵箱驗證碼驗證通過後即成功登錄,後續不需要重複登錄。

調試 npm

如果不需要調試,可以跳過調試步驟,直接發佈。

npm link
複製代碼
npm link 包名
複製代碼
npm unlink 包名
複製代碼

爲了防止本地調試 npm 與發佈後的 npm 混淆衝突,在調試完成後一定記得手動取消項目關聯。

發佈組件

在 npm 包項目根目錄運行命令:

npm publish
複製代碼

運行完稍等一段時間即可在 npm 官網搜索到發佈的 npm 包。

關於發佈的一些錯誤彙總

發佈私有包

我們在寫組件庫的時候會用到一些函數,這些都可以作爲一個包進行發佈,但是這些不需要發佈爲正式的包,只需要給組件庫的主包使用,比如:

package.json中如果 name 是使用@xx來表示的則代表私有包,如果發佈 npm 爲私有包則需要收費,我們可以通過配置publishConfig字段將其表示爲共有包,然後進行npm publish進行發佈,如果不想寫這個可以直接輸入npm publish --access=public 比如我的私有包的前綴是這個 @coderxwx,@後面跟的是你 npm 的賬號名

{
  "name""@coderxwx/uv-ui-hooks",
  "private": false,
  "version""1.0.0",
  "description""",
  "main""index.js",
  "scripts"{
    "test""echo "Error: no test specified" && exit 1"
  },
  "publishConfig"{
    "access""public",
    "registry""https://registry.npmjs.org/"
  },
  "keywords"[],
  "author""coderxwx",
  "license""MIT"
}
複製代碼

如果不想使用自己的賬號名作爲私有包的前綴,可以創建一個組織

創建完成後就可以使用這個名字來當前綴了,比如我創建的是 uv-ui, 之後就可以使用 @uv-ui 進行發佈了。

更新組件

當完成組件的修改後需要重新發布更新組件,這裏有兩種方式,一種是直接修改package.json中的version信息,然後進行npm publish

還有一種更規範的方式是使用 npm 的指令。

其中 type 有這些:

patch:小變動,比如修復 bug 等 版本號變動 v1.0.0->v1.0.1

minor:增加新功能,不影響現有功能 版本號變動 v1.0.0->v1.1.0

major:破壞模塊對向後的兼容性,大版本更新 版本號變動 v1.0.0->v2.0.0

npm version [type]

npm publish
複製代碼

文檔編寫

我用的是 vitepress 來構建文檔,基本沒啥難度

安裝

pnpm install -D vitepress vue
複製代碼

vitepress 的具體使用可以參考這篇文章:juejin.cn/post/716427…[3]

教程到這裏就結束了,如有錯誤歡迎指出!

結語

我是林三心,一個熱心的前端菜鳥程序員。如果你上進,喜歡前端,想學習前端,那咱們可以交朋友,一起摸魚哈哈,摸魚羣,關注我,拉你進羣,有 5000 多名前端小夥伴在等着一起學習哦 -->

作者:唯有灬努力
鏈接:https://juejin.cn/post/7211521650252398649
來源:稀土掘金

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/DiEKB8DXbBzpvRfMxDhqhw