フロントエンド原理主義者が目論んだ脱webpacker

webpacker脱出を表現した絵画

初めに

webpackerアドバイザーの小宮山です。最近の趣味は相変わらずランニングですが、下の階のヌーラボさんにインスパイアされてボルダリングにも夢中になりつつあります。

フロントエンド原理主義とはサーバーサイドからフロントエンドへの干渉(特にレンダリング)を嫌う一派に私が勝手に名前を付けただけです。ただしSSRはこの限りでは無いので最近は結構曖昧になりつつあります。

動機

webpack関連のアップデートがwebpacker対応待ち

最大のデメリットはやはり、webpack関連のアップデートがwebpacker対応待ちになってしまうことでした。どんなにwebpack4が3から進化しているとしても、導入するにはレールとなるwebpackerが対応してくれるのを待つしかありません。

そしてwebpackerが対応してくれたら即導入なんてうまくいくはずがなく、webpack4へのマイグレーションをwebpackerという仲介者を通して行っていく羽目になるのは目に見えています。

どのみちwebpackの設定をメンテナンスすることは必須なのですから、わざわざwebpackerを被せて余計に分かりにくく冗長にする必要は全くありません。

yarn操作が遅い

ずっと不思議に思っていたのですが、addやremoveなどの操作がとてつもなく遅いです。普通だったら数秒で終わるようなパッケージ追加でも常に数分はかかります。分けてaddするとさらに数分もっていかれるので、追加に漏れがないか確認してから一気にaddするという謎の気遣いを毎回強いられていました。

dockerとmacの相性の悪さなどその他の原因があるのかもしれないと思いwebpackerを犯人扱いするのは避けていましたが、いざ脱却してみたらやはり速くなった…ような気がします。他にも原因があるのかスピードのブレが大きく、残念ながら気のせいだった可能性も否定しきれません。

フロントエンド原理主義

結局のところ、一番大きな動機はこれだったのかもしれません。フロントエンド開発環境がRailsに依存している現状を打破したいという気持ちを抑えきれませんでした。

状況

プロジェクト構造

プロジェクト構造は、既存のRailsアプリケーションにwebpackerをVueモードで追加したものです。

そこへさらにStorybook、Karmaなどを追加してフロントエンド開発環境を整備しています。

SPAとして開発していたので、既にフロントエンドはRailsへの依存がほぼない状態でした。そして唯一最大の依存こそがwebpackerです。

webpackerの利点

大きな利点はやはりRailsの敷くレールに乗れることです。というのが一般的な意見だと思いますが、正直なところそんなの余計なお節介で邪魔になるだけだと感じているのはおそらく私だけではないはずです。もし5年前にこのレールが敷かれていたら画期的だったかもしれません。しかし今は2018年です。今更レールを敷かれても不便で窮屈になってしまうだけなのです。

もう1点メリットだと思っていたことは、デプロイをRails任せで簡単に行えるという説です。説と付けたのでもう察してもらえると思いますが、残念ながらこの説は棄却されました。デプロイ周りの整備は私が直接担当したわけではないですが、担当してくれた方がアセットプリコンパイルあたりで嵌り続けていたのがそれを物語っています。

あと1つ大きなメリットが残っています。それは、現状としてwebpackerを使って開発が進んでいるという事実です。触らぬ神に祟りなし、期待通りに動いているから問題ない。実際webpackerに依存した環境で開発を行っているのですから、このメリットについては反論のしようがないです。

独立可能性の証明

webpackerに対する不満は溢れるほどあるのに、それに依存している時点で文句を言うのは筋違いなのではないかという葛藤にケリを付けなければいけない。別にwebpackerなんか使わずとも、素のwebpackでフロントエンド開発環境を構築することなんて造作もないということを見せつけたいというモチベーションが湧いたので実力行使でwebpacker脱出PRを出しました。

たとえrejectされたとしても、「結構不便だし別に無くても全然大丈夫なんだけどwebpacker使っている、あえてね。」と言って心の平穏を保つことができるとても有意義なPRです。

※その後、関係者各位(ERDOS Balintshinya.debuchi)の協力もあって無事マージすることができました!

やったこと

純粋webpack

webpackerから脱出してwebpack環境を構築するまでは、ほぼ上記のPixivさんによる手順紹介を参考に行うことができました。Railsとwebpackで検索してもwebpackerのヒットに邪魔され続ける現状において、とても貴重でありがたい記事です。

特に私がフロントエンド寄りな人間でRailsのことはそれほど深く知っているわけではないので、ヘルパー実装をほぼそのまま使うことができたのはとても助かりました。この記事に敬意を評して、私が属するプロジェクトにて利用できるよう改造した版をここに掲載しておきます。

前提として、ビルドしたフロントエンドのアセットは「プロジェクトルート/public/packs」の中に吐き出すという想定で進めていきます。これはwebpackerを標準設定のまま利用した場合と同じパスです。

純粋webpack-dev-server

上で掲載したヘルパー実装はほぼPixivさんのコードままなのですが、manifest.jsonの取得方法に変更を加えています。これは開発環境では、webpack-dev-server(以下dev-server)からフロントエンドのアセットを取得するためです。当該記事ではdev-serverとの連携については省略されていたので、私が試行錯誤しながらなんとかdev-serverとRailsをいい感じに連携するまで辿り着けた方法を紹介したいと思います。

なおwebpackerではdev-serverを起動していない場合は自動でwebpackを実行してローカルのアセットを読み込んでくれるという機能がありますが、dev-serverを起動せずに開発をするというケースが考えられなかったのでこの機能は再現していません。

dev-server設定

dev-server起動用のwebpack設定はこんな感じになりました。書き方の都合で全体は紹介しにくいので一部を抜粋しています。

output: {
publicPath: '/packs/',
path: public/packsへのパス,
filename: '[name]-[hash].js'
},
/* ~~~ 色々設定 ~~~ */
plugins: [
new ExtractTextPlugin({
filename: '[name]-[hash].css',
allChunks: false
}),
new ManifestPlugin({
fileName: 'manifest.json',
publicPath: '/packs/',
writeToFileEmit: true
})
],
devServer: {
publicPath: '/packs/',
contentBase: public/packsへのパス,
host: '0.0.0.0',
port: 3035,
disableHostCheck: true,
headers: {
'Access-Control-Allow-Origin': '*'
}
}

ExtractTextPluginは、webpackerとVue.jsの単一コンポーネントファイルを使っていた場合は必要なプラグインです。jsとcssを分けてバンドルしておいて下記のように読み込むというのがwebpackerの作法?だったので、それを踏襲しています。vue-loaderもcssを分けて出力してくれるように設定しておきます。

{
test: /\.vue$/,
use: [{
loader: 'vue-loader',
options: { extractCSS: true }
}]
}

erbファイルでもjsとcssをそれぞれ読み込む必要があります。

<%= stylesheet_bundle_tag 'main' %>
<%= javascript_bundle_tag 'main' %>

上記設定を行うと、dev-serverに配置されるファイル構造はこうなります。

localhost:3035
- packs
-- manifest.json
-- main-[hash].js
-- main-[hash].css

このときmanifest.jsonに書かれるアセットパスはRailsサーバー側のホスト( “/” で始まる)が指定されています。当然Rails側にフロントエンドのアセットは無いのでまだ取得できませんが、プロキシしてdev-serverから取得できるように後ほど設定していきます。

publicPath: 'http://localhost:3035/packs/'

また上記のようにすればdev-serverから直接取得することもできますが、その場合はlocalhost環境のブラウザでしかアセットを取得することができなくなります。もしdev-serverのホストを固定で用意できるのであれば別ですが、大抵の開発環境ではRailsとdev-serverは同ドメイン別ポートで起動していると思うので、Railsからプロキシしてdev-serverにアクセスした方が融通が利きます。

ヘルパー実装

def dev_manifest
# webpack-dev-serverから直接取得する
OpenURI.open_uri("#{dev_server_host}/packs/manifest.json").read end

全体はすでに掲載しましたが、dev-server対応で必要なのは上記の箇所です。dev-serverはバンドルしたファイルをメモリ上にしか持たないので、manifest.jsonもdev-serverから取得してきます。

若干非効率ですがこれはdevelopment限定の処理で、productionではファイルに書き出したローカル環境のpublic/packs/manifest.jsonを参照するのでご心配なく。

dev-serverへプロキシ

プロキシ処理を行うためにrack-proxyをgemに追加し、シンプルな実装を行いました。

このプロキシ処理についてはwebpackerの実装を参考にしています。といっても「/packs/」に来たアセット取得リクエストを「localhost:3035/packs/」にただプロキシしているだけです。

development環境でのみプロキシしたいので、development用のconfigファイルで下記のように書いておけばOKです。

config.middleware.use DevServerProxy, ssl_verify_none: true

最初はなんでプロキシなんて必要なんだろうと思っていましたが、vm環境のIE11からブラウジングしてみたらlocalhost:3035にdev-serverが有るわけ無いので見事にエラーとなってしまったわけです。

純粋起動

ここまでくれば後は起動するだけです。bin/webpack-dev-serverという無駄にラップされたファイルを削除し、純粋なwebpack-dev-server起動コマンドを振りかぶって実行しましょう。

yarn webpack-dev-server --progress --color --hot --config development用のwebpack設定ファイル

無事起動したら、Railsが書き出したwebページを表示し、dev-serverのアセットが読み込まれていること、jsを変更したらホットリロードされることなどを確認してフロントエンド独立作業は完了です。

そしてwebpack4へ

かくしてwebpackerの時代は幕を閉じました。webpackの設定と睨めっこできることがこれほど楽しかったのは久しぶりだったかもしれません。これもひとえにwebpackerのおかげでしょう。彼は我々に、大切な何かを教えてくれようとしていたのかもしれません。

しかしこれはまだ序章にすぎません。今回はwebpacker脱出が目的だったので、webpackerがラップしていたwebpack3による既存開発環境は可能な限りそのままにしています。次に行うべきは、webpack4へのアップグレードです。かなり高速化しているという噂も聞きますので、ネックになりつつあるwebpackのビルド時間をどれほど減らしてくれるのかが今から楽しみでなりません。

webpack4にアップグレードしてもまだまだ終わりではありません。改善すべき箇所なんていくらでもありますし、PWAの時代だって既に幕を開けています。やりたいことはいくらでもあるのに全然間に合っていないというのが正直な現状です。つまり…

We are Hiring!

スタディストでは、フロントエンドエンジニアを絶賛募集しています。敷かれたレールの上を走るだけではもの足りない、フロントエンドという荒野のパイオニアを目指す方々をお待ちしております!