🐕

vue-cliを使ってVuetifyで作成した自作コンポーネントをnpmパッケージにする方法

2022/12/26に公開

自作したコンポーネントを異なるプロジェクトで再利用したくなることがあります。
当然、Vue.jsはSFC(single file component)なので、プロジェクト間でファイルコピーすれば済む話。ところがバグを修正したり、機能を追加したりした場合のバージョン管理には一工夫必要になるし、何より、変更した内容を個々のプロジェクトに反映させていくのが手間です。
そんな時は、自作コンポーネントをnpmでパッケージにして、プラグインとして利用できるようにしてしまいましょう。

npm i PACKAGE_NAME

上記コマンドでインストールするだけで使えるようになります。
ここではVuetifyのv-text-fieldを拡張したコンポーネントをnpmパッケージにしてみます。

環境

記事投稿時の環境はこちら
Node v16.18.1
@vue/cli 5.0.8

パッケージ用プロジェクトの作成

vue-cliを使ってパッケージ用のプロジェクトを作成します。
同時にVuetifyを追加しておきましょう。

vue create PACKAGE_NAME
cd PACKAGE_NAME
vue add vuetify

作成が完了すると、プロジェクトは(大体)以下のような構成になっているはずです。

├─ node_modules
├─ public
├─ src
│  ├─ assets
│  ├─ plugins
│  │  └─ vuetify.js
│  ├─ App.vue
│  └─ main.js
├─ .gitignore
├─ jsconfig.json
├─ package-lock.json
├─ package.json
├─ README.md
└─ vue.config.js

App.vueやmain.jsなどはパッケージの公開には不要なので削除しても構わないのですが、公開前の動作確認をこのプロジェクト内でやりたいのでそのまま残しておきます。

コンポーネントの作成

パッケージングまでの流れを確認できれば良いので、さくっと簡単なコンポーネントを作成します。

src/components/MyTextField.vue
<template>
  <v-text-field label="テスト" />
</template>
<script></script>
<style></style>

labelプロパティが固定されてしまう使い物にならないコンポーネントですが、パッケージ出来たことを確認するだけなら十分です。

エントリーポイントの作成

パッケージとはいっても結局はjsファイルなので、プラグインとしてインストールされた先でimportして利用されます。

import hoge from 'PACKAGE_NAME'

このimportされた時に実行されるファイルがエントリーポイントです。
つまり、エントリーポイントは上記で作成したコンポーネントをimportしてexportすれば最低限の要件はクリアということになります。
src/build/build.lib.jsを作成します。

src/build/build.lib.js
import MyComponent from '../components/MyComponent.vue'
export default MyComponent

簡単ですね。
ですが、この状態では自作コンポーネントを利用しようとするたびにプラグイン(パッケージ)をimportしなくてはなりません。

プラグインは通常 Vue にグローバルレベルで機能を追加します。
Vue.use() グローバルメソッドを呼び出すことによってプラグインを使用します。

// `MyPlugin.install(Vue)` を呼び出します
Vue.use(MyPlugin)

new Vue({
  // ... オプション
})

公式: プラグイン-Vue.js

サンプルコードを見ると、importしたプラグインをVue.use()の引数にすると、プラグインのinstall()が実行されるとあります。

せっかくなので、グローバル登録できるようにinstall()を定義しましょう。

中身を以下のように加筆します。

src/build/build.lib.js
import MyComponent from '../components/MyComponent.vue'

// Vue.use() によって実行される install 関数を定義
+ const install = function (Vue) {
+   if (install.installed) return;
+   install.installed = true;
+   Vue.component('MyComponent', MyComponent);
+ }

// Vue.use() のためのモジュール定義を作成
+ const plugin = {
+   install,
+ };

// vue が見つかった場合に自動インストールする (ブラウザで <script> タグを用いた場合等)
+ let GlobalVue = null;
+ if (typeof window !== "undefined") {
+   GlobalVue = window.Vue;
+ } else if (typeof global !== "undefined") {
+   GlobalVue = global.Vue;
+ }
+ if (GlobalVue) {
+   GlobalVue.use(plugin);
+ }

// default exportをMyComponentからpluginに変更
- export default MyComponent
+ export default plugin

// ローカル登録も実現するのであればnamed export
+ export { MyComponent }

公式:Vue コンポーネントを npm パッケージ化するを参考にinstall()関数を定義しました。
未確認ですが、let GlobalVue = null;以下、exportの手前までのコードを追加することでCDN対応ができるようです。

動作確認

npmにパッケージとして公開する前に、エントリーポイントが正しく機能するかを確認します。

main.jsで先ほどのエントリーポイントをimportします。

src/main.js
import Vue from "vue";
import App from "./App.vue";
import vuetify from "./plugins/vuetify";
+ import PackageName from "./build/build.lib.js";

Vue.config.productionTip = false;

+ Vue.use(PackageName);

new Vue({
  vuetify,
  render: (h) => h(App),
}).$mount("#app");

エントリーポイントとなるファイルをPackageNameとしてimportし、Vue.use()でプラグインとして読み込みます。

App.vueを下記のように修正します。

src/App.vue
(略)
<my-component />
(略)

修正が完了したらnpm run serveしてブラウザを開きます。
labelが「テスト」となっているテキストフィールドが表示されていればOKです。
確認できたら、Ctrl+cで終了させておきましょう。
これで公開するコンポーネントとエントリーポイントの作成が完了しました。

package.jsonの修正

次はvue-cliを使って公開用のファイルをバンドルするために、package.jsonを修正します。

npmパッケージとして公開するのに必須となるのは次の設定です。

name

パッケージ名です。package.jsonが生成された段階でプロジェクト名が設定されているはずなので基本的に修正する必要はありませんが、npmに公開するためには、既存で公開されている他のパッケージと同じ名前が使用できません。また、パッケージ名でおおよそどんなものなのかを推測できる名前にした方が良いでしょう。

version

パッケージのバージョンです。
初めて公開するのであればそのままでOKです。

private

trueにすると非公開にすることができますが、npmアカウントが有料となります。
falseにすると一般公開となりますが、無料です。

main

パッケージがライブラリとして読み込まれたときに、実行されるべきファイルのパスを指定します。

files

公開対象のディレクトリ、ファイルを配列で指定します。

今回は以下のようにしてみてください。

package.json
{
  "name": "PACKAGE_NAME",
  "version": "0.1.0",
  "description": "",
  "private": false,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
+   "build-bundle": "vue-cli-service build --target lib --name PACKAGE_NAME --formats umd src/build/build.lib.js",
    "lint": "vue-cli-service lint"
  },
  "main": "dist/PACKAGE_NAME.umd.js",
  "files": [
    "dist"
  ],
(以下、略)

scriptsに一行追加しています。
npm run build-bundleで公開するためのファイルをバンドルすることができるようにしました。
vue-cli-service buildのオプションとして以下のように指定しています。

--target

libにするとライブラリとして構築されます。

--name

パッケージ名です。

--formats

構築されるファイルの形式です。umdを指定します。

最後のパス

作成したエントリーポイントのファイルパスを指定します。

ここまで来たらあと一息です。

npmアカウントの作成

npmパッケージにするのであればnpmのアカウントが必要です。
無料で作成できるので気軽に作ってしまいましょう。
npmはこちらから

バンドル

先ほどpackage.jsonscriptsで指定したnpm run build-bundleを実行します。

パブリッシュ

npmにパッケージとして公開しましょう。

npm publish

インストール

無事にnpmへパッケージを公開することができました。
vue-cliで別のプロジェクトを作成して、公開したパッケージをインストールしてみましょう。

vue create sandbox
cd sandbox
vue add vuetify
main.js
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify'
+ import PackageName from 'PACKAGE_NAME'

Vue.config.productionTip = false

+ Vue.use(PackageName)

new Vue({
  vuetify,
  render: h => h(App)
}).$mount('#app')
App.vue
+ <my-component />

動作確認

npm run serve
localhost:XXXXにアクセスして、インストールした自作コンポーネント(labelが'テスト'になっている)が正しく表示されていれば完了です。

まとめ

参考にさせていただいたサイトは数多あったのですが、微妙な環境の違いや自分の知識不足で公開できるようになるまで四苦八苦し、かなり時間がかかってしまいました。
複数の自作コンポーネントを一つのパッケージ(ライブラリ)にするなど、ここから先はご自身でいろいろ試してみてください。
自分の備忘録としてだけではなく、どなたかの参考になれば幸いです。

Discussion