2018-05-02

MobXをVue.js(Nuxt.js)で試してみた

なぜVuexじゃないのか

先日来、ViewModel フレームワークが片っぱしから値の変化やイベントを検出して View を書き換えようとする姿勢に疑問が生じていて、本来は MVVM とかじゃなかったの? M 無視しすぎじゃね? あるいは M は全部 HTTP とか割り切りすぎじゃね? という感じになってきていた。1

それで検出されないようにするにはどうしたらよいかというのが昨日の「ViewModel系フレームワークでデータの変化を監視させない - あーありがち(2018-05-01)」になるわけだけど、これはいわゆる VM component 内の話。でも実際のところ Model はだいたいグローバルでシングルトンになるんじゃねーの?という気がしていて、そうなると component でだけ実現できてもなーと思うわけですよ。

で、じゃあグローバルでシングルトンてことになると Flux 系 Store 具体的に例えば Vue.js だとまぁ Vuex になるんだと思うけど、その Vuex ではこういうことはできない。Vuex は雑に言うと監視対象となる state とその state に変更を加えることを許された mutation から成る2 が、これらはそれぞれ独立した function であり、mutation には基本的には state しか渡ってこない。mutation 以外に action や getter も定義できるが、事情は同じである。すべて state を中心に置いている。そして改めてくり返すが、その state はすべて監視対象というわけだ。

class ベースの React.Component みたいに Store という名の Model 相当の class があって自由に property を増やせるわけではない。その強い制約こそがグローバルな状態管理によいのかもしれないが、それにしてもなぁという感じ。

MobXのサンプルコードを見てみる

そこで MobX.

Introduction | MobX

こいつは Store というよりは基本的には Observer と言ってよいと思う。

公式のサンプルコードを引用するとこんな感じ。

import { observable } from "mobx"

class Todo {
  id = Math.random();
  @observable title = "";
  @observable finished = false;
}

ここで「これ JS か?」と思うかもしれないけど、class property と decorator を Babel ってやれば使えるれっきとした ES.next valid な JS なので、JS 脳が化石化してる人は慌てた方がよいかもしれない。

ポイントは @observable で、この decorator がついている値だけが監視対象になる。ね、これこれ、こういうことですよ、やりたいのは。

ついでにこの observable な値に変更を加えるには @action を付けた function を経由するとデバッグしやすくてよいよ、ということなので、それらを適当に書き足すと

class Todo {
  @observable title = "";

  @action setTitle(title) {
    this.title = title;
  }
}

こんな感じ。

これは「各自が気ままに値を書き換えると追えなくなるよ」という教えに従っているだけで、絶対守らなきゃいけないってわけじゃないけど、まぁ守っておこう。別に混乱を生みたいわけじゃない。

実際にVue.jsで試してみた

Vue.js と MobX をブリッジしてくれるライブラリは有名どころで二つある。これ以外にもあるのかもしれないけど、見つけられなかった。

実際に利用したパッケージは

  • mobx 4.2.0
  • vue-mobx 2.0.0
  • movue 0.3.0

ここで雑に比較すると、とにかく component 内での記述を少なくしたいなら vue-mobx がオススメ。Vue.use の記述は済んでいるものとして component 内のコードはこれだけで済む。

import Model from '..'

export default {
  fromMobx: {
    Model
  }
}

なんと、fromMobx に Model を与えるだけ。こうしておいて、この object を食わせて Vue component を生成するだけでいきなり Model 内の observable な値と action が component 内に生える。

いきなり生えるのが Ruby の module っぽくて「おぉ、なるほど」となるのだが、さすがにどんな component から呼ばれるか分からない Model の observable と action がそのまま生えると、やたらと VM component が増えがちな昨今のフレームワークでカジュアルに使うにはちょっとまずそうな気がしないでもない。

そんなあなたには movue.

movue も fromMobx という property で VM component 内に Model の情報を呼んでくるのは一緒だけど、この fromMobx では細かく設定をコントロールできるので、こっちの component ではこの名前で、あっちの component ではこの名前で、といった使い方ができそう。

Decorator対応

MobX の decorator は optional なので、実はさっきの例は以下のようにも書ける。

import { observable, decorate } from "mobx"

class Todo {
  id = Math.random();
  title = "";
  finished = false;
}

decorate(Todo, {
  title: observable,
  finished: observable
})

こっちの方がより広範囲で動くのだけど、確かに decorator を使えた方が記述が分離せずに分かりやすい。

ということで使えるようにしていこう。

Nuxt x Babel対応

正直ややこしい。

Babel は 6 と 7 の間で decorator の扱いが変わっている。今回の実験は Nuxt.js 上で行っており、2018-05 現時点で Nuxt 1.0.0 は Babel 6 依存なのでそれで動くようにしていく。

babel-preset-mobx があるのでそれを使ってみたのだが、これは動かなかった。

zwhitchcox/babel-preset-mobx: All dependencies mobx depends on

中身を見ると preset と言いつつ実際には以下の4つの plugin を利用する

  • babel-plugin-transform-class-properties
  • babel-plugin-transform-decorators-legacy
  • babel-plugin-transform-es2015-classes
  • babel-plugin-transform-regenerator

Nuxt.js の Babel の設定は vue-app ( vue + babel-preset-env ) なので本当に必要なのは以下の2つ

  • transform-decorators-legacy
  • transform-class-properties

これを plugins に追加してやれば ok

順番が大事。 decorators-legacy の次に class-properties ここら辺は Babel のイヤンなところ。エラーを見ても何が起きてるのか分からん。

おまけ VS Code 対応

decorator でずっと警告が出るので ESLint の設定がちゃんと反映されてないのかなーと思って疑ってたけど、これは VS Code で独自に出してるやつでした。

javascript.implicitProjectConfig.experimentalDecorators": false,

を true にすると解決。

まとめ

MobX は Redux の面倒くささを解消してくれるとか jQuery とも合わせられるとかいろいろ言われている最近注目の状態管理ライブラリらしいけど、「observable で明示するだけ」というシンプルさは自分の考えている、もっと広く一般的な Model としても扱いやすそうに見えた。

昨今の ViewModel フレームワークが薄いフレームワークになることで View 側の問題の本質に注力しているように、Model 側は Model 側で「Backbone.Model のようないい具合にイベント起こしてくれる程度のシンプルなやつねーかな」と思っていたところに MobX を見つけることができたので、自分の中では割と期待が大きくなってきている。

(これなら既存の jQuery を叩き直すのにも、何かの拍子に付き合わなきゃいけなくなる可能性の高い React ともなんとかやっていけるかも?)

まだテスト周りがよく分かっていないので、次はテスト周りをチェックして、よさそうなら今後は MobX に振っていくかもしれないなーという気がしてます。

  1. MVW って言ってた Angular でさえ M のことは知らんてなってる感じがすごい(小並感 

  2. action はいったん置いておく 

About

例によって個人のなんちゃらです

Recent Posts

Categories

Tool 日々 Web Biz Net Apple MS ことば News Unix howto Food PHP Movie Edu Community Book Security Text TV Perl Ruby Music Pdoc 生き方 RDoc ViewCVS CVS Rsync Disk Mail FreeBSD Cygwin PDF Photo Zebedee Debian OSX Comic Cron Sysadmin Font Analog iCal Sunbird DNS Linux Wiki Emacs Thunderbird Sitecopy Terminal Drawing tDiary AppleScript Life Money Omni PukiWiki Xen XREA Zsh Screen CASL Firefox Fink zsh haXe Ecmascript PATH_INFO SQLite PEAR Lighttpd FastCGI Subversion au prototype.js jsUnit Apache Trac Template Java Rhino Mochikit Feed Bloglines CSS del.icio.us SBS qwikWeb gettext Ajax JSDoc Rails HTML CHM EPWING NDTP EB IE CLI ck ThinkPad Toy WSH RFC readline rlwrap ImageMagick epeg Frenzy sysprep Ubuntu MeCab DTP ERD DBMS eclipse Eclipse Awk RD Diigo XAMPP RubyGems PHPDoc iCab DOM YAML Camino Geekmonkey w3m Scheme Gauche Lisp JSAN Google VMware DSL SLAX Safari Markdown Textile IRC Jabber Fastladder MacPorts LLSpirit CPAN Mozilla Twitter OpenFL Rswatch ITS NTP GUI Pragger Yapra XML Mobile Git Study JSON VirtualBox Samba Pear Growl Mercurial Rack Capistrano Rake Win RSS Mechanize Sitemaps Android JavaScript Python RTM OOo iPod Yahoo Unicode Github iTunes God SBM friendfeed Friendfeed HokuUn Sinatra TDD Test Project Evernote iPad Geohash Location Map Search Simplenote Image WebKit RSpec Phone CSV WiMAX USB Chrome RubyKaigi RubyKaigi2011 Space CoffeeScript Nokogiri Hpricot Rubygems jQuery Node GTD CI UX Design VCS Kanazawa.rb Kindle Amazon Agile Vagrant Chef Windows Composer Dotenv PaaS Itamae SaaS Docker Swagger Grape WebAPI Microservices OmniAuth HTTP 分析基盤 CDN Terraform IaaS HCL Webpack Vue.js BigQuery Middleman CMS AWS PNG Laravel Selenium OAuth OpenAPI GitHub UML GCP TypeScript SQL Hanami Document SVG AsciiDoc Pandoc DocBook Develop Jekyll macOS Node.js Vite Heroku Transformer AI Data Cloud Wasm