オールアバウトTech Blog

株式会社オールアバウトのエンジニアブログです。

【Vue.js】業務で開発した自作ライブラリをnpmに公開してみた

昨年に引き続き、オールアバウトの新卒1年目エンジニアが投稿する連載企画 「テックブログ新卒週間 2018」がはじまりました!

f:id:allabout-techblog:20180315161756p:plain

今回は第一弾として、お買い物情報メディア「PICUP」をほぼ一人で開発している@sinpey_g2が担当します。

picup.allabout.co.jp

PICUPについては昨年末にも記事を書いたので、こちらもご覧ください。

allabout-tech.hatenablog.com

はじめに

PICUPでは、記事の入稿や公開をするためのCMSを内製していて、CMSではJavaScriptフレームワークにVue.jsを使用しています。今回は、Vue.jsで実装したUIをライブラリ公開した話について書きたいと思います。

Vue.jsとは?

Vue.jsとは、JavaScriptフレームワークの1つで、プログレッシブフレームワークという思想のもと、がっつりSPAを作ることもできるし、フォームの1要素のみに使うこともできるなど、規模を問わず適用することができます。詳しくは、公式のドキュメントを御覧ください。

jp.vuejs.org

特に、コンポーネントという機能が便利で、今回ライブラリ化したものも、単一ファイルコンポーネントと呼ばれるファイル単位で要素やロジックを切り分けられる機能を用いて作成しました。

コンポーネント — Vue.js

単一ファイルコンポーネント — Vue.js

Vue.jsを使って実装したCMSの機能例

ここで、Vue.jsを使って実装したCMSの機能を一つ紹介したいと思います。

f:id:allabout-techblog:20180313171846g:plain

これは記事のパーツを移動させる機能で、記事のリライト時に役立っています。要素をドラッガブルにする機能は、Vue.Draggableというライブラリで簡単に実現することができています。

github.com

このように、Vue.jsにはライブラリが豊富にあるため、PICUPのCMSではそれらを有効に活用しています。

今回作成したライブラリの機能

ここから、今回作成したライブラリの機能の説明をしていきます。機能としては、上で挙げたライブラリの改良版のようなものを実装しました。上のgifを見て分かる通り、既存のドラッガブルライブラリでは、要素を一つずつしか動かすことができません。そこで、さらなる作業の効率化のために、複数の要素を同時に動かす機能を実装しました。

先に完成したものをお見せします。

f:id:allabout-techblog:20180313173746g:plain

複数の要素が同時に動いていることがわかります。

実装のポイント

GitHub - sympe/vue-multiple-sortable: sorting to with multiple selected elements in your Vue.js application.

簡単に実装のポイントだけ紹介します。ソースは全て上記リポジトリで公開しているので、詳しく見たい方はそちらを見てください。

1. ドラッガブル要素のイベントハンドリング

<div
    :class="{selected: item.isSelected}" 
    draggable="true" 
    @dragstart="dragstart(item, $event)"
    @dragend="dragend"
    @dragenter="dragenter(item, $event)"
    @click="select(item)"
    v-for="item in items"
    :key="item.index"
>

要素に対して、ドラッグイベント(dragstart、dragenter、dragend)や、クリックイベント(click)をハンドリングするように指定しています。

2. 選択時の処理

select: function (item) {
      if (this.selectedItems[item.index]) {
        this.deleteSelectedItems(item);
        return;
      }

      this.addSelectedItems(item);
    },

onClickイベントをハンドリングして、クリックしたitemを選択中状態にします。

3. ドラッグ時の処理

if (event.pageY < this.beforeY) {
  item.index += Object.keys(this.selectedItems).length;
  for (var key in this.selectedItems) {
    this.selectedItems[key].index -= 1;
  }
} else {
  item.index -= Object.keys(this.selectedItems).length;
  for (var key in this.selectedItems) {
    this.selectedItems[key].index += 1;
  }
}

f:id:allabout-techblog:20180319135307j:plain

各itemがインデックスを持っていて、インデックスの順番に並び替えをするようにしています。上図のように、ドラッグ時は選択された要素のインデックスを全てインクリメント(もしくはデクリメント)し、ドラッグされたアイテムは選択された要素の数だけインデックスをずらします。マウスの向きによって処理を変えていて、このあたりの処理は少しきたないのでいずれ修正したいです。

以上が簡単な実装のポイントになります。

ライブラリ公開

ライブラリを調査している中で、自分と同じように困っているという内容のissueを多く見つけたことから、今回実装した機能をライブラリとして公開してみることにしました。

ここからは、ライブラリを公開するまでの流れについて書きたいと思います。

1. 公開用Vue.jsプロジェクトの作成

公開用に、今回実装した機能だけをコンポーネントとして持つVue.jsプロジェクトを作成します。

1-1. Vue.jsのインストール

Vue.jsをインストールします。いくつかの方法がありますが、今回はvue-cliを使います。

$ npm install -g vue-cli
$ vue init webpack my-library

A newer version of vue-cli is available.

latest:    2.9.3
installed: 2.8.2

? Project name Enter
? Project description Enter
? Author Enter
? Vue build standalone
? Install vue-router? No
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm

基本は全部EnterでOKです。vue-routerは使わないのでNoにしました。 ESLintやE2Eテストなどは必要に応じて入れてください。

実行

$ cd my-library
$ npm install
$ npm run dev

DONE  Compiled successfully in 4828ms

Your application is running here: http://localhost:8080

http://localhost:8080/ にアクセスするとサンプルが表示されるはずです。

f:id:allabout-techblog:20180313174559p:plain

1-2. 不要なファイルの削除

インストールができたので、ここからサンプルプロジェクトをベースにライブラリを実装していきます。実はVue-cliを使ったことで、既に単一ファイルコンポーネント導入済のVue.jsプロジェクトの作成ができています。ディレクトリ構成は以下のようになっています。

.
├── node_modules
├── build
├── config
├── index.html
├── package-lock.json
├── package.json
├── src
│   ├── App.vue
│   ├── assets
│   ├── components
│   │   └── HelloWorld.vue
│   └── main.js
└── static

ここから不要なファイルを削除すると、ディレクトリ構成は以下のようになりました。

.
├── node_modules
├── package.json
└── src
   └── MultipleSortable.vue

単一ファイルコンポーネントのApp.vueをMultipleSortable.vueにリネームして、先程の複数ドラッガブルの機能を実装します。

2. npm公開用の設定

npmに公開するために、webpack.config.jsとpackage.jsonを以下のように記述します。

webpack.config.js

const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge')
const CleanWebpackPlugin = require('clean-webpack-plugin');

var config = {
  output: {
    path: path.resolve(`${__dirname}/dist/`)
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: {
          loader: 'vue-loader',
        }
      }
    ]
  },
  externals: {
    'vue': 'Vue'
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      minimize: true,
      sourceMap: false,
      mangle: true,
      compress: {
        warnings: false
      }
    })
  ]
};

module.exports = [
  merge(config,
  {
    entry: path.resolve(__dirname + '/src/MultipleSortable.vue'),
    output: {
      filename: 'multiple-sortable.js',
      libraryTarget: 'umd',
      library: 'multiple-sortable',
      umdNamedDefine: true
    }
  })
];

package.json

{
  "name": "vue-multiple-sortable",
  "version": "1.0.0",
  "description": "You can sorting to with multiple selected elements in your Vue.js application.",
  "main": "dist/multiple-sortable.js",
  "author": "[名前]",
  "license": "MIT",
  "private": false,
  "dependencies": {
    "vue": "^2.5.13"
  },
  "repository": {
    "type": "git",
    "url": "[GitHubのURLあれば]"
  },
 "devDependencies": {
    "autoprefixer": "^7.1.4",
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-env": "^1.6.0",
    "clean-webpack-plugin": "^0.1.17",
    "cross-env": "^5.1.1",
    "css-loader": "^0.28.7",
    "style-loader": "^0.18.2",
    "vue-loader": "^12.1.0",
    "vue-template-compiler": "^2.3.3",
    "webpack": "^3.6.0",
    "webpack-merge": "^4.1.0",
    "webpack-node-externals": "^1.6.0"
  },
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
  }
}

特に、package.jsonのprivateをfalseにしておかないと公開できないので注意してください。

ビルド

ここまでできたら、npm run buildコマンドでビルドしておきます。

$ npm run build

ビルドすると、distディレクトリにmultiple-sortable.jsというファイルが作成されます。最終的に、公開前のディレクトリ構成は以下ようになります。

.
├── node_modules
├── dist
│   ├── multiple-sortable.js
├── package.json
├── src
│   ├── MultipleSortable.vue
└── webpack.config.js

3. npmのアカウント作成、公開

npm.jsでnpmアカウントを作成します。アカウントを作成したら、実装済のVue.jsプロジェクトにアカウント情報を登録します。登録にはnpm adduserコマンドを使います。Username、Password、Emailを聞かれるので、作成したアカウントの内容を入力します。

$ npm adduser
Username: 
Password:
Email:

以上で、公開することができるようになっているはずです。公開にはnpm publishコマンドを使います。

$ npm publish

公開することができました。

www.npmjs.com

こちらが公開したライブラリです。簡単なデモや、使い方も書いてあるのでぜひ使ってみてください!

初回公開後にしたこと

初回公開後にしたことを、いくつか紹介します。

ReadMeにバッジをつける

ReadMeにバッジをつけてみました。MITライセンスと、Travis CIのビルド結果のバッジを追加しました。

f:id:allabout-techblog:20180313180143p:plain

Travisのバッジの付け方は以下を参考にしました。

christina04.hatenablog.com

デモページ公開

デモがあった方がわかりやすいと思ったので、GitHub Pagesでデモページを公開しました。

https://sympe.github.io/vue-multiple-sortable-demo/dist/

公開方法は、以下のQiitaを参考にしました。

qiita.com

本来はJSFiddleなどを使いたかったのですが、単一ファイルコンポーネントが使えないため断念しました。少し調べたところ、WIP版らしいですが単一ファイルコンポーネントが使えるオンラインエディタもあるみたいなので、こちらでデモを作成してもいいかもしれません。

vueの単一ファイルコンポーネント用のオンラインエディタを作ってみた - Qiita

アップデート

ReadMeの追加や、バグ修正などを反映するためにはアップデートが必要です。npmでは、ライブラリのアップデート手順はとても簡単です。

  1. 変更箇所を保存
  2. package.jsonのversionを変更
  3. 再度npm publish実行

package.json

{
  "name": "vue-multiple-sortable",
  "version": "1.0.0",  // ←ここを変更
   以下省略

今後のアップデート予定

今後は、まだReadMeの内容が薄いので、まずはドキュメントを充実させたいと思います。その後は、スクロール機能への対応など、より幅広いケースで使えるようにアップデートしていきたいと思います。

まとめ

このように弊社ではVue.jsを使って複雑なUIを簡単に実装しています。特にJavaScriptフレームワークを使うことで一定のルールが担保されるので導入してよかったと感じています。

今回ライブラリ公開というものに挑戦してみて、npmライブラリのビルド方法や公開方法を学べたことは、エンジニアとしていい経験になりました。今後も積極的にOSS活動していきたいと思います。

最後にGitHubリポジトリを載せておくので、よかったらissueやプルリクなど(starも)お待ちしています。

github.com