はじめまして。
Mountain View導入の経緯
普段 Ruby On Railsでプロダクトを開発しており、gemで完結するCSSのスタイルガイドを探していました。
有名なgemをいくつか素振りしてみたのですが、LivingStyleGuideはスタイルガイドを簡単にカテゴリ分けできず、Hologramはメンテナンスコストが高そうに感じました。
そんな時に、Railsのビューコンポーネントをそのままスタイルガイド化できるMountainViewと言うgemを知りました。
Mountain Viewとは
With Mountain View you create reusable components for your Rails frontend, while generating a living style guide.
Mountain Viewを使用すると、スタイルガイドを生成しつつ、Railsフロントエンド用の再利用可能なコンポーネントを作ることができます。
この記事で紹介すること
紹介しておいてなんですが、私はパフォーマンス上の理由からMountain Viewのコンポーネント機能をそれほど使っていません。
(コンポーネントはRailsのパーシャルに任せれば済む話で、どちらかと言うとスタイルガイドとしての機能が欲しい...)
この記事では私がMountain Viewを導入し、コンポーネントとして使用することを諦め、スタイルガイドとして使用することに落ち着くまでに試行錯誤した内容をまとめています。
javascriptを使ったモダンなフロントエンド開発からは遠い話になりますのでご容赦ください。
導入 ~ スタイルガイドの作成
Rails 5.2.1環境で公式のREADMEだけを参考に導入することができました。
まずは Gemfile
に mountain_view
を追加します。
# コンポーネント機能を利用する場合はグローバルに読み込みます
gem 'mountain_view'
bundle install
を実行し、routes.rb
ファイルに次の行を追加します。
mount MountainView::Engine => "/mountain_view" if Rails.env.development?
下記のコマンドでMountain Viewの新しいコンポーネントを作成します。
rails generate mountain_view:component button
すると、次のようなディレクトリとファイルが作成されます。
app/
components/
button/
_button.html.erb
button.css
button.js
button.yml
拡張子にscssやslimなど、Railsで使っているプリプロセッサを使うことができます。
rails s
でサーバーを立ち上げてみましょう。
ローカル環境からbuttonコンポーネントのスタイルガイドのページが作成されていることが確認できると思います。
http://localhost:3000/mountain_view
i18n対応
i18n対応で日本語化(default_localeをjaに変更)していた場合、Mountain Viewにja.ymlが存在しないためtitleタグの表示で下記のようなエラー出ているかもしれません。
<title><span class="translation_missing" title="translation missing: ja.mountain_view.layout.styleguide_title">Styleguide Title</span></title>
私はこちらの en.ymlファイルの内容をコピペした config/locales/mountain_view.ja.yml
のようなファイルを作成しました。
buttonコンポーネントを作ってみる
ここでは bootstrap
を使用してコンポーネント作りを試してみます。
Gemfile
に以下を追加して bundle install
します。
gem 'bootstrap'
buttonコンポーネントのscssでbootstrapを読み込みます。
@import "bootstrap";
bootstrapのbuttonコンポーネントをMountainViewに反映させた例です。
<button type="button" class="btn <%= properties[:modifier] %>">
<%= properties[:title] %>
</button>
-
:modifier: btn-primary
-
:modifier: btn-secondary
-
:modifier: btn-success
-
:modifier: btn-danger
-
:modifier: btn-warning
-
:modifier: btn-info
-
:modifier: btn-light
-
:modifier: btn-dark
キャッシュの問題
Mountain ViewではCSSの更新やコンポーネントの作成を行った後にキャッシュが残ってしまい、下記のコマンドを実行しないとコンポーネントのデザインがうまく反映されないことがありました。
bin/rake tmp:cache:clear
buttonコンポーネントを使ってみる
定義したコンポーネントはrender_component
メソッドを使用することで使うことができます。
第一引数にコンポーネント名、第二引数にハッシュ値を渡して使います。
<%= render_component("button", { title: "Btn Primary", modifier: "btn-primary" }) %>
コンポーネントにブロックを渡し、properties[:yield]
で読み込んで使用することもできます。
<button type="button" class="btn <%= properties[:modifier] %>">
<%= properties[:yield] %>
</button>
<%= render_component("button", {modifier: "btn-primary" }) do %>
Btn Primary
<% end %>
プレゼンターを使ってみる
コンポーネントの階層に{コンポーネント名}_component.rb
ファイルを追加してMountainViewコンポーネント用のプレゼンターを定義することができます。
app/
components/
button/
_button.html.erb
button.css
button.js
button.yml
+ button_component.rb
MountainView::Presenterを継承して使います。
プロパティのデフォルト値なども使用できます。
class ButtonComponent < MountainView::Presenter
properties :modifier, :title
property :element, default: 'btn'
def modifier_title
title || properties[:modifier].titleize
end
end
定義したメソッドやプロパティをコンポーネントのパーシャルで利用することができます。
<button type="button" class="<%= element %> <%= modifier %>">
<%= title %>
</button>
Railsのコンポーネント管理がMountainViewで完結して最高!
...と思ったのですが、このコンポーネント機能には、後述するパフォーマンス上の問題があるようです。
MountainViewのボトルネック
MountainViewのrender_component機能をeachすると、パフォーマンスがとても残念なことになってしまいます。
これを避けるために公式READMEではMountainViewのプレゼンターでrenderメソッドをオーバーライドする方法を紹介しています。
しかし、個人的にはそれよりも素直にRailsのパーシャルを使用した方がメリットが大きいなのではないかと思います。特にrender_component機能ではパーシャルのコレクション機能が使えないのが痛いので、私はeachする要素でMountain Viewのrender_component機能は使いません。
例えば、Mountain Viewを
<%= properties[:list_item].title %>
こんな風に定義するよりも、Railsのパーシャルで
<%= list_item.title %>
このように定義しておけば、renderをキャッシュしてくれますし、collection機能でn+1対策もできます。
<% @lists = List.all %>
# @listsが100個あったら100回render
<% @lists.each do |list_item| %>
<%= render_component("list_item", {list_item: list_item}) %>
<% end %>
# @listsの結果が100個あっても1回のrenderで済む
<%= render partial: 'components/list_item', collection: @lists, as: :list_item %>
最高のビューコンポーネントはRailsのパーシャルでした...😂
と、言うわけで私はMountain ViewをRailsコンポーネント管理用のスタイルガイドとして割り切って使うことにしました。
MountainViewをスタイルガイドとして使用する
MountainViewはデフォルトではスタイルガイドのHTMLタグが表示されません。
デフォルトで prism.js
が使われているので、views/mountain_view/styleguide/show.html.erbをオーバーライドして、render_componentの記述の上部に下記のコードを追記しただけでスタイルガイドっぽくなってくれます。
...
<div class="mv-component__description__properties" style="margin-bottom: 20px;">
<code class="language-html"><%= CGI::pretty("#{render_component(@component.name, component_stub.properties.clone)}") %></code>
</div>
...
また、MountainViewのサイドメニューがレスポンシブ表現に邪魔なので、メディアクエリで非表示にしてみました。
.mv-main {
@media screen and (max-width: 768px) {
width: 100%;
padding: 30px 0;
}
}
.mv-sidebar {
@media screen and (max-width: 768px) {
display: none;
}
}
イニシャライザでMountain Viewでグローバルに読み込みたいCSSを指定することができます。
MountainView.configure do |config|
config.included_stylesheets = ["mountain_view/layout"]
end
CSS読み込みの問題
MountainViewはディレクトリ名のコンポーネントをmountain_view.css.erbでディレクトリ名と同名のcssをまとめて読み込んでいるようです。
例えばbuttonコンポーネントでbootstrapをimportした後、別のコンポーネントを作ろうとしてみたところ、button.scssでしかimportしていないbootstrapが既に読み込まれています。
MountainViewの動作がCSS設計の方針と違った場合、私はマニュフェストファイルやレイアウトファイルをオーバーライドして調整するようにしています。
プレゼンターをスタブとして使ってしまう
Tipsとして、私は既存のコードをコンポーネント化する際、一旦Mountain Viewのプレゼンターをスタブとして一旦置いてみる方法をとっています。
class PostComponent < MountainView::Presenter
# コンポーネント作りに必要なデータを取得
def current_user
User.find_by(email: 'necessary-user@exapmle.com')
end
end
こんな感じでプレゼンターのメソッドを使うと既存のビューをコンポーネントでリプレースする工程がスムーズにいって便利です。
最後に
Railsのgemで完結するスタイルガイドを探したところ、Mountain Viewを改造しながら使っていくと言う道に辿り着きました。
もっとオススメのgemや、Mountain Viewの便利な使い方をご存知の方がいらっしゃいましたらぜひコメント欄で教えてください...🙏