熱海・伊豆旅行記 2/3 望水編 絶景の海!日の出が見れる旅館!
熱海・伊豆旅行記 2つ目です。
伊豆北川駅
MOA美術館を見た後は熱海駅に戻りそこから伊豆北川駅まで電車移動。
1時間電車に揺られて到着。
そこは無人駅でした。
駅からの眺め
駅では猫がお出迎え
ただしエサ厳禁
駅から旅館までは10分ほど歩きます。
こんな道を下って、海岸線を歩きます。
弧を描いた海岸線なので端からでも目的地の宿が見えてる
望水到着
やっとこさ到着。
この時点で4時くらいになってたと思う。
宿泊する宿は望水というお宿です。
でかい・・・思った以上に。
8階建てなので旅館?にしてはホテルっぽい感じもある。
海岸線の方が入り口かと思ったらこっちは裏口で、正面入り口はどうやら8階らしい。
部屋、ラウンジ
一度8階のラウンジに通された後チェックインを済ませたらお部屋に通されました。
今回は7階のお部屋。
確か一番お安いスタンダードなお部屋だったと思う。
お部屋に通された後はお茶と菓子
温泉
お部屋で少し休んだ後は温泉へ
温泉を上がると湯上りサロンというものがあり、そこでところてんが食べられます
めちゃくちゃ美味しかったところてん
行くことがあれば絶対食べてほしい
わさび味が絶妙にさっぱりしていて最高だった
夕食
温泉で休んだ後はお部屋食
どれもこれも美味しかった・・・!
食べた後はプライベートガゼボへ
プライベートガゼボ
望水ではプライベートガゼボという特別なお風呂が用意されています。
宿泊者には1回50分が無料でついています。
追加料金払えば回数を増やせるみたいですね。
お部屋に着いた時にいつ入るか聞かれていたので夕食の後の時間でお願いしました。
準備ができたら部屋に電話がかかってくるので向かいます。
そんなお風呂があるんだ〜くらいで向かったら凄かった。
なんだろう・・・完成された空間だった。
2019/10/22 伊豆北川温泉の旅館、望水のプライベートガゼボ pic.twitter.com/RrVOprAmKg
— 山口さんちのいがにん (@igayamaguchi) November 23, 2019
この空間で裸になって風呂入ったり、バスローブに包まれてゆったりしたりを繰り返してすごく贅沢で幸せな時間だったw
夜の散策
ガゼボを堪能した後は夜の散策に。
8階の正面入り口の看板
夜の望水
ラウンジでチョコとドリンクが振舞われたり
1日目満喫できました。
おやすみなさい〜。
2日目は日の出から
望水の大きな特徴として絶対あげたいものが日の出コールというものがあります。
スタッフに伝えておくと日の出が出る時間に部屋に電話して起こしてくれるというもの。
この電話をしてくれることで宿から日の出を眺めることができ最高です。
雲などで隠れて日の出が見れない日はそのままお電話しないとのこと。
今回は運よく日の出が出てお電話が!
日の出が最高に綺麗で良かった!
これが一番いい体験でしたね。
2019/10/23 伊豆北川温泉の日の出 pic.twitter.com/6PdqCR9m11
— 山口さんちのいがにん (@igayamaguchi) November 23, 2019
ラウンジからの日の出
部屋から
日の出を浴びる望水
日の出を浴びる伊豆北川温泉
台風の影響で入れなかった黒根岩風呂
朝風呂
朝風呂に入った後はまた湯上りサロンで軽食(温泉は人がいたので写真は撮れなかった)
朝食
朝食ももちろん美味。
海苔が食べ放題でめっちゃ食べてしまった
チェックアウト
日も上がり切ったので部屋からは綺麗な海が見えてまたいい景色
2019/10/23 伊豆北川温泉の旅館、望水からの眺め pic.twitter.com/MVSSA4ZVSh
— 山口さんちのいがにん (@igayamaguchi) November 23, 2019
ラウンジからも綺麗
そんなこんなでチェックアウトを済ませて望水、伊豆北川温泉を後に。
2019/10/23 伊豆北川温泉の海岸とカモメ pic.twitter.com/zT3ypiEg3i
— 山口さんちのいがにん (@igayamaguchi) November 23, 2019
充実しすぎ、写真多すぎでめちゃくちゃ長くなってしまった・・・これは是非足を運んで欲しい、また行きたい宿でした。
次は城ヶ崎海岸へ。
熱海・伊豆旅行記 1/3 熱海探索編 MOA美術館を勧めたい!
つい先週に天皇即位が行われるための祝日とその次の日にお休みをとって熱海と伊豆に旅行に行ってきました。
めちゃくちゃ良かったのでいろんな方にお勧めしたいのと、自分の記録として残しておきます。
1日目
2日目
- 宿からの日の出
- 城ヶ崎海岸
- 熱海サンビーチ
ってな感じで行ってきました。
長くなりそうなので記事分けてます。
熱海駅でしらす丼
何はともあれ移動ですね。
新幹線で行ったのですが40〜50分くらいで東京から熱海まで行けてしまうんですよね、新幹線早い。片道も5000円弱。
ささっと駅着いてちょうどお昼くらいの時間だったので商店街に。
結構人もお店もいっぱいで栄えてる感じだった。
新幹線の移動中に何が美味しいかなーと思ってネットで調べていたらこのしらす丼のお店が出てきたのでここに。
お洒落なカフェ?なんだけどしらす丼がとても美味しかった〜〜〜!
自分が頼んだのは2食丼で1200円。
MOA美術館へ
熱海にはMOA美術館というものがあり、そこが山の上に建っていることもあり景色が最高らしいと。
熱海駅からMOA美術館へはバスで行くことができます。
バスは20分間隔くらいなので乗り遅れないよう気をつけて停留所に。
バスに7分ほど揺られると到着。
チケットはネットで買っておいたのでチケット売り場に並ぶ方々を横目に入場。
入場するとオシャレ空間が始まります。
この階段が数個続いた先の虹色の空間を抜けると一度外に。
そこで初めて美術館の全体を見ることができます。
この景色を振り返ると海!これはたまらない!!
立ち位置によっては熱海城や熱海の海岸を眺めることができます。
展示品は歴史的なもの、美しいものと色々ありこの空間を味わうだけでも大満足。
展示の順路入口
お洒落オブジェ
平安時代から江戸時代まで幅広い巻物や道具が展示されていたり
この展示のガラスは映り込みが少ないように工夫されているらしく写真を撮っていても目の前で見ていてもガラスを忘れるようになっています。
展示を一通り見た後は美術資料のある建物の裏にある茶の庭に行ってみました。
綺麗な道を抜けると
お洒落な食事処があったり
当時の暮らしを再現した建物があったり
こんな感じでMOA美術館を満喫してきました。
2時間くらい見ていた気がする。
すっごい楽しかった。
そこからまた熱海駅に戻って旅館に。
次に続きます。
ParcelとFirebase Hostingで簡単サイト公開
ParcelとFirebase Hostingを使ってみて今はこんなに簡単にサイトが作れるんだと感動したので紹介。
先週こんなサイトを公開した。
ただの悪ふざけで作ったサイト。
平成のカウントアップなのにreiwa-count-upというサブドメインにしたり。
ちょうど令和100日目にこんなツイートをして公開した。
本日はなんと令和100日目!!!
— 山口さんちのいがにん (@igayamaguchi) August 7, 2019
100日目を記念してサイトをつくりました!!!!
令和最高!!!https://t.co/UqxThBsmGP
このサイトはParcelとFirebase Hostingを使用して開発している。
Parcelすっごく簡単
Parcelは簡単に言ってしまえばWebpackの簡易版。
指定のHTMLをエンドポイントにHTML、CSS、JSを良しなにまとめてくれる。
例えば以下のHTMLをparcelでビルドするとmain.jsを main.4217b2e0.js
みたいな感じのファイルとして生成してリンクを張り替えてくれる。
<html> <body> <script src="main.js"></script> </body> </html
JS内での他ファイルのimportももちろん対応している。
import './style.css'; import hoge from './hoge'; hoge();
試していないがDynamic importsなんかも対応しているとのこと。
npm install --save-dev parcel-bundler
でインストールしnpm scriptでparcel index.html
で開発用のサーバーが立ち上がる。
標準はdistというディレクトリに出力される。これは-d
オプションで出力ディレクトリを変更可能。
開発ではなく本番用のビルドをするにはparcel build index.html
。
Parcelの気になったところ
Mochaとの組み合わせ。
テストをするときにParcelでビルドされたものをテストする方法が分からなかった。
なのでテストファイルに書いたimportがそのままでは解決できず、Parcelでは必要ない.babelrcを設定する必要が出た。
こんな感じのnpm scriptを用意して、.babelrcを用意してテストを走らせる必要があった。
"test": "cross-env NODE_ENV=test mocha --require babel-register src/js/*.spec.js",
{ "presets": ["es2015"] }
あとは特定の画像などをハッシュ付きにしないで出力ディレクトリに出力する方法が分からなかった。
コピープラグインなどあるようだが、Parcelだけだと出来なそう?
SNS用のOGP画像などは https://reiwa-count-up.web.app/hei.png といったメインでの絶対パス指定になるのでhashがついたパスで出力されるとそこがうまく解決できなかった。
なので同じ画像を出力先ディレクトリに保存しておく運用にしている。
buildした後はhei.png
とhei.[hash].png
が存在する形になっている。
これはsitemap.xmlなども同じで出力ディレクトリに保存している。
Parcelについてもっと知りたかったらこの記事を見るといいかも。
よくまとまっている。
細かい設定が出来ないが、すごくシンプルなのでプロトタイプや小規模なものをサクッと作るのに良さそう。
Firebase Hosting色々便利
ドメインもらえる!
Firebase自体もちゃんと使うのは初めてで、Hostingを使ってみたがこれがすごい。
まずドメインが貰える。
正確には[プロジェクト名].web.app
と[プロジェクト名].firebase.com
というドメインをもらうことが出来る。
しかもHTTPSにも対応しているのでそのまま使うことが出来る。
凄くいいなと思ったのはxxx.web.app
というドメインが設定できること。
なんかアプリ感あるドメインぽくてよくない?
今までHerokuもそうだけどPaaSの製品名がドメインに入っているのが普通だったけど、ちゃんと普通のサイトっぽいドメインをそのまま使えるのはいい。
最初は独自ドメイン当てようかなと思ってたんだけどこんなちょっとしたサイトでドメイン買うのもなーと思っていたら、このドメインだったのでこれはいいと思ってそのまま使った。
CLIでデプロイ簡単、バージョン管理も
プロジェクトディレクトリでfirebase init
コマンドを叩くとfirebaseプロジェクトとして設定を行うことができる。
どの機能を使うか(HostingやFirestoreなど)、プロジェクト名などの設定が簡単にできる。
設定後はfirebase deploy
で即デプロイが可能。
設定ファイルも以下のような数行で終わる程度。
{ "hosting": { "public": "dist", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ], "rewrites": [ { "source": "**", "destination": "/index.html" } ] } }
デプロイするとバージョニングがされ、指定日時にロールバックも可能。
いや~、本当に便利だ。
値段も相当バズったり動画や画像を置きまくらない限り無料プランで完全に賄える。
余談
Google Search Console反映遅すぎ。
3週間くらいかかった。
sitemap.xmlの反映がうまくいってなかったのが原因かもしれないけど。
JavaScriptで指定のスタイルが当たっている要素を探す
JavaScriptで指定の要素に適用されているスタイルや、特定のスタイルが適用されている要素を探したいときってあるよね。
そんなときに簡単にとろうと思ってJSでコードを書いてみると、
document.getElementById('hoge').style
こんなコード書けばとれるんじゃないかと。
で、オブジェクトが返ってくるので中身を見てみるとCSSに書いたスタイルが取れてないんですよ。
この記法だと要素に直接記述されたスタイルしか取れない。
結論、要素に書いたスタイル、それ以外のスタイルも含めて要素に適用されているスタイルをとるならgetComputedStyleを使おうぜって話。
const elm = document.getElementById('hoge'); const styles = getComputedStyle(elm);
これでCSSも含めてすべてのスタイルが適用された要素のスタイルがCSSStyleDeclarationという型のオブジェクトで返ってくる。
スタイルはそれぞれケバブケースからキャメルケースにした名前でその値が取得できる。
const elm = document.getElementById('hoge'); const style = getComputedStyle(element).backgroundColor; // rgb(0, 0, 0)
注意として色をblack
や#000
としている場合にrgbに変換された値で出てくるので比較するならそれを配慮しなければいけない。
指定の要素から特定のスタイルが当たっている先祖要素を探す
再帰的に検索してあげればいい。
子孫要素から探すなら逆に再帰関数を書けばいける。
/** * @param {HTMLElement} element * @param {string} styleName * @param {string} styleValue */ function findHasStyleAncestorElement(element, styleName, styleValue) { if (!element) return null; const style = getComputedStyle(element)[styleName]; if (style === styleValue) return element; return findHasStyleAncestorElement( element.parentElement, styleName, styleValue ); } const elm = document.getElementById("c"); const blackBackgroundElm = findHasStyleAncestorElement( elm, "backgroundColor", "rgb(0, 0, 0)" // blackではない );
Dapperでトランザクションを使用、トランザクション分離レベルを変更する方法のメモ
Dapperでトランザクションの分離レベルを変更するときにどういう挙動をするのかよく分かっていなかったので調べたメモ。
ついでにトランザクション周りのSQLも確認。
今回の環境
今回試しているのはSQLServerだが他のRDBでも方言の違いはあれど基本的に同じなはず。
SQL
まずSQLの確認から。
トランザクションの分離レベルの設定。 Serializableであれば下記。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
分離レベル オプションは一度に 1 つだけ設定でき、設定したオプションは明示的に変更されない限り、その接続で継続的に使用されます。 ステートメントの FROM 句内にあるテーブル ヒントで、テーブルに対して別のロック動作やバージョン管理動作が指定されない限り、トランザクションのすべての読み取り操作は、指定した分離レベルのルールに従って実行されます。
公式ドキュメントを見ると接続単位でトランザクション分離レベルが決まるらしい。
実際に今の分離レベルを確認するなら。
DBCC USEROPTIONS
このSQLを実行するとisolation levelの項目に今の分離レベルが表示される。
SSMSで確認すると切断、再接続をしてもずっと変更後の分離レベルになっているのでここらへんの挙動ははっきりわからない。
恐らくセッションが生きているようで、SSMS自体を再起動すると規定値に戻る。
MySQLで分離レベルを確認する場合はこんな感じらしい。(未検証)
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
実際に分離レベルを設定してトランザクションを張るにはBEGIN TRAN、コミットはCOMMIT TRAN。
BEGIN TRAN /****** 実行する処理 ******/ COMMIT TRAN
実際にはSQLで全部やるならTRYCATCHしてROLLBACKの処理が入るだろう。
Dapper
次にアプリケーション側でどう書くか。
今回はDapperを使って書いてみる。
まずはusingでSqlConnection.BeginTransactionを呼び出す。
これでトランザクションを張ることが出来る。
BeginTransactionは第一引数で分離レベルを設定することが出来る。
実際に実行するコードはこんな感じに。 このSQLだと分離レベルを変える意味はないがご愛嬌。
using (var tran = conn.BeginTransaction(IsolationLevel.Serializable)) { var sql1 = @" INSERT INTO Samples ( Id, Name ) VALUES ( @id, @name )"; var sql2 = @" INSERT INTO Samples ( Id, Name ) VALUES ( @id, @name + 'duplicate' )"; try { await conn.ExecuteAsync(sql1, new { id = sample.Id, name = sample.Name }, tran); await conn.ExecuteAsync(sql2, new { id = sample.Id, name = sample.Name }, tran); tran.Commit(); return true; } catch (SqlException e) { // log出力とか return false; } }
usingでBeginTransactionを囲っておけばこのコードが終わった時にコミットされていなければロールバックしてくれる。
ここで分離レベルを指定しなかった場合、Dapperで実行するとそのコネクションプールが保持されている限り、その接続で前に使用されたトランザクションレベルが使用されてしまう。
・・・と思ったけどDBCC USEROPTIONS
の結果は固定化されているが using (var tran = conn.BeginTransaction())
したときは一番最初のトランザクションレベルとなっている。
公式ドキュメントにはこのように記載があった。
分離レベルを指定しない場合は、既定の分離レベルが使用されます。 分離レベルを指定する、BeginTransactionメソッドを受け取るオーバー ロードを使用して、isoパラメーター (BeginTransaction)。 トランザクションの分離レベルは、接続が終了または破棄されるまで、トランザクションが完了した後に永続化します。 分離のレベルを設定スナップショットsnapshot 分離レベルが有効になっていないデータベースではない例外をスローします。 既定の分離レベルを使用して、トランザクションが完了します。
日本語訳が変だけど、以下も併せて読んでみるとどうやらREAD COMMITTEDがSQLServerでのデフォルトでになっており、それが使用されるらしい。
ので、分離レベルはこんな感じに切り替わる。
using (var conn = CreateConnection()) { var t1 = conn.Query("DBCC USEROPTIONS"); // 前のトランザクションレベルが表示される conn.Execute("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"); // BeginTransactionでは意味なし using (var tran = conn.BeginTransaction()) { var t2 = conn.Query("DBCC USEROPTIONS", new { }, tran); // READ COMMITTEDが表示、既定のトランザクションレベル var sql1 = @" INSERT INTO Samples ( Id, Name ) VALUES ( @id, @name )"; var sql2 = @" INSERT INTO Samples ( Id, Name ) VALUES ( @id, @name + 'duplicate' )"; try { await conn.ExecuteAsync(sql1, new { id = sample.Id, name = sample.Name }, tran); // READ COMMITTEDで実行される await conn.ExecuteAsync(sql2, new { id = sample.Id, name = sample.Name }, tran); tran.Commit(); return true; } catch (SqlException e) { // log出力とか return false; } } } }
trasactionの引数でちょっとした罠があり、パラメータがなくてもQueryAsyncの第二引数に匿名オブジェクトを入れて、第三引数にトランザクションを入れなければいけない。
そうしないとIDbTransactionをオブジェクトとして認識してただのクエリに入れるパラメータと判断し、以下のエラーが出る。
InvalidOperationException: ExecuteReader requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.
分離レベルを変えて、Serializeレベルで行うならこんな感じになる。
using (var conn = CreateConnection()) { var t1 = conn.Query("DBCC USEROPTIONS"); // 前のトランザクションレベルが表示される using (var tran = conn.BeginTransaction(IsolationLevel.Serializable)) // 変わる箇所 { var t2 = conn.Query("DBCC USEROPTIONS", new { }, tran); // Serializableが表示 var sql1 = @" INSERT INTO Samples ( Id, Name ) VALUES ( @id, @name )"; var sql2 = @" INSERT INTO Samples ( Id, Name ) VALUES ( @id, @name + 'duplicate' )"; try { await conn.ExecuteAsync(sql1, new { id = sample.Id, name = sample.Name }, tran); // Serializableで実行される await conn.ExecuteAsync(sql2, new { id = sample.Id, name = sample.Name }, tran); tran.Commit(); return true; } catch (SqlException e) { // log出力とか return false; } } }
実行したソースはこちら。
https://github.com/igayamaguchi/OrmExample/tree/master/InsertDuplicate
調べている上で知ったこと
SQLServerへの接続を切る方法。
クエリ発行をするセッションは切れない。
切断。
KILL [セッションID]
セッション確認。
SELECT conn.session_id, host_name, program_name, nt_domain, login_name, connect_time, last_request_end_time FROM sys.dm_exec_sessions AS sess JOIN sys.dm_exec_connections AS conn ON sess.session_id = conn.session_id;
プライマリーキー、一意性違反の検知
SqlExceptionのNumberに2627という数字が入る。
これはSQLServerの重複違反のエラーコード。
使用するデータベースによって数字は異なる。
public enum CreateResult { Success, Duplicate } try { await conn.ExecuteAsync(sql, new { id = sample.Id, name = sample.Name }, tran); tran.Commit(); return CreateResult.Success; } catch (SqlException e) when (e.Number == 2627) { // log出力とか tran.Rollback(); return CreateResult.Duplicate; }
ORMの使う使わないについて
- とあるコミュニティでORMを使う使わない、使い方の議論があった
- 自分の考えを整理するためにも今自分がこうしたほうがいいなって運用方法を書き出してみる
- 個人の好みによるものが大きいので一意見として
- ここではサーバーアプリケーションでの文脈
ORMってそもそも何
前提知識としてORMとは
- インピーダンスミスマッチを解消するもの
- インピーダンスミスマッチとは
- 概念モデルと論理モデルの違いを埋める
- アプリケーションの要件にそった概念モデル(Entity)は、論理モデル(テーブル)と1対1になるとは限らない
- こういったDB側の都合をアプリケーションで意識しないで済むようになるもの
ORMを使うか使わないか
でもDB側の都合を意識しないで済むって言うけど結局パフォーマンスとか考えたら意識しなくちゃいけないよね?
で、ORM使ってWhereとか書き始めたらそれSQL書いているだけだよね?ORMいらなくない?
っていうのが今回のORM使う使わない問題の根幹としてあると思う
サーバーアプリケーションからDBの値を引く
サーバーアプリケーションからプログラムを書いて、DBから値を読み込み、アプリケーションで使用する、その方法は大体こんな感じだと思う
個人的意見
個人の好みによる ORMでクエリビルダーを有効活用するのが自分の最適解
SQLを直接書いてORMを用いずマッピング
ここからは各方法を実際のコード(C#)で良いところ、悪いところを挙げていく
ORMを使用しないマッピングの場合、
var sql = $@" SELECT USER.NAME ARTICLE.TITLE FROM USER INNER JOIN ARTICLE ON ARTICLE.ID = USER.ID WHERE ID = @id "; return Connection.Query(sql, new { id }); // 上の呼び出し元 foreach(result in results) { var name = user["Name"] var title = user["Title"] // 何かの処理 }
良いところ
- クエリを直接かけるのでハイパフォーマンス
- 特別構文解釈の負荷もかかりにくい
悪いところ
- 取り回しが悪い
そのままSQLだからしっかりデータベースのことを考えてチューニングできるのでいい
でもビジネス要件に耐えられるものではないかなあと思ってしまう
上記は単純なSQLだが複雑度が増し、その複雑度の増したSQLが画面やAPIによって若干の条件が変わるごとに同じSQLを書くことになったりして辛くなるのでは
サーバーアプリケーションにおいてはこれを用いることによって行えるパフォーマンス向上も一般的なサーバーアプリケーションでは早すぎる最適化なのではと思う
ORMとSQL書くパターン
var sql = $@" SELECT USER.NAME ARTICLE.TITLE FROM USER INNER JOIN ARTICLE ON ARTICLE.ID = USER.ID WHERE ID = @id "; // Userクラスにマッピングされる return Connection.Query<User>(sql, new { id });
良いところ
悪いところ
- 取り回しが悪い
これも前述の取り回しの問題が解決できない
ORMでクエリビルダーで書くパターン
C#だとメソッドで書くパターンとクエリ式というもので書くパターンがある
// メソッド形式 var result1 = _context.Users .Join( _context.Articles, user => user.ID, article => article.UserId, (user, article) => new { user, article }) .Where(m => m.user.ID == id) .Select(m => new { m.user.Name, m.article.Title }).ToArray(); // クエリ式 var result2 = (from user in _context.Users join article in _context.Articles on user.ID equals article.UserId where user.ID == 1 select new { user.Name, article.Title }).ToArray();
良いところ
- (静的解析であれば)補完が効く
- 共通化、拡張がしやすい
悪いところ
- 独自構文、メソッドを覚えなくてはいけない
- 発行されるSQLを把握しとく必要がある
これのいいところは取得するカラムの絞り込みを後から行いやすかったり、Whereの共通化、追加を行いやすい
JoinやWhereの部分を拡張メソッドとして切り出してあげることも可能
public static IQueryable<User> HasArticle(this IQueryable<User> query) { return query.Where(user => user.Articles.Any()); } var result = _context.Users .HasArtice() .ToArray();
みたいな
ここらへんはORMによりけり
C#のクエリ式はSQLそのままに近く、かつプログラミング言語の機能を生かせるのでかなりいいと思っている
リッチなORMであれば上記のようなクエリビルダーを使うこともないかもしれない
ここらへんは後でもう少し深堀したいところ
Vue.js スタイルガイドのすヽめ
概要
この記事は Vue.js #3 Advent Calendar 2018 の 10 日目の記事です。
この記事ではVue.jsのスタイルガイドを今一度皆さんにすすめたいと思います。
Vue.jsのスタイルガイドって?
Vue.jsでコードを書くときに命名や記述順序など細かい設計で議論を呼ばないように公式で公開されているスタイルガイドです。
HTML、JavaScriptのスタイルの言及ではなくVue.jsのスタイルの言及のみを行っています。
例えば、これから紹介するコンポーネントのファイル名だったりプライベートなプロパティ名の命名規則なんかが明記されています。
現実的なことが書いてある
このスタイルガイドは細かいことは議論をスキップできるという意味でいいのものです。
加えて特にいいポイントが、公式が現実的な解を示しているというところです。
この現実的な解というのは例えば昨今のエディタを意識していたりといったことです。
例えば 密結合コンポーネントの名前 の詳細な説明をみて欲しいです。
このスタイルは親コンポーネントと密結合した子コンポーネントは、親コンポーネントの名前をプレフィックスとして含めるというものです。
その中で以下のような記述があります。(一部省略)
この問題を、子コンポーネントを親コンポーネントの名前を元に命名したディレクトリの中に入れることで解決したいと思うかもしれません。
例えば:
components/ |- TodoList/ |- Item/ |- index.vue |- Button.vue |- index.vue
これは推奨されません。以下のような結果を生むからです:
同じような名前のファイルがたくさんできてしまい、コードエディタ上で素早くファイルを切り替えるのが難しくなります。
ネストしたサブディレクトリがたくさんできてしまい、エディタのサイドバーでコンポーネントを参照するのに時間がかかるようになります。
これの正しい例としては下記のようになります。
components/ |- TodoList.vue |- TodoListItem.vue |- TodoListItemButton.vue
このスタイルガイドではこのように、現代のエディタに合わせて開発をしやすいように現実的な解答をしていたりします。
また、コンポーネント名における単語の順番 にあるようにコンポーネント名はそのコンポーネントとして重要な単語を最初にするということも書いてあります。
例えば検索関連のコンポーネントでは、以下のような自然言語のようなコンポーネント名ではなく
components/ |- ClearSearchButton.vue |- ExcludeFromSearchInput.vue |- LaunchOnStartupCheckbox.vue |- RunSearchButton.vue |- SearchInput.vue |- TermsCheckbox.vue
以下のように何に関するコンポーネントなのかを最初に明記するということを推奨しています。
components/ |- SearchButtonClear.vue |- SearchButtonRun.vue |- SearchInputExcludeGlob.vue |- SearchInputQuery.vue |- SettingsCheckboxLaunchOnStartup.vue |- SettingsCheckboxTerms.vue
一般的にエディタではファイルはアルファベット順に並ぶので、コンポーネント間のあらゆる重要な関連性はひと目ではっきりと分かります。
こういった現実的な解を定義しています。
他に
といったことが定義されています。
一部はeslintのプラグインを入れることで自動化が可能なので導入をおすすめします。
これで快適なVue.jsライフを!