トップ 追記

2018-04-19 [長年日記]

_ Nuxtでrouteに応じてVuex Storeをmodule分割する方法

まとめ

  • Nuxt は route に応じて自動で Vuex Store を module 分割できるが、あえて手動で registerModule することもできる
  • module 分割の際に大事なのは
    • namespaced 設定
    • state を function に
    • registerModule はすでに存在する module を上書きしてしまう(壊れる)

Vuex Storeのmodule分割の基本

cf. モジュール · Vuex

基本的な Vuex Store の構造は

state: {
},
mutations: {
},
actions: {
}

となるが、これを以下のようにする。

{
  modules: {
    name: {
      state: {
      },
      mutations: {
      },
      actions: {
      }
    },
    name: {
      state: {
      },
      mutations: {
      },
      actions: {
      }
    }
  }
}

Nuxt.js では store/ 以下に page に対応する module 構造を置いてあげれば自動的に module として登録される。便利。

routeに同期させて同一構造のmoduleを手動でregisterする

手動 register

module 構造の Store を作る方法は上の構造の object をそのまま new Vuex.Store() に突っ込んでもよいのだが、動的に組むには store.registerModule(NAME, OBJECT) してあげるとよい。

Nuxt の場合は特に何の設定もしなくても各 component から this.$store で Vuex Store へアクセスできるので、

this.$store.registerModule(NAME, OBJECT)

になる。

同一構造の module を動的 register する際の注意点
  1. namespaced: true を設定
  2. state は function
  3. module を registerModule するタイミングで state が消える
  4. created() で registerModule 以外の $store 操作を行なっている場合は module 生成が黙って cancel されるか壊れてしまうっぽいので mounted() で行うのが安全

1 については同一構造の state が複数あるとどれに対してアクセスしているのかを明示しつつそれぞれの影響範囲が閉じている必要がある。そのための namespaced.

2 については Vue component の data と同じ。インスタンスが複数登場する場合は plain object を使ってしまうと state がみんな同じになってしまう。

3 については分かってしまえばその通りなんだけど、route を手動で書かなくても自動的に設定してくれる Nuxt 環境では注意しないといけない点。

実際にどう対処するか、以下のコードをサンプルとして挙げておく。module name を route name と同一にすると仮定した場合はこのように意図せぬ Store の破壊を防ぎつつ構築していくとよい。

if ( typeof this.$store.state[this.$route.name] == 'undefined' ) {
  this.$store.registerModule(
    this.$route.name,
    {
      namespaced: true,
      state() {
        return {
        }
      },
      mutations: {
      },
      actions: {
      }
    }
  )
}

4 も似た話。少なくとも registerModule() と $store.commit() を同じ created() の中で処理した場合に commit() だけが有効になってしまうという挙動が見られた。

※ 困ったことに vue-devtool では Store が破壊されたことは分からない。そこまで中の状態を細かく見ていないようだ。少なくとも v4.1.1 の段階では分からなかった。


2018-03-22 [長年日記]

_ UglifyJSでコードの書き換えはいろいろやれるけどちょっと分かりにくい(define, compress, source map編)

今まで UglifyJS は単に圧縮としてしか使ってなくて、あまり細かく設定を見たことがなかったが、いざ触ってみたらいくつか発見があったのでそのメモを残しておく。

UglifyJS — JavaScript parser, compressor, minifier written in JS

まとめ

  • 外部から define で値を埋め込むことができる
  • UglifyJS の基本はコードの圧縮だが、その際に dead code の除去も行える
  • source map の扱いにはややクセがある

define による外部からの設定の埋め込み

UglifyJS 自体は Node.js で動くので Node.js から環境変数を取得することはできるが、UglifyJS は Browserify ではないので、Browserify のように環境変数にアクセスしている部分をいい具合に置換してくれるわけではない。

そこで利用できるのが define.

  • オプション --define でコード内の定数をセットできる
    • literal に展開されるので文字列を埋め込むなどは難しい
    • boolean だけに留めておくのが正解っぽい
  • shell script の機能を呼び出して分岐させることはできる
uglifyjs  --define PRODUCTION=`[ "$NODE_ENV" = "production" ] && echo "true" || echo "false"`

とやると JavaScript のコードの中では

if ( PRODUCTION ) {
  ...
} else {
  ...
}

のように参照して分岐させることができる。

compress

結構細かく制御できるし、default では割と積極的にコードが削除される。

例えばデフォルトの動作で dead_code を remove できるので、上のように if を書いておくと PRODUCTION が true の場合は then の中身だけが出力コードに残り、false の場合は else の中身だけが出力コードに残る。

他の assets や API の JSON を取得する処理はよくあると思うが、その URL は development, staging 環境と production で差し替えたくなるだろう。上のようなコードにしておくと UglifyJS の compress だけでそれが実現できる。(UglifyJS 後には if はなくなって差し替え後のコードだけが残る。)

source map

  • source map オプションは output オプションの後に置かないと機能しない
    • output オプションのパスを絶対に参照するみたい(中身は見てない)
  • option の解釈が自身の生成向けオプションと読み込むソース向けのオプションと両方入っていて分かりにくい。これは UglifyJS がトランスパイルの最終工程に置かれることが多いゆえなのか、設計が古いのか、もうちょっと工夫してほしかった。

例えば uglifyjs 3.2.2 で確認したところ出力先のコードに source map を含めるには以下のようにする必要があり、

uglifyjs -o ./output/application.js \
         --source-map url=inline,includeSources

さらにこの際、./output/application.js.map の生成は抑止できない。(ファイルの生成と inline への埋め込みがトグルするものと思っていたので指定に失敗しているのかと思ってしばらく悩んだ。)

ちなみに uglifyjs の前工程で source map を inline に生成している場合は以下のように content=inline を追加する必要がある。まぁその場合は最終工程の uglifyjs を省略すればいいだけじゃね?と思わなくもない。

uglifyjs -o ./output/application.js \
         --source-map content=inline,url=inline,includeSources

cf. Emit inline source maps with UglifyJS v3 · Issue #2711 · mishoo/UglifyJS2

Tags: JavaScript

2018-02-18 [長年日記]

_ guiflowとuiflowでUiFlows試してみた

あるいは Kanazawa.rb meetup #66 に参加してきました。

UiFlowsとは

自分が知ったきっかけは誰かのツイート経由で

画面遷移に疑問を感じたあなたにオススメするUI Flowsというツール | UXデザイン会社Standardのブログ

だったのですが、37 Signals ( Basecamp ) で

A shorthand for designing UI flows – Signal v. Noise

採用されている(今もなのかな?)シンプルに

  • 表示内容
  • 操作内容
  • リンク先

を記述する言語です。

「ツール」と紹介されることが多いみたいだけど、具体的なツールというよりは「記法」と表現した方がまだ近いかも。元のブログでは普通に手描きのものが紹介されています。(ただ、そもそも UI Flows って「名前」は存在しないような気がしませんか?)

元のブログは英語ですが、画像だけ拾ってみても十分に雰囲気は分かります。だいたい以下のような感じになります。

ユーザーの目にするもの
----------------------
ユーザーの操作するもの  --->  ユーザーの目にするもの
                              -----------------------
                              ユーザーの操作するもの

guiflowとuiflow

自分がこの言語に興味を持ったのは、グラフィックツールがなくても GitHub でメンテしやすい遷移図を描けないか?だったので欲しいのは言語そのものだけではなく、それを実際に遷移図に起こしてくれるツールだった。そこで出てきたのがこの二つ。

この二つは同じ作者の手によるもので、 UI Flows 言語(仮にこう呼ぶ)を解釈し、ビジュアライズする実装。guiflow は Electron 製、uiflow は Node.js 製。

guiflowよさげなんだけど…

すぐに始められてグラフが見れるというのはすごくいいんだけど、ちょっと以下が気になった。

試したのは v 0.1.1

  1. 日本語が Unicode Escapesequence になってしまう
  2. DnD でファイルを開いてくれない

特に 1 が致命的で、一度このツールで保存しちゃうと、もうこのツールを使って読むか、変換済みの画像でなければ人間が共有するのは難しくなってしまう。(Unicode Escapesequence をそのまま読み下せる人は身近にはいませんので)

うーーーーん。これはちょっと期待していたものと違うなぁ。

uiflowは大丈夫なんだけど

同じ作者の uiflow では、普通にエディタで作成した UTF-8 のデータを変換することができた。

中を軽く読んでみたんだけど、どうも変換の処理ではなく、ACE というエディタでテキストデータを扱うと Unicode Escapesequnce になっちゃうのかな?

Ace - The High Performance Code Editor for the Web

uiflow を叩くのは我々コマンドラインに慣れている人間にはよいけど、遷移を考える人はそういう人ばかりではないので、これを普段使いにするかというと、ちょっと微妙な気がします。自動化の際には活躍すると思いますが。

さーてこれはどうしよう、というところまでたどり着いて今回は力つきてしまいましたとさ。

まとめ

  • UIの流れを記述するシンプルな言語がある
  • 2016年くらいから実装が増えてて試しやすくなってる
  • ただし元ネタは2009年のブログであり、今も使っているのかはよく分からなかった
  • guiflow は手軽に始められるけどテキストデータとしてはちょっと管理しにくいので課題はある

2018-01-19 [長年日記]

_ Rails 4.2にWebpacker入れた

まとめ

  • Rails 4.2 で既存の AssetPipeline にまったく手を加えずに Webpacker を導入しました
  • Webpacker は導入から deploy まで簡単でよい
  • Webpacker のサイトの情報だけでなく、Webpack も Yarn も(そこそこ)追っておこう
    • さすがに Node.js と npm もさすがにね

前提知識

  • Webpacker は AssetPipeline とは独立した RailsEngine であり、共存できる
  • Webpacker は Yarn で Webpack を入れるのでそれぞれについてある程度知っている必要あり
    • (使うだけならインストール方法と簡単な使い方の手引きがあればよい)

決めなきゃいけないこと

  • Webpacker で AseetPipeline を置き換えてしまうか否か

決めたこと

  • AssetPipeline は AssetPipeline でそのままにする
  • /app/javascript/ はさすがに狂ってる気がするので /app/webpacker/ に置くことにした

webpack 管理が webpacker 管理の他に生まれるということは基本的に考えていない。

根拠
  • まだ Webpack やモダンJS、モダンCSS に習熟したエンジニアがプロジェクトの中心にいない
  • 既存のコードについて 熟練工がすぐに必要な状況ではない
  • ただし、新規では Vue.js を使ったアプリを追加する予定
    • これを AssetPipeline + CoffeeScript + jQuery で書くかというとそれはイヤだ

感想

個人的にはこれまで Browserify に寄せててどうしても Webpack と仲良くできる気がしてなかったんだけど、そんな自分でも Webpacker の導入は非常にスムーズに進んだ。

特に deploy について何も気にしなくてよかったのは本当に助かった。ビルドシステムを作ることに頭を使うのは全然やりたいことじゃないので。

ただ、やはり事前に少なくとも Node.js と Yarn の知識はあった方がよい。Webpacker のドキュメントでは Node.js 6.0+ / Yarn 0.25+ って書いてあったけど、Webpack はすでに "The current Long Term Support (LTS) release is an ideal starting point." と言っている。Webpacker はあくまで Webpack と Rails が仲良くする手法の一つなので、あくまで本家情報を大事にしよう。

おまけ

rake ( Rails 5.1- ) webpacker:install と rake webpacker:install:xxx は違う。そりゃそうだ。

参考

Webpack 2 との比較
Tags: Webpack Rails