GAミント至上主義

Web Monomaniacal Developer.

Vue.jsとElementで配列操作UIをつくる

現在、Vue.js 2とElementを使って、モバイル向け履歴書作成アプリを作成しているけど、学歴、職歴などでリスト要素の操作がいろいろ必要になってつくったので整理しておく。

前提としてモバイル(タッチパネル)向けなので、PCではよくあるドラッグでの移動は画面が長くなるとスクロールしてしまうため諦めた。面倒だけどすべてポチポチして操作する。

あとの機能はこんな感じ

  • リスト要素はコンポーネントにして使い回せるように
  • それぞれのリスト要素からできる操作は各要素から上下に追加、上下に移動、複製、削除
  • リスト要素からのデータ操作はコンポーネントから$emitを通じてすべて親で行う
  • 親から出来る操作は追加、並び替え、リセット
  • 並び替えにはlodashの_.orderByを使う
  • 本体の機能であるトランジションを使って、削除と追加時はアニメーション表示

今回のためにCodePenに登録して使ってみた。

実際の開発は下記テンプレートを使ってWebPack、Single File Componentでやっているので、CodePen用には書き直しになるためちょっと面倒。

GitHub - vuejs-templates/pwa: PWA template for vue-cli based on the webpack template

とりあえず動いたところまでで力尽きたので解説は後。

See the Pen Vue.js + Element List Control UI by yu yamazaki (@uyamazak) on CodePen.

Vue.js v2で親子間以外のイベントやり取り

最近はずっとVue.jsを使ってUIを開発しているけど、ページが複雑になるにつれ、どうしても親子間以外の兄弟とか、深くなったコンポーネントからとか、グローバルなイベントのやり取りが必要になってきた。

発信側はthis.$emit(’foobar’, data)と、受信するコンポーネントでv-on:foobar="bazzMethod"を書いて繋げていくのも不可能ではないけど、メンテナンスを考えると不可能。

ずっと検索でさまよっていたところ、公式ドキュメントでv1.xからの移行にあった。昔は$broadcastと$dispatchというのがあったらしい。
jp.vuejs.org

2ではeventHubという概念を使うらしい。Vueの機能とは言えない気がするので概念とした。

一つグローバルなVueオブジェクトを作って、みんなそれからemitしたり、onしたりするだけ。

公式のドキュメントだとvue-routerを使う場合はつらみがあるので、Vueオブジェクトのprototypeにぶち込んでしまい、どのコンポーネントからでも呼び出せる下記のやり方が非常に便利。

Create a global event bus in Vue.js – VueJobs – Medium

このやり方でもいいんだけど、webpackのテンプレートを使っていて、main.jsをなるべく汚したくないし、せっかくなので別の機能でプラグインの使い方も覚えたので、プラグイン形式で作り直すことにした。

まずプラグイン用ファイルを作る

EventHub.js

const eventHub = {
  install: function (Vue, options) {
    Vue.prototype.$eventHub = new Vue()
  }
}
export default eventHub


あとは呼び出して登録するだけ

main.jsとか(このテンプレートを使った場合)に追加

import EventHub from './path/to/EventHub'
Vue.use(EventHub)

これで、コンポーネント内からthis.$eventHubで呼び出せるようになる。

on側はだいたいコンポーネントのmountedでいい気がしている

  mounted: function () {
    this.$eventHub.$on('save-complete-notify', this.fooMethod)
 }

emit側はmethodsとか、watchとかでやる

sendMessage function () {
      this.$eventHub.$emit('save-complete-notify', '保存したよ')
}

テンプレートだとthisはいらない

<button @click="$eventHub.$emit('download', data)">ダウンロード</ebutton>

一度eventHubを登録してしまえば、mountedでonすればどこからでもemitして処理ができるのでめっちゃ楽になった。

グローバル登録してしまうのでアプリケーションの規模や開発人数によっては名前がかぶってしまうとか、つらくなってしまうかもしれないけど、Vueで大人数で開発するのは何か間違ってる気がするので問題になることは少なそう。

$emitと$onしたいというより、dataをグローバルで保持したいということになってきたら公式の状態管理ライブラリであるVuexの出番だと思うけど、今の所出番はない。

Introduction · Vuex

Vue.jsでPWAアプリをつくりはじめた

2018/07/12追記 公開しました

rirekisho.yagish.jp

2018/7/5追記

この記事で利用したテンプレートはすでにメンテナンスに入っており、今後はvue-cliプラグイン形式になるようです。
Webpackも3から4になっているので推奨です。


GitHub - vuejs-templates/pwa: PWA template for vue-cli based on the webpack template


vue-cli/packages/@vue/cli-plugin-pwa at dev · vuejs/vue-cli · GitHub


追記ここまで


bizoceanでは、ワードやエクセル形式の書式を配布しているが、ほとんど書式自体の編集が不要でワード・エクセルを使うまでもない書式もあるし、今後PCを使う人自体が減れば、どんどん利用も減っていくだろうと思う。

個人的にもワード・エクセルは高機能すぎてよくわからなくなるので使いたくない。

そんなことで2月から本格的に書式をWEB上で作れる新規サービスの設計&開発を始めた。

いろいろやるうちに、結論としてはVue.jsを使ったPWAが良さそうということになった。

初期版

Djangoのフォームと、ヘッドレスChromeを使ったHTMLからPDFへの変換サーバーを使ったもの。

ほぼDjangoで完結するのでこれはこれで保守性は良さげ。だけど、UI周りでグリグリしようとすると、Djangoはそういうのない(というより自由な)ので、勉強も兼ねてVue.jsを使うことにする。

雑食系WEB開発者としては、jsのフロントエンドの経験が必須だと思っていた。

Vue.jsをDjangoテンプレート上でつかう版

とりあえず本格的にVue.jsを使うのは始めてなので、CDNからのscriptタグを使って開発をはじめた。
テンプレートタグの{{ var_name }}は丸かぶりするけど、vue側はdelimitersで簡単に変更できる

var app = new Vue({
  delimiters: ['[[', ']]'],
  el: '#app',
  data: {
// 以下略

が、コンポーネントが3つか4つ程度ならscriptタグでなんとかなるけど、コードが長くなってきたり、書式の種類ごとにコンポーネント化をしたくなってくると早々に限界が来た。

単一ファイルコンポーネントがファイルごとに分けられて良さげ。

jp.vuejs.org
しかも事前にテンプレート描画処理をコンパイル的なことができるので、動作も早いらしい。

しかし、それにはwebpackが必要だった。。。

webpack使用してUIは全部Vue.js、DjangoはあとでAPI的なのつくるよ版

イマココ。

ついにずっと避けてきたnpmやwebpackと対峙することになった。フロントエンドは変化が激しすぎる上に設定が多すぎるイメージで落ち着くまで待ちたかった。

これからやるならPWAがいいよね、オフラインでも動くし、Googleも推してるし、Safariも対応しそうだし、ってことで下記のvueの公式PWAテンプレートを元に書きかけのものを移植した。

GitHub - vuejs-templates/pwa: PWA template for vue-cli based on the webpack template

npm、nodeを開発サーバーに直に入れたくないので、おなじみのDockerを使った。いろいろインストールしまくったり、ぐちゃぐちゃになってもホストマシンは汚れずリセットできるので安心。
Dockerfileはこんな感じで、appディレクトリをdocker run時に-vでマウントして開発する。

FROM node:9-slim
RUN mkdir /varuna/
RUN apt-get update --fix-missing && apt-get -y upgrade

RUN apt-get install -y \
    git \
    bzip2

RUN npm install -g vue-cli

COPY app app
EXPOSE 8080
WORKDIR /varuna/app/shanyang-project
CMD ["bash"]

何かのインストール時に必要なbzip2とかgitをとかいれたり、忘れたけどいくつかのエラーを潰して、やっとnpm run devできたと思ったら、最初の難関は、Lintのエラーの嵐。

セミコロンがいらんとか、インデントがどうとか、変数名がどうとか。300個は直した気がする。もっと早くやっておけばよかった。

npmの開発サーバーはファイルを更新すると勝手に更新してくれるので再読込すら不要で便利。

まだ表に出せる段階ではないけど、いくつかもっと早く知っておきたかったことをメモ。

vue.js関連

コンポーネント親子間のデータ受け渡し

コンポーネント入れ子が深くなってきたら、親子間のデータの受け渡しはほぼ必須となる。
親から子はプロパティ(props)、子から親はカスタムイベント(emit)は死守すべき。
jp.vuejs.org

this.$rootとかで出来なくはないけど、ルーティングを入れたりし始めると破綻する。

また、親子より遠くなると途端に面倒になるので、なるべく深くせず、またルーティングで分けすぎない方がいい。

最初、書式の編集、プレビューのルーティングを分けたが面倒すぎたので、編集とプレビューは同じコンポーネント内に出して、表示の切り替えをすることにした。

状態が複雑になってきたら素直にVuexを使ったほうがいいと思うけど、今回はまだそんな複雑ではないので使ってない。
vuex.vuejs.org

Atomの設定

いつもサーバー側プログラムはvimだけど、他の人とリアルタイム編集作業が便利なのでAtomを使い始めた。
保存時にフォーマットするAtom Beautifyとか、vue対応を入れたら便利になった。

ここらへん参照
qiita.com

vue-router

公式のルーター。URLで状態を変えることができ、個別ページの共有や、ブラウザバックなどができるようになるので、ほぼ必要になると思う。

コンポーネントへの分割も必要となり、いろいろ理解も進むので早めにやってしまったほうが良かった。

公式テンプレートでrouterを使用すると、main.jsは下記のようになっており、メインのアプリケーションすらもコンポーネント化が必要なる。

new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App }
})

紹介 | Vue Router

PWA

PWAに関してはまず、Googleの公式を理解するのが一番良さそう。まだ途中。
はじめてのプログレッシブ ウェブアプリ  |  Web  |  Google Developers

ローカルへのデータ保存

編集中の文字列の自動保存など、最初一番簡単なLocalStorageを使ってみたけど、パフォーマンス的にあんまり良くないらしい。いろいろ探しているとMozillaが作っている良いlocalForageというライブラリがあり、さらにそれをVueで簡単に使えるライブラリもあった。

これを使えば、環境に合わせて一番いい機能を使ってくれる。PWAのP、プログレッシブ的なことをやってくれる。最初からこれを使えばよかった。

www.npmjs.com

ビルドしたファイルの確認

npm run buildするとdistディレクトリにまとまったやつができる。恐れていた開発中に動いたけど、ビルドしたら動かないという問題は今のところない。ほぼテンプレートそのままだからか。

でもPWAはhttpslocalhostではないと動かない。開発用にSSLのサーバーをいちいち用意するのは面倒なのでlocalhost一択。

Googleのドキュメントにも出てくるけど、Chromeの拡張でできてしまうがあった。なんでもできるな。

Web Server for Chrome - Chrome Web Store

今後

やっとVueの2%ぐらいは理解できた気がして、作りたいものを所々つまづきながらも書けるようになってきた。

まずはHTML,JSで単体で動けるようなものを作り、PDF変換とか、ログインとかデータのバックアップ等必要に応じてDjangoAPIを作っていく。

UI側が全部Vue.で済めば単なる静的ファイルの配信だけなのでGAEに置いてしまってもいいかもしんない。HTTPSロードバランサーとCloud Storageでもできるけど、GAEは安いうえにSSL証明書自動更新がついて便利になったので。

そしてPWA最適化を行い、サクサクスマホWEBアプリを作りたい。