emahiro/b.log

Drastically Repeat Yourself !!!!

git restore を使う

Overview

今日教えてもらったのだが、git の差分で余計な差分(時刻の更新とか)が出てしまったときにわざわざ file changes に表示させてレビューのノイズにしたくないときには git restore コマンドを使うと良い。

tracpath.com

使い方

一つ前に戻す

git restore -s HEAD~1 $filePath

特定のコミットに戻す

git restore -s $CommitHash $filePath

感想

自動生成コマンドの副産物としていらない差分を Pull Request 煮含めてしまうことがよくあるのでこの git restore コマンドはそういうときに便利だなと思いました。

Git は毎回発見があって面白い。

Chrome Extension に入門する

Overview

業務で Chrome Extension を使ってちょっとしたツールを実装する機会があったので備忘録としてまとめます。

基本的な作り方は以下のエントリを参考にさせていただきました。

r7kamura.com

Chrome Extension のいいところ

そもそも Chrome Extension を作ってみようと思った理由でもあるのですが、以下のような理由で作れると良いなと思いました。

  • Chrome を使ってさえいればブラウザ上で便利ツールを実装できることです。
  • ディレクトリ構成は JS と manifest ファイルさえあればいいので、余計なセットアップ等々も考慮することなく(ビルドプロセスとか)とりあえず動くものを実装できる。
  • 現職の場合、業務レベルではブラウザは Chrome が推奨なので Chrome 向けの対応さえしておけば簡単なツールは簡単に全社展開できること。(まぁ Chromium 使ってるブラウザであれば同様に対応できるので Firefox か Saferi を使っているユーザーでなければ対応できてしまいます)

かける労力に対して得られるアウトカムが思いの外大きいのは Chrome Extension のいいところかなと思いました。

基本的な実装の進め方

以下の公式のドキュメントを見つつ進めました。

developer.chrome.com

特に chrome のランタイムで必要な API を Call するときには permissions を設定する必要があるので都度権限が必要化を調べる必要があります。

いくつか触ってみた API

chrome.getURL

developer.chrome.com

local のファイルを取得するときもこれを使用します。

参考は以下

stackoverflow.com

URL を生成してそれを fetch で呼び出すので Promise が使えたりと便利です。これをするには manifest ファイルの web_accessible_resources にアクセスしたいファイルパスを予め定義しておく必要があります。

今回仕事で実装したツールについてはある条件においてリンクを別のリンクに張り替える(location.href を書き換える)というものだったので、そのマッピングデータを Extension で実装した local のデータファイルに定義しておいて、その定義ファイルを読み込む、ということをしていました。

サンプルは以下です。

  "web_accessible_resources": [
    {
      "resources": ["$pathToLocalFiles"],
      "matches": ["http://*/*", "https://*/*"]
    }
  ],

matches は Chrome Extension が動作する URL を正規表現で指定します。基本的にどの URL でも読み込みだけさせるには上記のような全公開的な正規表現にすればいいと思いますが、大体はドメインくらいは限定しておいたほうが良いかもしれません。

chrome.declarativeNetRequest

※ permission の設定が必要です。

ページをロードする(window.onload が走る)タイミングで動的になにかの処理を対象のページに施すことが出来ます。

ex. あるURL にアクセスされたら別の URL にリダイレクトしたりとか、あるURLのブロックをしたりとか。

どういった操作ができるのかは以下の公式ドキュメントを眺めるとなんとなくわかります。

developer.chrome.com

はまったところ

Manifest ファイルは V3 を利用する

ネット上で Chrome Extension の実装方法を探すと過去の実装方法をベースにしたものも多く見つかります。
パッケージ化しない(= 公開しない)ものであれば V2 も使って実装できるっぽい(エラーは出るけど)ですが、今回はちゃんとしたものを実装するために V3 をつかった 実装方法

Manifest では 実装した Extension が発火するページを制御できる

正規表現にマッチしたページのみで Chrome Extension を発火させることが可能です。

以下のドキュメントが詳しいです。

developer.chrome.com

Chrome Extension には Chrome 独自の module を使う

※ この module は import 宣言をせずとも実行環境が Chrome 上だと書くだけで動くので eslint 等を入れた場合 no-undef の警告に引っかかってしまいます。

そのため、eslint 等を導入する場合は no-use-before-define を off にしておく必要がありました。

以下がそのために追加した linter の定義ファイルです。

module.exports = {
  env: {
    browser: true,
    commonjs: true,
    es2021: true,
  },
  extends: 'airbnb-base',
  overrides: [
  ],
  parserOptions: {
    ecmaVersion: 'latest',
  },
  rules: {
    'no-restricted-globals': 0,
    'no-use-before-define': 0,
  },
};

Local の Debug がしづらい

これは仕方ないことですが、 local では Chromeエミュレーターを簡単に用意することは出来ないので、簡単なツールであれば実際に開発中のリポジトリChrome拡張機能にそのまま突っ込んで動作確認をしていました。

Chrome の設定から拡張機能を開いて上記のパッケージ化しないでアップロードする対象に manifest をルートとしてリポジトリをそのまま指定すれば Extension をインストールしたことになります。
拡張機能側でエラーが発生した場合もこの拡張機能一覧のページにエラーが発生したことが表示されるので、JS のエラー等々は確認できます。

まとめ

JS さえ使えて、かつパッケージ化しないクローズドな環境(社内限定とか)で使う Extension であれば、かなり簡単に Chrome Extension は実装、導入することが出来ました。
API もふんだんに用意されていて、結構自由度が高いのでこれからもなにか機会があれば Extension を実装して解決するようなアプローチをしていきたいなと思いました。

See Also

developer.chrome.com

qiita.com

Keychron を使い始めた

TL;DR

  1. Keychron Q2 (茶軸を使い始めた)
  2. キーマップのカスタマイズ便利 via Ramap

買った経緯

10年弱くらい HHKB を愛用していましたが、Bluetooth の相性がやっぱり Mac とあっておらず、タイピングにラグがあったりと特に M1 にしてから不安定さが以前より増したような気がしていたので、ちょっと一回別のキーボードを使ってみようかなと思ったのが始まりです。
有線版の過去の HHKB も手元にあるにはあるのですが、Type-S (BT含む)の打鍵感に慣れてしまうと有線版の打鍵感に戻れなくなってしまったので、結局一度 HHKB から離れてみよう、と思うに至りました。

その中でどれを使おうか考えていたのですが、以前からちょっと気になっていた Keychron の Qシリーズの中で Q2 の茶軸を購入しました。


使ってみての感想

使ってみての感想は以下です。

  • 茶軸の打鍵感は HHKB より軽い。
  • 右シフトキーの位置が HHKB と異なっていてまだ慣れない。(後述するキーマップの更新で↑と右シフトキーボタンを交換しようか迷ってやめました。)
  • 有線の方が入力自体は安定する。
  • 有線にしたことで Bluetooth の接続端末を一つ減らしたらマウスの方の接続とレスポンスが安定した。
  • HHKB から移行する場合はデフォルトのキーマップで Ctrl の位置に CapsLock があるのでこれは交換しないとキーの配列が全然慣れない。
  • HHKB は結構特殊なキー配列だったことを初めて知りました。
  • 自分でキー配列をカスタマイズするのが結構楽しくてキーボード沼にハマる人の気持ちが少しわかりました。

キーマップのカスタマイズについて

Keychron は QMK/VIA の規格に対応してるので外部ソフトウェア(公式では VIA が記載されています)を使ってキーボードのキーマップをカスタマイズ可能です。

keychron.jp

何故か自分はこの手順通りに進めても端末(キーボード)を認識してくれずカスタマイズの方法をどうしようかと考えていましたが、以下のエントリに助けられました。

salicylic-acid3.hatenablog.com

QMK を利用するなら現在は Remap と呼ばれるツールを使うのが良いらしいです。これは VIA と異なり、Webアプリでもあるのでこれはこれで、クライアントアプリを追加しなくて良いのは良いなと思いました。 また OpenID でのログイン機能があるので、端末(ブラウザ)を変えてもいくつか引き継げそうな設定があることも魅力でした。
実際にカスタマイズを進める手順ですが、以下のキーマップ設定の JSON ファイル集から Q2 JIS を選択します。(日本語のサイトだと Q2 JIS のリンクは載っていません)

Remap で DL した JSON ファイルをアップロードして以下のようなファイルを追加すると以下のようなマップの画面になります。

www.keychron.com

その中でどのキーに何を割り当てたいかは Drag&Drop で設定可能です。割当を行ったら書き込み(Flush)処理を行うと Key の設定が Flush されます。

かなり簡単にキーマップ設定の追加が行えました。

『Google のソフトウェアエンジニアリング 9, 10章』

久しぶりに読書録です。

Overview

Google のソフトウェアエンジニアリング』を2022年内で通読したので、いくつか気になった章の簡易的なまとめを備忘録として記載します。
ちなみに現職の社内勉強会の一環として輪読会を開いておりその中で読み進めている内容になるので、まとめる順序は章立てに準拠はしておらず、また内容も自分自身の解釈を踏まえた主観を多分に含みますのでその点は保了承ください。

今回は特に印象的だった 9,10 章についてまとめます。

Memo

9章: コードレビュー

  1. コードは債務である と認識しろ。将来の誰かの保守運用コストがかかる。
  2. 債務である前提を元に変更(新機能開発含) が どうして必要なのかをコードを書く前に徹底的に議論 しろ。
  3. 開発者は各々がオーナーになるくらい言語特有のリーダビリティに精通してるべき(これがレビューのリードタイムを短くする)
  4. コードのレビュー時に意味の把握(複雑さの低減)or 機能性(パフォーマンス)を向上させる以外に個人の意見を理由として代案を出すべきでない。
  5. 変更は小さく、明確に説明するべし。
  6. コードレビューは 知識共有の枠組み 。
  7. コードを書く側にもプロ意識を期待する。
  8. 自分という存在と自分が生み出したコードは別物。 自分の作った差分は自分のものではなく「チームのもの」。
  9. 人間がやってる機械的なタスクは自動化せよ。

コードレビューのセクションはメンタリング的な内容が多い。

特に良かった考え方は「コードは負債であること」と「コードの作者(実装者) にもプロフェッショナルの意識を求めること」という部分。
Reviewer, Reviewee に力関係は存在しないし、自分が生み出したコードと自分という存在を分けて考えることは、絶対に身につけておきたいスタンスでもあるので、それがちゃんと記載されている、ということは一般的なベストプラクティスとして Google でも認知されているということで、自分自身のスタンスに自信を持つことができました。

10章: ドキュメンテーション

  1. ドキュメントをコードのように扱う。
  2. 組織とともにスケールし、既存のワークフローと調和するようなプロセスとツールを導入することで質の高いドキュメンテーションを継続できる。
  3. ドキュメントの価値は時間が後になってわかる。(労力がかかる割に即時的な利益を Author にもたらさない)
  4. ドキュメントは書かれることは1度だが、後読まれることは何百回、何千回とある。
  5. ドキュメントは自分のために書くものではない。対象読者のために書く。
  6. ドキュメントにおける 4W (Who = 対象読者、What = ドキュメントの内容、When = 作成時刻、Why = ドキュメントの目的)
  7. ドキュメントにもライフライムがある。
  8. 優れたドキュメントには完全性、正確性、明確性のそれぞれのトレードオフが存在する。

個人的にドキュメント(及びドキュメンテーション)の特性というのは「時間と距離を超えて伝播し、残り続けること」が価値だと考えていたので、その価値の捉え方の方向があっていたことは自身になりました。

今回紹介した2章以外にもこの『Google のソフトウェアエンジニアリング』は スケーリング持続性 をテーマとして様々なソフトウェアエンジニアリングの領域における Google の実験から得たベスト(現時点でのベターくらい?)プラクティスが詰まっている良書でした。
ページ数的にもなかなか気合が必要な一冊ですが、一読して損はない一冊かと思います。

2022 年の振り返り

年の瀬なので今年も振り返りをします。
今年は仕事よりもプライベートでの変化が大きな年でした。

仕事編

1人のエンジニアに戻った

GW前くらいまで PjM 兼務でTechLead をやっていましたが、一旦そのロールを降りて1人のエンジニアに戻りました。
戻った経緯は以下のエントリに記載しています。

ema-hiro.hatenablog.com

忙しいのは相変わらずでしたが、PjM としてやってきたことをそのまま現場の1エンジニアとして活かせた部分もあり、ロールを変えてみて見えるものも変わってきたので、この意思決定は良かったなと思います。

尚、来年はどうなってるのかさっぱりわかりません。

アウトプット

登壇した

小さな勉強会ですが個人では数年ぶりに登壇しました。

speakerdeck.com

ブログ継続した

引き続き継続できました。

忙しくて下書きのまま貯めてるものもあるので年末にかきあげて放出したいなと思っているものもあります。
現職のエンジニアリングブログでもいくつか記事を書きました。

medium.com

medium.com

ブログでアウトプットするのは半分趣味みたいなもので、内容も最近エンジニアリングに限らないことも書いてますが、引き続き継続できていることは良い習慣かなと思います。

価値観の変化

去年の後半くらいから個人の成長といったものを一切目的にしなくなりました。
昨年、本業で1つの大きな成果を残したので、個人としてタイトルを目指すモチベーションが枯渇してしまったことも要因としてありますが、それ以上に結婚したことでもはや自分だけの人生でなくなり、時間の使い方が変わったことが大きいかなと思います。
また同時に、スキルの幅を広げ "続ける" 体力みたいなものが年々枯渇していってる感覚も徐々に出てきており、自分自身の持てるリソースの配分をうまいこと調整しながら、持続的に学び続けることができる方にシフトしている、というのが自分自身の感覚をうまく言語化したものかもしれません。
知識をつけるときも、表面的な技術スタックはもうあまり追っかけておらず、より一層深い部分への理解だったり、技術の歴史だったり、そして技術に限らない汎用的な「仕事力」といったものに対して知的体力のリソースを傾けるようになりました。

まとめると個人としては学習にかけるリソースの内訳を今までと変えて、いわゆるエンジニアリングそのものに関しては良くも悪くも「仕事としてのエンジニアリング」、「飯を食う種としてのエンジニアリング」という割り切りをするようになったという感じです。

一方で、今まで以上に事業やチームとして持続的に成長するにはどうすればいいのか?ということを考える頻度は逆に増えました。
ちょうど年末にかけて『Google のソフトウェアエンジニアリング』を読み切って、この中でソフトウェアエンジニアリングに「時間」の概念を含める話が何度も出てきており、持続的な開発について考えるに至った自分自身の変化や、自分の中でうまく言語化できてなかった諸々がうまいことまとまり、来年はまた違った観点でコトに向かえそうな気がします。

私生活編

家を買った

この住む場所を固定化(= 家の購入)したことが1番大きな変化だったかなと思います。

車を買った

車を買ったので移動が原則車前提になりました。
もちろん出社なり都内で用事があれば電車も使いますが、移動手段として電車しかなかった時代には戻れなくなりました。

結婚式をした

小規模ながら結婚式をしました。
男なので、まぁ結婚式なんかしなくていいんじゃね?と思っていたタイプでしたが、やってみたらやってみたで良かったです。

ema-hiro.hatenablog.com

新婚旅行をした

結婚式の日から10日くらいお休み貰って石垣〜沖縄に新婚旅行に行ってきました。
竹富島の「星のや」と沖縄の「ハレクラニ」に宿泊してましたが、こういったリゾートホテルにまともに宿泊したのは人生で初めてだったので、とても良かったです。
また、うまい具合にインバウンド解禁前に新婚旅行できて、かつ国内旅行だったので思ったより予算をかけずに行けました。

石垣島は毎年行きたいと思うようになりました。

筋トレを継続した

なんだかんだ週1~くらいでジムに通い続ける生活を今年も続けられました。
体重も今年に入ってから順調に増えていて、いい感じにバルクアップできているので、筋肉に関して「だけは」圧倒的成長を実感しています。
来年こそベンチプレス 75kg をクリアしたい。

英語継続した

「スピークバディ」というアプリを使って引き続き英語を週4くらいのペースで継続できました。
そろそろ次のレベルに行きたいのでなにか良い教材、もしくは現職の福利厚生である英会話をちゃんと利用しようかなと画策中。

サッカー観戦した

今年は1月の高校サッカー選手権決勝を皮切りに J リーグを2年ぶりに見に行けました。

来年

まぁ去年に引き続き、特になにか達成したいことなどは決めてないです。
毎年何かしらすごい変化があるので、どうせ今決めても何も達成できなくなりそうなのもあります。
きっと来年も多分なんかあるんだろうなと思っています。

とりあえず本読んでブログ書いて過ごせれば最低限自分のメンタルは安定するので、そこさえ守れればあとは特に求めるものはないです。あ、イカは上達したい。

というわけで来年も引き続き頑張っていこうと思います。

2022 年買ったもの

2022 年買ったもの

とんでもなく金が飛んだ1年でした。
(主に最初の2つのせい)

家を買いました。

まぁこの辺の意思決定はその人の人生における選択の優先順位が現れるところでもありますが、自分の場合は単純に WFH が前提(※1)で働く場所には束縛されないし、住みたい広さの物件に都内で住む経済力はないし、郊外で探すと賃貸より購入したほうが固定費が抑えられる(2022年時点)と考えて買っちゃった感じです。

個人的には、この先数十年住所固定化するので、会員登録や色んな行政系の申請が楽になったり、賃貸だと引けない強強インターネット回線(※2)を引けたり、賃貸だと引っ越しなり、原状復帰義務を考えて改造できなかったことに拘ってカスタマイズできたり(※3) するのがいいなと思っていたりします。

※1. 2022年後半くらいから 週1~2 回は出社はするようになりました。
※2. 住んでる地域にもよりますが、何もしなくても下り 900 弱くらい出るのでオンラインゲームするにも困りません。
※3. スマートホーム化とかドア付け替えたりとか。小さいですが家庭菜園できるスペースもあるので冬が明けたらやってみようかなと思ってます。

ちなみに買ってみて初めて知ったのですが、カーテンレールとかテレビとかは追加で工事する必要があり、家本体を買って後の追加コストがまぁまぁすごかったです。今まで賃貸で当たり前のように最初からついていたので、気づかないポイントでした。

去年すでに購入は決めてましたが今年に10月についに納車されました。
トヨタのヤリスクロス (HV) です。
キャッシュ一括で買いました。貯金が0 になりました。痺れました。

郊外に住むことを決めた時点で自家用車は必須だと思っていましたし、そもそも最近の車がどんなもんかを知らなかったのですが、買ってみると「でかいスマホ」って感じで、便利な機能も多く、新しいおもちゃを手に入れた感覚です。

尚、普段は奥さんの通勤車です。

レンタカーの返却時刻を気にしなくて良くなったり、またものを結構積み込めるので遠出の旅行しやすくなったり、キャンプに行ったりと行動範囲もだいぶ車前提にシフトしました。

iPhone14 Pro

親が使っていた iPhone がいよいよ古くて使い物にならなくなっていたので自分が使っていた SE3 を譲って、自分としては数年ぶりにフラッグシップモデルに買い替えました。

とりあえずカメラがすごい進化を感じましたが、めちゃくちゃ筐体が重たくて、片手で持ってると手が痛くなります。

あとパンチホールはぶっちゃけなくてもいいなと思いましたし、顔認証より指紋認証の方が便利だなと思います。

www.apple.com

AirPods Pro2

一昨年初代 AirPods Pro をタクシーで無くして以降、 WF1000x-m4 で凌いでましたが、今年満を持して全然見た目の変わらない第2世代が出たので買いました。
ノイキャンはすごいですがそれ以外初代と何が違うのか自分の耳ではさっぱりわかりませんでした。
林檎信者恒例のお布施として4万円で耳栓を買った気分です。

www.apple.com

とりあえず 14 Pro と合わせて円安の影響をもろに食らったので、所有欲は満たされましたが、お財布はだいぶダメージを受けました。

WH-1000X M5

WH-1000X M3 を持っていて、今年リニューアルしたバージョンが出たので買いました。
ノイキャン性能は本当に素晴らしくまた軽さもあるので、仕事で使う分には十分なスペックだと思っています。

www.sony.jp

LinkBuds S

実は WF1000x-m4 が自分の耳には合わなかったので、AirPods Pro2 が発売されるまでにこの LinkBuds シリーズのカナル型イヤホンをずっと使ってました。
とにかく軽くてつけ心地も AirPods Pro と遜色ないので、これは地味にいい買い物でした。
最近ソフトウェアがアップデートされてマルチポイントにも対応したので、PC とスマホの付替とかもシームレスでノイキャンに拘りがないなら AirPods Pro じゃなくてこっち買ったほうがコスパはいいのでは?みたいなことを思っています。

www.sony.jp

ダイニングテーブル

戸建ての購入に合わせて1人暮らしのときに使っていた小さいダイニングテーブルから大きなもの(4人用)に買い替えました。
テーブル下部をルンバが通れるものにしたので、掃除のときにも困らず、あとテーブル広いのでだいぶリビングっぽくなっていい感じです。


Dyson Micro

使っていた掃除機が壊れたので、ルンバ到着とほぼ同時期に買い替えました。
コードレス掃除機は軽さが正義だと実感しました。
とはいえ1階はほぼルンバがやってくれてしまうので、これは今2階の掃除担当です。

SwitchBot

カーテンと Hub を購入しました。
特にカーテンは毎日決まった時間に開閉してくれるように設定したのですが、毎朝、毎晩のちょっとした手間が減ってよかったです。

また Hub 経由でテレビやエアコン、部屋の電気も操作できたりして、寝る前にいちいち電気消しに行かずに声で操作して消したり、帰宅前にエアコンを付けておく、みたいなことにも使えて便利です。

Amazon Echo 各種

上記の SwitchBot と被りますが、自宅のVUIデバイスを全て Amazon Echo に統一しました。Wake word が1番短いからと言う理由かつ、自宅で使ってるサービスは割と Amazon に依存してる( EC や Kindle FireTV など)ので Amazon に統一しておいたほうが便利だなと思った次第で Google じゃなくて Amazon に乗り換えました。

しかし、最近レイオフがあったらしくこの先のデバイスの進化に陰りが見え始めているのがちょっと悩みの種です....。

なお、自宅で導入したのは Echo Dot の第4世代と Echo Show 5 です。

Anker Eufy ハンディクリーナー

Amazon サイバーマンデーで買いました。
机の上のホコリ掃除したり、あとコーヒー豆を引いたときの微粉や飛んだ殻の掃除に役立ってます。

FunLogy SOUND3

これもサイバーマンデーで買いました。サウンドバーには元々興味があったのと、テレビの配置的に音が聞こえづらいことがあったので購入してみました。
これより良いものものはたくさんありますし、自分も BOSEサウンドバー等々も検討して実際に見に行ったりもしたのですが、とりあえず入門版としてこちらを試してみたところ、違いがよくわからなかったので価格の面からも十分満足してます。

今年のW杯はこのサウンドバーのおかげで音声も含めてだいぶ楽しめました。

アイリスプラザ 本棚 ディスプレイラック

おそらく今年のベストバイはこれです。
自宅に備え付けのデスクがあって、その下の机下収納をずっと探していたのですが、これは横にしたときのサイズがちょうどよくまた本棚にもなるので書籍や書類の収納先も増えました。

HUAWEI MateView 28.2インチ 4K+ ウルトラHD

今まで使っていた局面ディスプレイを同僚に譲ってこちらの 4K ディスプレイに変更しました。
4K にしてみたのは会社のディスプレイで 4K を使ってみたら明らかに目の疲れ方がフル HD とはことなり、発色が目に影響することを実感したので思い切って買い替えました。
Mateview はアス比も3:2 というちょっと独特な比率をしていて横向きでありながら縦に長いのでコードの読み書きにも向いてるなと思います。

ただ Vesa マウント対応してないのでディスプレイアーム等には非対応、というのは人によっては微妙なポイントかも知れません。

その他

ルンバ S9+

これは買ったわけではなく会社からの結婚祝いで頂いたものですが、掃除の概念が劇的に変わりました。
一階の掃除担当はこの子で、小さなホコリ系は全部任せてます。

ロボット掃除機すごいの一言でした。

www.irobot-jp.com

ReFa FINE BUBBLE S

これも僕が買ったわけではないのですが、シャワーヘッド取り替えるだけで髪を洗う体験が劇的に変わりました。
もともとアレルギー持ちで頭皮がめっちゃ痒くなる症状があったんですが、これがかなり軽減されました。
あとあんまり実感はないですが、水道代も節約できるみたいです。

この体験がどれくらいすごいのか、ということを同僚に聞かれたのでとりあえず Intel Mac -> M1 Max くらいの変化を感じるよ、と伝えておきました(個人の主観です)

mtg-pro.co.jp

まとめ

今年は本当に、本当に、、、ほんとーーーーにたくさんお金を使いました。
来年もたくさん使うぞ!

Vue3 で TextArea を入力に応じて可変させる

Overview

タイトルのとおりです。
ちゃんとアプリとかにありがちな TextArea の入力量(改行)に応じて、TextArea を拡張させる実装のサンプルを記載します。

結構ありがちな実装だと思いますが、Vue3 のサンプルが少なかったのでかんたんなサンプルコードを書いてみました。

Sample

<script setup>
import { ref, watchEffect } from 'vue'

const msgInput = ref('')
const msgTextArea = ref(null);
  
watchEffect(()=>{
  // デフォルトのテキストエリアの大きさを指定する。
  msgTextArea.value?.style.setProperty('height', `30px`)
  
  if(msgInput.value !== ''){
    //
    msgTextArea.value?.style.setProperty('height', `${msgTextArea.value?.scrollHeight}px`)
  }
  
})
</script>

<template>
  <h1>Title</h1>
  <textarea 
            v-model="msgInput"
            placeholder="input"
            ref="msgTextArea"
  />
</template>

ref: https://sfc.vuejs.org/#eNqVUs2K1EAQfpW2ETIDM2nF25iM7EHQm4c99mFjtjLJ0uluujuzuwwBJwFXUC+CBz2K4B/u3edp1uewkkxk1EXYXNJV9dVXX/1s6IHW4boCuqCRTU2hHbHgKr3ksii1Mo5siIFsRk4Tl+YPswxSR2qSGVWSAPMCLrlMlbSOlHb1WOrKkbjLmATBdC9yCGfuwECyC8pKiOl9Lgnhco95MpnGy03nZoz49sK3b33zxbfffPvCby99+9w3333zozObz7796psP6L/6+MlvX/stgt/8fHVxdfneb9/55qV/1nRce+XDdSIqeBBady4gxE6fGKXBuPNJkEOxyl0wI0f37uizIxTfiSOkyCZjZ0M2uRXHBLvrdXZKh/9Nq9zeXJeRGiXEox5UjyrqQUqNRsSGJeF60HBQapE4QIuQKL+7PCycgIjhq/c4ZE+6oQ8Kx289L9UxiJjTsTFO/0Qgawq5EsdgEFVcB8EtDgRjCwOAYeWI/RZGZ3Q4o3mZ6PDEKomH1g8OafuA5XRBdqPkFC+qsznNndN2wZjN0u48T2yozIrhKzSVdEUJIdhy/tSoUwsGiTmd7XEwdK7BzA1IbAHM/zj/gv7Du9tATetf01coHg==

実際の動作は以下の様になります。

ハマったところ

vue3 ref height textarea expand みたいなキーワードでググるといくつかサンプルが出てきますが、Vue3 のものがなく、ref で指定した DOM の要素をどうやって SFC の script のところで取り回せるかわからなかったのですが、結論から先にいうと ref の名前と高さをいじりたい textarea を格納する変数名を同じにする ということでした。
つまりここでいうと ref="msgTextArea" と textarea に ref を貼った場合、script 内で使用する変数も msgTextArea とする必要があります。これが最初わからず時間を溶かしました。

Nuxt3 で Custom Navigation を実装する

Overview

Nuxt3 を使って Custom Navigation を実装します。

大まか内容は公式の Pages の https://nuxt.com/docs/guide/directory-structure/pages/#navigation の部分に記載されてる内容です。

Usecase

以下のような Tab の UI を Nuxt3 を実現する際にこの Custom Navigation を使います。

tailwindui.com

例えばユーザーの詳細ページでいくつか情報のセクションを分けてタブで画面の遷移なく情報の表示を変えたいケースなどを考えます。

Samples

ディレクトリの構成のサンプルは以下です。

pages
   users
       [id]
          [tabName].vue

このときに $tabName で指定した tab にするときに navigateTo() を使ったメソッドを用意することで画面ロードが走らない画面遷移を実現することができます。

const selectTab = (tabName: string) => {
  navigateTo(`/users/${route.params.id}/${tabName}`)
}

上記のサンプルでは例えばユーザーのフォロー一覧を見る場合に /users/1/follows というような URL に遷移するように実装し、follows tab のページを [tabName].vue に実装します。

これでリロード無しで Nuxt3 上でタブ遷移やページ遷移を実装することができます。案外簡単だなと思ったのとこういうユースケースはちゃんとドキュメントで網羅されてるので目を通しておかないといかんなと思いました。

Nuxt3 で Data Fetching を行う

Overview

Nuxt3 で DataFetching を行う方法について記載します。

大まかには以下のドキュメントに記載されているとおりですが、一部ハマったところがあったので備忘録として追記します。

nuxt.com

ハマったところ

Data を Refresh するときには関数型で書く

ドキュメントどおりに useFetch を使用する(=引数に API の endpoint を指定する)と refresh の callback を指定しても Data が再 Fetch されて挙動に遭遇しました。

refresh したときに再度 Data を Fetch するためには https://nuxt.com/docs/getting-started/data-fetching/#refreshing-data に書かれてるように useFetch(() => "$endpoint") という形で実装する必要があります。

理由はわかりませんが素直に実装してみたらハマったのでそのメモとして残しました。

wezterm で pane 間の移動をする

Overview

wezterm でキーボードショートカットのみで pane 間を移動します。

手順

以下のドキュメントに書いてあるとおりです。

wezfurlong.org

自分は Cntl+[ でどの pane に移動するかの選択画面を開くようにしました。

keys = {
        {
                key = '[',
                mods = 'CTRL',
                action = wezterm.action.PaneSelect
        },
}, 

令和の時代にモバゲーアバターに受肉する

Overview

sp.mbga.jp

モバゲーのアバターVRM 形式で出力できるらしい(※期間限定)ので試してみた話です。

※ 一部課金が必要になります。

TL;DR

  1. Mobage にログインして自分のアバターVRM で出力する。
  2. 出力した VRMKalidoface 3D はじめ VRM アップロードに対応してるサービスに取り込む。
  3. モバゲーアバター受肉できる。

手順

Mobage のアカウントを作る

作ってください。

スマホでアクセスする

スマホで上記のプレオープンされてるサイトにアクセスします。
VRM で出力するにはブラウザの UA を SP にするだけでは駄目でした....。

そうすると以下のような手順が表示されるので 3D 出力できるきせかえ画面に遷移します。

アバターを着せ替え 3D 出力を開始する

ここで、VRM で出力する条件にある プレミアムクローゼットを事前に 1 枠以上(1枠 525 円税込) を事前に購入しておきます。
購入はアバターのページから買えます(動線分かりづらいですがw)

購入したら適当なアバターの衣装を購入して着せかえます。
着せ替えが終わると以下のような 3D 出力する案内が出ます(多分)

そのまま出力を開始します。
これは非同期で出力され、完了したら Mobage のチャットに通知が来ます。
きせかえアイテムによって VRM 出力に対応してるものとしていないものがあるようです(表情とかは現時点では駄目っぽい)

出力を開始すると以下のようにチャットに通知が来る旨が案内されます。一応時間がかかるらしいですが、僕がアクセスしたときは一瞬で出力完了しました。
そんなに使われてなかったのだろうと思います。

PC からブラウザ でMobage に入って VRM モデルを DL する

ここからは UA を SP にしても可能です。ポータビリティ性考えると PC ブラウザで操作するのが楽だと思います。

3D 出力リクエスト後、Mobage のチャットに遷移すると出力が完了した旨の通知が来ます。

チャットの詳細で DL のリンクを踏むと DL されます。

VRM に対応してるサービスにアップロードする

VRM 形式で出力されてさえしまえば VRM に対応してるサービスどこでも出力した Mobageアバターを使えるので試しに Kalidoface 3D にしました。

OBS 経由で配信も可能でした。
ただ、上記でも記載したとおり表情等はまだ 3D出力には対応してないので、無表情でただアバターが動くだけです。
正直若干不気味です。

下の画像は現職の Slack の Huddle 内でモバゲーアバターを被って配信してるところです。

Kalidoface では何も問題がなかったので、ついでに Cluster でも試してみました。

行けました。
これでモバゲーアバターで Cluster のワールドを闊歩できます笑

まとめ

期間限定ですが、令和の時代にモバゲーのアバター受肉する体験をしました。
ガラケーで見ていたあのアバターに令和の時代に 3D でなりきれるとは思っても見なかったのでノスタルジーを刺激されます。
会社で OBS で配信してみたところある年齢のより上の世代には割とウケました。

こんなことをするために何年かぶりにMobage 内を色々探索したりしてました。めちゃくちゃ色々迷子になりました。

余談

VRM 形式で出力してくれると VRM に対応してるサービス感でアバターを持ち運びできる(ポータビリティ性が高まる)のでメタバース云々言う前にとりあえず VRM をアップロードできるサービスがたくさん増えるといいなと思いました。

独自エラーを errors.Is でハンドリングするには Unwrap を実装する必要がある

Overview

何度も忘れるので備忘録。
きっとまた忘れて似たようなエントリを書く気もする。

Sample

独自エラーを errors.Is でハンドリングしたいときは以下のように Unwrap を実装する。

type sampleError struct {
    err error
    msg string
}

func (e *sampleError) Error() string {
    return e.msg
}

func (e *sampleError) Unwrap() error {
    return e.err
}

func main() {
    var err error = &sampleError{
        err: context.Canceled,
        msg: "context canceled",
    }

    if errors.Is(err, context.Canceled) {
        fmt.Println(err)
        return
    }
    fmt.Println(err.Error() + "-a")
}

ref: https://go.dev/play/p/h3fipvb69mL

firebase.json 内の rewrites.functions に存在しない directory を設定すると 403 になる

Overview

以下のエントリの内容を書くきっかけになった RC のアップデートのタイミングで firebase-functions のバージョンを最新にしたときに、rewrites に存在していた存在しない functions の directory を指定してしまうと 403 Fobidden になりました。

ema-hiro.hatenablog.com

直し方

rewrites の挙動をちゃんと理解してませんでしたが、 https://firebase.google.com/docs/hosting/full-config?hl=ja#rewrites を見るとそもそも 複数の URL で同じコンテンツを表示する ことがない場合は設定する必要はないですし、functions を指定する場合には、存在する functions を指定する必要があります。 書くと当たり前のことですが、たまに触ると以前触った時のことをほとんど全て忘れてしまってますね...。

nitro: false の時は NITRO_PRESET=firebase はビルド時に指定しなくて良い

Overview

タイトルの通りなのですが、いつからか、 Nuxt3 の設定の nuxt.config.js で nitro:false && ssr: false の場合は firebase 向けのビルドをするときに必要だった NITRO_PRESET=firebase が必要なくなっていました。
考えると当たり前で SPA モードにするために ssr: false かつ nitro: false にしてるので NITRO の PRESET を指定する必要はないんですけどね。

NITRO_PREST=firebase とは?

以下で言及されています。Hosting 先を指定するときに使用します。

github.com

ちなみに以下の Zenn の記事で知りましたが、 nitro: {} の設定において preset に指定してもいいようです。

zenn.dev

PRESET の指定でビルド結果が変わる

前提なのですが、そもそもどうして気付いたかというと、たまたま Nuxt3 の RC のバージョンを上げたタイミングで、Firebase Hosting にデプロイしたアプリケーションが Firebase の 404 を返すようになって気づきました。

挙動を調べると NITRO_PRESET を指定してるケースでは nitro: false が効かず、nitro: true の時と同じビルド結果になる、ということがわかりました( .output/public 配下に html が生成されない)

html が生成されていなかったために、Hosting にデプロイしても HTML が見つからずに 404 になっていました。

※ 本来、.output/public 配下に HTML が生成されない(= SSR モード)の時は Cloud Functions などで node のランタイムを動作させておく必要があります。

解決策

nitro: false の時は NITRO の PRESET を指定しない状態でビルドしてデプロイしたら上手く行きました。

Nuxt3 の useFetch を勉強する

Overview

Nuxt3 の useFetch API を実際に使ってみたので使った範囲での機能についてまとめます。

useFetch の詳細は以下の Nuxt3 のドキュメントに書いてある通りです。

v3.nuxtjs.org

ざっくりいうと Fetch と Data Sync を同時に行ってくれます。

Refreshing Data

概要は以下の公式のドキュメントの Refreshing Data に書いてある通りです。

v3.nuxtjs.org

ある reactive な state を URL の parameter に指定して、その state を useFetch API 側で解釈してクエリパラメータに指定する、といった時に使います。

実際に使用するユースケースとしては「フィルタ機能」などになると思います。

例えば、ある条件下でフィルタリングされたリストがある(特定の年齢以上のユーザーの一覧を出したい等)場合、フィルタリング条件を reactive な stage に登録しておくとドキュメントに書いてあるように useFetch を呼び出しを関数にすることで UI 側で指定した条件で変化させるたびに UseFetch API が Call されて Data の Refreshing が自動で行われます。

<script setup>
const age = ref(1); // age を UI 上で参照し、変化させるたびに useFetch API が Call される。

const { data: users, pending, refresh, error } = await useFetch(() => `users?age>${age.value`, { baseURL: config.API_BASE_URL }
);

</html>

Pending

useFetch API の第二返り値に指定されている値で、useFetch API が Call されたときに 「Call中かどうか(=要は HTTP リクエストが完了したかどうか)」を判定します。

これにより、ある stage に対してHTTP のリクエストが完了したら更新する、といった処理を簡単に書くことができます。

個人的には Nuxt3 を使い始めて一番便利だなと思った Feature でした。

SearchParams

20220829 時点ではまだ正式なドキュメントがないですが、useFetch で Call する endpoint に対してクエリパラメータを設定できるようにする option です。

ドキュメントでは https://v3.nuxtjs.org/api/composables/use-fetch/#type に記載されており、Description にも Query params と記載があります。

型としては以下になるので Go でいうところの map[string]any と同値で文字列で key を指定して値はなんでも当てはめることができます。

interface SearchParams {
    [key: string]: any;
}

まとめ

実際に仕事で使ってみた機能をベースにまとめてみました。ドキュメントの読み方を教えてもらえたので、もう少し useFetch API に一人でも慣れていけそうな気がしました。