僕は結構Asset Pipelineの仕組みが好きなんで特にやめちゃいたいみたいな意識はないんですが、 個人プロジェクトだし新しいものを実際に使ってみる経験っていうのも大切かなと。
あと、僕のプロジェクトは依存性の解決だけをyarnがやっていて、yarnで落としている3rd Party系アセットをAsset Pipelineで運用していくやり方に限界を感じていました。
CSSとその他フォントファイル等が共存しているパッケージとか。
現状はそれなりにRailsの基本的な状態です。僕はあんまり色んなものをゴテゴテと入れないタイプなので。
「移行すること」を目的にしちゃうのはあまりにもナンセンスなので、移行後に目指すところと守るべきラインを決めます。
そもそも、現状の環境に困ってるわけではないですし・・・。
Set Up Your Build Tools | Tools for Web Developers | Google Developers
CSSに関する部分だけなので、とりあえず以下だけにしました。
これが守れないならmasterブランチにマージしません。
僕はCSSがでっかくなるのがすごく嫌なタイプなので・・・。とはいえ少しくらい増えるのはOKとします。 10KBとか増えるならNGで。
そんなでかいプロジェクトでもないので、言うほどでもないかと。 FontAwesomeが幅取ってるかもですね・・・。
ビルド
1$ time rails assets:precompile RAILS_ENV=production 2yarn install v1.2.0 3[1/4] Resolving packages... 4success Already up-to-date. 5Done in 1.12s. 6I, [2017-10-10T08:36:05.289046 #5] INFO -- : Writing /myapp/public/assets/bg-b811dc3d5d616bfc91c7be908927f5797db3878ef5eed28e0277ed40f5c2a9ba.png 7 : 8 : 9 : 10I, [2017-10-10T08:36:14.803537 #5] INFO -- : Writing /myapp/public/assets/express/lib/application-489ef282d160b38a75de19f711472bb48c4eca65cecd6e1ed83fceba74dcee35.js.gz 11Webpacker is installed 🎉 🍰 12Using /myapp/config/webpacker.yml file for setting up webpack paths 13Compiling… 14Compiled all packs in /myapp/public/packs 157.71user 2.51system 0:33.19elapsed 30%CPU (0avgtext+0avgdata 125336maxresident)k 160inputs+0outputs (1major+177016minor)pagefaults 0swaps
ファイルサイズ
1$ ll -ltrh public/assets/ 2total 6832 3-rw-r--r-- 1 daisuketsuji staff 12K Oct 8 17:17 application-b190eef5e6c0c69209dc27227adc2168bfe95ae722ed7cc4a011fb96bd1b11ee.css.gz 4-rw-r--r-- 1 daisuketsuji staff 60K Oct 8 17:17 application-b190eef5e6c0c69209dc27227adc2168bfe95ae722ed7cc4a011fb96bd1b11ee.css
処理時間の計測はこうでいいんでしょうか。こういうの詳しくないので誰か教えて・・・。
webpackerは標準設定があるので、まずそれを把握するためにとりあえずREADMEに目を通します。
rails/webpacker: Use Webpack to manage app-like JavaScript modules in Rails
あれ!PostCSS - Auto-Prefixer
って書いてあるな・・・。Autoprefixer、デフォルトで有効ってこと・・・?
追記: cssnextに標準でAutoprefixerが入ってるからです。
こういうののディレクトリ構成はデファクトみたいなのがあると思うんですが、僕はこうしました。
1. 2├── app 3│ ├── assets 4│ ├── helpers 5│ └── javascript 6│ ├── packs 7│ └── stylesheets
application.jsはこう。
1import '../stylesheets/application'
CSSの中身は移行したものではなく、PostCSSを使っていない暫定の記述だけです。とりあえず疎通が見たかったので。
HTMLからの呼び出しはstylesheet_pack_tag
を使えばいけるっぽいんですが、この記事に書いてもしょうがない作業なんで割愛します。
application.css
の中身をPostCSSぽくして、ちゃんと動くかを見てみたいので、
まず、webpackerのCSSに対するローダーがデフォルトでどのようになっているかを確認してみます。
設定はここですかね。
webpacker/style.js at master · rails/webpacker
逆から読むんでしたっけ?これは以下の順番で処理してくれるということなんでしょうか。webpackの設定ファイルの見方がいまいち・・・。
あれ、sassも読めるのかな・・・。まあどちらにせよ、PostCSSのローダーは入っているようなのでこのまま行ってみましょう。
PostCSSの設定はルートに.postcssrc.yml
を置けばいいみたいですね。
1const postcssConfigPath = path.resolve(process.cwd(), '.postcssrc.yml')
webpackerのインストール直後はこんな感じで配置されるようです。
1plugins: 2 postcss-smart-import: {} 3 postcss-cssnext: {}
smart-import
が入ってるので、import
が使えるかを見てみます。
app/javascript/stylesheets/modules
ディレクトリを作って、test.css
を作ってみます。
1.teststyle { 2 color: #eceff1; 3}
application.css
はこんな感じ。
1@import "modules/test";
bin/webpack
を実行すると・・・、
1$ cat public/packs/application-3cb0874e28a4e33e875112e4732c6ddd.css 2.teststyle { 3 color: #eceff1; 4}
行けてそう!
とりあえずどかっとファイル移動しただけでどうなるかを見てみます。SCSSの拡張子もそのままです。
1ERROR in ./node_modules/css-loader?{"minimize":false}!./node_modules/postcss-loader/lib?{"sourceMap":true,"config":{"path":"/myapp/.postcssrc.yml"}}!./node_modules/resolve-url-loader!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./app/javascript/stylesheets/application.scss 2Module build failed: 3@import "modules/*";
あー、まあエラーしたので、章をわけて1個ずつ対応します。
globでのimport
は出来ないんですね。過去にpostcss-import
プラグインがサポートしていたようですが、外されたようです。
Remove glob support · postcss/postcss-import@1fbeca6
postcss-easy-import
というプラグインでできるようですが、まあここで増やすのもな・・・という気もするので、1個1個import
するように書き換えました。
TrySound/postcss-easy-import: PostCSS plugin to inline @import rules content with extra features
この対応だけしたら、普通にbin/webpack
は通るようになりました。
これまではGemで読んでたので、ダイジェストもいい感じにやってくれてました。 webpacker経由でうまいことダイジェストも解決する方法を知らないので、ここで解決しちゃいます。
1npm i font-awesome --save
application.css
には
1@import "~font-awesome/css/font-awesome";
CSSだとフォントファイルにダイジェストがつかなかったりするかな?と思ったら、なんか読み込むだけでうまくいきました・・・。
1$ head -n 20 public/packs/application-b619b7134a47c7bc1d3df67c6b135f76.css 2@charset "UTF-8"; 3 4/*! 5 * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 6 * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 7 */ 8 9/* FONT PATH 10 * -------------------------- */ 11 12@font-face { 13 font-family: 'FontAwesome'; 14 src: url(/packs/_/_/node_modules/font-awesome/fonts/fontawesome-webfont-674f50d287a8c48dc19ba404d20fe713.eot); 15 src: url(/packs/_/_/node_modules/font-awesome/fonts/fontawesome-webfont-674f50d287a8c48dc19ba404d20fe713.eot) format("embedded-opentype"), url(/packs/_/_/node_modules/font-awesome/fonts/fontawesome-webfont-af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"), url(/packs/_/_/node_modules/font-awesome/fonts/fontawesome-webfont-fee66e712a8a08eef5805a46892932ad.woff) format("woff"), url(/packs/_/_/node_modules/font-awesome/fonts/fontawesome-webfont-b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"), url(/packs/_/_/node_modules/font-awesome/fonts/fontawesome-webfont-912ec66d7572ff821749319396470bde.svg) format("svg"); 16 font-weight: normal; 17 font-style: normal; 18} 19 20.fa { 21 display: inline-block;
これはなんかのローダーがやってくれてるんでしょうね。ちょっとその辺は追ってませんが、すごいね・・・。
とりあえずビルドは通ったので、ここでちょっと計測してみます。 変数展開とかが全然できてないはずなので、まだ完成じゃないですが・・・。
ビルド
1$ time rails assets:precompile RAILS_ENV=production 2yarn install v1.2.0 3[1/4] Resolving packages... 4success Already up-to-date. 5Done in 1.06s. 6I, [2017-10-10T08:45:22.160159 #6] INFO -- : Writing /myapp/public/assets/bg-b811dc3d5d616bfc91c7be908927f5797db3878ef5eed28e0277ed40f5c2a9ba.png 7 : 8 : 9 : 10I, [2017-10-10T08:45:32.132392 #6] INFO -- : Writing /myapp/public/assets/express/lib/application-489ef282d160b38a75de19f711472bb48c4eca65cecd6e1ed83fceba74dcee35.js.gz 11Webpacker is installed 🎉 🍰 12Using /myapp/config/webpacker.yml file for setting up webpack paths 13Compiling… 14Compiled all packs in /myapp/public/packs 1519.57user 11.70system 2:20.05elapsed 22%CPU (0avgtext+0avgdata 238252maxresident)k 160inputs+0outputs (16major+276827minor)pagefaults 0swaps
ファイルサイズ
1$ ll -ltrh public/packs/ 2total 296 3-rw-r--r-- 1 daisuketsuji staff 125B Oct 8 17:47 application-d90f6dc35a6c073d4a45066d53e72dd6.css.map 4-rw-r--r-- 1 daisuketsuji staff 18K Oct 8 17:47 application-d90f6dc35a6c073d4a45066d53e72dd6.css.gz 5-rw-r--r-- 1 daisuketsuji staff 102K Oct 8 17:47 application-d90f6dc35a6c073d4a45066d53e72dd6.css 6drwxr-xr-x 3 daisuketsuji staff 102B Oct 8 17:47 _/
比較
before 7.71user 2.51system 0:33.19elapsed 30%CPU (0avgtext+0avgdata 125336maxresident)k
after 19.57user 11.70system 2:20.05elapsed 22%CPU (0avgtext+0avgdata 238252maxresident)k
あれー!致命的なほど遅くなった!source mapも出しちゃってるからかな・・・。
このままでは失敗に終わってしまう!足りないプラグインを足しながら設定を見直します。
全然知識がないのでとりあえず調べてみる。
devtool: "eval" has the best performance, but it only maps to compiled source code per module. In many cases this is good enough. (Hint: combine it with output.pathinfo: true.)
ふむふむ。source mapは一旦いっか。
1$ time rails assets:precompile RAILS_ENV=production 2yarn install v1.2.0 3[1/4] Resolving packages... 4success Already up-to-date. 5Done in 0.92s. 6I, [2017-10-10T12:34:02.467861 #6] INFO -- : Writing /myapp/public/assets/bg-b811dc3d5d616bfc91c7be908927f5797db3878ef5eed28e0277ed40f5c2a9ba.png 7 : 8 : 9 : 10I, [2017-10-10T12:34:12.881463 #6] INFO -- : Writing /myapp/public/assets/express/lib/application-489ef282d160b38a75de19f711472bb48c4eca65cecd6e1ed83fceba74dcee35.js.gz 11Webpacker is installed 🎉 🍰 12Using /myapp/config/webpacker.yml file for setting up webpack paths 13Compiling… 14Compiled all packs in /myapp/public/packs 1521.31user 10.99system 2:34.00elapsed 20%CPU (0avgtext+0avgdata 216200maxresident)k 160inputs+0outputs (16major+279629minor)pagefaults 0swaps
む、むしろ遅くなった・・・。
source mapの設定はいったんデフォルトに戻して、違う原因を探します。
application.scss
を削っていったら改善されたので、どうやら@import
の数か、純粋に処理してるファイル量に応じて遅くなっているらしい・・・。
SCSSをそのまま移行したのがだめだったのだろうか?
1$ bin/webpack 2Hash: 3a5ad03cf822d8759fb0 3Version: webpack 3.6.0 4Time: 90548ms
90548ms!
とりあえずSCSSをCSSに変えてみよう。 拡張子を変えて、処理できるようにいくつかプラグインを追加します。
1npm i postcss-simple-vars --save // 変数宣言を$でやれるようにする 2npm i postcss-mixins --save // mixin機能 3npm i postcss-extend --save // extend機能 4npm i postcss-nested --save // ネスト記法
.postcssrc.yml
にも。
1plugins: 2 postcss-smart-import: {} 3 postcss-mixins: {} 4 postcss-extend: {} 5 postcss-nested: {} 6 postcss-simple-vars: {}
.postcssrc.yml
は記述の順番がシビアみたいです。とりあえず上の状態では動きました・・・。
mixinについてはちょっと記述の修正が必要でした。
1@define-mixin mixin-name $arg1: 1rem { 2 size: $arg1; 3} 4 5.test-class { 6 @mixin mixin-name 2rem; 7}
これで実行してみると・・・。
1$ time bin/webpack RAILS_ENV=production 2Hash: efdeaedf276a411fa522 3Version: webpack 3.6.0 4Time: 9356ms
お!10分の1に!
assets:precompile
は・・・。
1$ time rails assets:precompile RAILS_ENV=production 2yarn install v1.2.0 3[1/4] Resolving packages... 4success Already up-to-date. 5Done in 0.91s. 6I, [2017-10-11T01:19:52.825045 #5] INFO -- : Writing /myapp/public/assets/bg-b811dc3d5d616bfc91c7be908927f5797db3878ef5eed28e0277ed40f5c2a9ba.png 7 : 8 : 9 : 10I, [2017-10-11T01:19:53.612881 #5] INFO -- : Writing /myapp/public/assets/application-e7c2d6880942eb7309f8eb703c4db21e2d3a26d5b1f7a3895fb5593afe618704.js.gz 11Webpacker is installed 🎉 🍰 12Using /myapp/config/webpacker.yml file for setting up webpack paths 13Compiling… 14Compiled all packs in /myapp/public/packs 1513.00user 4.80system 0:58.08elapsed 30%CPU (0avgtext+0avgdata 222560maxresident)k 160inputs+0outputs (17major+259560minor)pagefaults 0swaps
比較
before 7.71user 2.51system 0:33.19elapsed 30%CPU (0avgtext+0avgdata 125336maxresident)k
after 13.00user 4.80system 0:58.08elapsed 30%CPU (0avgtext+0avgdata 222560maxresident)k
まだ移行前より遅いけど、だいぶよくなった!もうちょっと頑張ればこえられるかな・・・。一旦は許容範囲ということにしましょう。
ちなみにsource mapを出さないようにしてみると・・・?
1$ time rails assets:precompile RAILS_ENV=production 2yarn install v1.2.0 3[1/4] Resolving packages... 4success Already up-to-date. 5Done in 0.89s. 6I, [2017-10-11T01:25:08.843047 #5] INFO -- : Writing /myapp/public/assets/bg-b811dc3d5d616bfc91c7be908927f5797db3878ef5eed28e0277ed40f5c2a9ba.png 7 : 8 : 9 : 10I, [2017-10-11T01:25:09.646634 #5] INFO -- : Writing /myapp/public/assets/application-e7c2d6880942eb7309f8eb703c4db21e2d3a26d5b1f7a3895fb5593afe618704.js.gz 11Webpacker is installed 🎉 🍰 12Using /myapp/config/webpacker.yml file for setting up webpack paths 13Compiling… 14Compiled all packs in /myapp/public/packs 1513.90user 4.23system 0:58.81elapsed 30%CPU (0avgtext+0avgdata 228544maxresident)k 160inputs+0outputs (17major+258170minor)pagefaults 0swaps
全然はやくならん!やり方違うのかな・・・。
ファイルサイズはかわってなさげ。これはいいね。
1-rw-r--r-- 1 daisuketsuji staff 12K Oct 9 10:26 application-c22dd87ef356a17cda53b66b25a442e0.css.gz 2-rw-r--r-- 1 daisuketsuji staff 60K Oct 9 10:26 application-c22dd87ef356a17cda53b66b25a442e0.css
他に参考にした資料。
Optimising build performance, initial: 40s, incremental: 6s · Issue #1574 · webpack/webpack
webpackでCSSを処理するようになったので、これまでのようにrails s
してれば一緒に処理される〜ってことがなくなりました。
(追記 : これ、処理されるみたいです。ただ常駐しないので毎回webpackが立ち上がって・・・って感じの挙動で遅かったです。)
bin/webpack-dev-server
をrails s
と並行して起動する必要があるので、公式も推奨しているForemanを使ってプロセス管理をします。
開発環境はシンプルに保ちたいので、この工程が入るなら導入しなくてもいいかなと思うくらいのハードルでした・・・。
webpacker/env.md at master · rails/webpacker
とりあえずProcfileを作ります。ローカル用に分けたかったので、Procfile.local
にしました。
1rails: bundle exec rails s -p 3005 -b '0.0.0.0' 2webpack: bin/webpack-dev-server
Foreman自体も入れましょう。
1group :development do 2 : 3 : 4 gem 'foreman' 5end 6
あとは実行するだけ。
1foreman start -f Procfile.local
さて、ここらへんで今後の運用も考えて、PostCSSにはどんなプラグインがあって、どんなのを入れたいかを考えてみようと思います。 導入しないにしても、知っておく必要はあると思うので。
とりあえず移行した!だけだとただのやってみた記事になるし、そもそももったいないので。
以下から探せるんですが、
PostCSS.parts | A searchable catalog of PostCSS plugins
一覧性が悪かったので、以下をざーっと眺めてみました。
postcss/plugins.md at master · postcss/postcss
CSS4のやつとかは見てて面白いですね。
まずは目的の1つであったAutoprefixerから。
npm i autoprefixer --save
なんか、どのブラウザを対象にするかのジャッジがちょっとむずかしいらしいですね。 デフォルトだと結構バサッと切り捨てるようなので、一旦そのままにしてあとでちゃんとドキュメントを読んでみようと思います。
これ、僕普段すごく使うので入れなきゃいけませんでした。忘れてた。
seaneking/postcss-hexrgba: PostCSS plugin that adds shorthand hex methods to rgba() values
rgba(0, 0, 0, .5)
みたいなのを、rgba(#000, .5)
みたいに書けるようにするやつ。
npm i postcss-hexrgba --save
画像のサイズを自動的にwidth、heightに設定してくれるもの。これはすごいですね!
導入はしなかったけど覚えておこう。
これはどちらにしろ入れる方針だったので、最後にやっちゃいます。
1npm i stylelint --save
なんかCLIでも動くようにできるみたいですが、せっかくだしPostCSSの処理に組み込んじゃいます。
僕はCSSのlintはsmacssのソート順になっていることだけをチェックできてれば満足なので、以下の設定を使います。
1npm i stylelint-config-property-sort-order-smacss --save
.stylelintrc.yml
はこんな。
1extends: stylelint-config-property-sort-order-smacss
stylefmtとやらを使えばauto correctもできるみたいなので、今度やってみよう。
長くなったので整理します。
約30秒も遅くなっちゃいました。悔しいけど妥協します。
17.71user 2.51system 0:33.19elapsed 30%CPU (0avgtext+0avgdata 125336maxresident)k 213.67user 7.65system 1:00.65elapsed 35%CPU (0avgtext+0avgdata 226164maxresident)k
これはAutoprefixerも入った状態のものです。1KB増えましたね。許容範囲です。
1-rw-r--r-- 1 daisuketsuji staff 60K Oct 8 17:17 application-b190eef5e6c0c69209dc27227adc2168bfe95ae722ed7cc4a011fb96bd1b11ee.css 2-rw-r--r-- 1 daisuketsuji staff 61K Oct 9 14:43 application-a390b9b8bda3b3036f4273c37d529aba.css
1{ 2 "name": "myapp", 3 "private": true, 4 "dependencies": { 5 "@rails/webpacker": "^3.0.2", 6 "autoprefixer": "^7.1.5", 7 "font-awesome": "^4.7.0", 8 "postcss-extend": "^1.0.5", 9 "postcss-hexrgba": "^1.0.0", 10 "postcss-mixins": "^6.1.1", 11 "postcss-nested": "^2.1.2", 12 "postcss-simple-vars": "^4.1.0", 13 "stylelint": "^8.2.0", 14 "stylelint-config-property-sort-order-smacss": "^2.0.0" 15 }, 16 "devDependencies": { 17 "webpack-dev-server": "^2.9.1" 18 } 19}
1plugins: 2 postcss-smart-import: {} 3 postcss-mixins: {} 4 postcss-nested: {} 5 postcss-extend: {} 6 postcss-simple-vars: {} 7 postcss-hexrgba: {} 8 autoprefixer: {}
stylelintはこのプロセスに入れちゃうとnode_modulesが処理されちゃって除外の仕方がわからなかったので、CLIでやるようにしました・・。
エディタ上では効くので、まあCIで弾ければいいかなと。
せっかくなので、JSと画像系アセットもwebpack側に寄せようかなと。 そしたらAsset Pipelineがいらなくなるので、少しは早くなるかな・・・。