getClientRects と getBoundingClientRect の違い
たまに使おうとするとどっちだっけってなるやつです
どちらも Element のメソッドで要素の場所やサイズを取得するものです

よく読めば違いは名前でわかるようになってます
getClientRects は複数形なので複数の DOMRect を取得します
それに対して getBoundingClientRect は単数なのでひとつです
Bounding という言葉からわかるように 全部の DOMRect を覆うひとつの DOMRect になります

基本は 1 つなのですが 折り返すケースでは複数の DOMRect に分かれます
例えばこういう HTML があって

<div>
<span>AAA AAA AAA AAA AAA</span>
<span>BBB BBB BBB BBB BBB</span>
</div>

見た目がこうなったとします

AAA AAA AAA AAA AAA BBB BBB BBB
BBB BBB

このとき A の span では

[getClientRects]

DOMRectList {
"0": DOMRect {
"x": 8,
"y": 8,
"width": 184.40625,
"height": 24,
"top": 8,
"right": 192.40625,
"bottom": 32,
"left": 8
}
}

[getBoundingClientRect]

DOMRect {
"x": 8,
"y": 8,
"width": 184.40625,
"height": 24,
"top": 8,
"right": 192.40625,
"bottom": 32,
"left": 8
}

となり getClientRects の結果は 1 つだけです
getBoundingClientRect で得られるものと同じです

それに対して B の span では

[getClientRects]

DOMRectList {
"0": DOMRect {
"x": 197.84375,
"y": 8,
"width": 107.765625,
"height": 24,
"top": 8,
"right": 305.609375,
"bottom": 32,
"left": 197.84375
},
"1": DOMRect {
"x": 8,
"y": 32,
"width": 70.03125,
"height": 24,
"top": 32,
"right": 78.03125,
"bottom": 56,
"left": 8
}
}

[getBoundingClientRect]

DOMRect {
"x": 8,
"y": 8,
"width": 297.609375,
"height": 48,
"top": 8,
"right": 305.609375,
"bottom": 56,
"left": 8
}

右上と左下の 2 つに分かれます
getBoundingClientRect ではこの 2 つを含む領域なので 大きめの矩形です
ほぼ親の div と一致します
横の僅かな隙間などがあるので完全には一致しません

基本は getBoundingClientRect で良いと思います
GitLab のウェブ IDE が VSCode になってた
久々にウェブ IDE を使ったら VSCode の画面になってた
リポジトリのフォルダを VSCode で開いてるのとほぼ変わらなくて使いやすい
ファイルの保存というのはなくて まとめてコミットすることになる
ローカルで Git を使わずウェブの UI だけで使うにはすごく便利

これまで Gist においてたようなのは GitLab にしようかな
すでに公開する系を色々まとめておくためのリポジトリは用意してるし

便利すぎて Ctrl-S を押す感覚でコミットして 動作確認は Pages 画面でやればいいかみたいになってる
でも Git でバージョン管理するものとしてはふさわしくない気がする
期待通り動くようになった時点でそれまでのコミットを squash でまとめられたらいいけど
GitLab の UI を通してはできないような気がする
ブランチも master だし
ブランチのプロテクト解除してローカルで書き換えてから force push かな
でもとりあえず色々まとめておいてるだけのリポジトリでそこまで頑張る必要もないかも
WeasyPrint で同じフォルダのフォントを使う
普通に @font-face で url に相対パスを指定すれば動く
扱い的に local ぽいけど local はインストール済みフォントをパスではなく名前指定で探すもの

(例)
フォントは 「M PLUS 1 Code」 を使用
Google Fonts でダウンロード
https://fonts.google.com/specimen/M+PLUS+1+Code?query=Coji+Morishita&vfonly=true

index.html と index.css と MPLUS1Code-Regular.ttf を同じフォルダに配置

[index.html]
<!doctype html>
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" href="./index.css"/>
</head>
<body>
<h1>H1</h1>
<pre><code class="">function a() {<br/> console.log("!#$%&{}+*@|?")<br/> return 1<br/>}</code></pre>
<pre><code class="font1">function a() {<br/> console.log("!#$%&{}+*@|?")<br/> return 1<br/>}</code></pre>
<pre><code class="font2">function a() {<br/> console.log("!#$%&{}+*@|?")<br/> return 1<br/>}</code></pre>
</body>

[index.css]
@font-face {
font-family: "mp1c";
src: url("MPLUS1Code-Regular.ttf");
}
pre code {
display: block;
border: 1px solid #000;
padding: 5px;
}
.font1 {
font-family: monospace;
}
.font2 {
font-family: mp1c;
}

コマンド
weasyprint index.html out.pdf

結果
out

コードのプレビュー機能
メインの方のブログと こっちのサブのブログの使い分けですが 基本は長さです
主にセクション分けしたいくらいになってきたらメインの方に書いて そうでもないときはサブの方です

他にはプレビュー機能の有無があります
プレビューボタンを押すとコードブロック内の HTML を実際に別タブで表示するので動きを確認できます
実現方法は新しいタブを開いて window オブジェクト経由で document.open して document.write するというだけ
あんまり好ましい方法じゃないと思いますが こういう用途に限ればありかなと思ってます

ただ 最近のこういう機能を実現してるページって ServiceWorker を使ってます
ServiceWorker を使うと実ページとして URL を持ってアクセスできます
今の方法だと動的に document.write で書き込んでるのでリロードすると再表示できない とか devtools でデバッグするとかで問題があるのですが ServiceWorker にするとそれにも対応できます
サブブログの方は ServiceWorker を使って機能を追加しようかなと考えてます

でも プレビューできるようにするとなると HTML 形式で 1 ページ全体のコードを記載する必要ができます
できるだけ短くシンプルにしたいはずの Short のブログなのに長くなってしまうのは どうなのかなと思ったりもしてます

ほぼ閲覧者はいないと思いますが 一応ライブドアブログはスマホ表示もあって こっちだと共通の処理やスタイルが適用されないので あまり特殊な機能を入れてるとそっちに影響したりします
CustomElements とか実質使えないですし

と考えると Gist などに完全なコードをおいて Githack でページを用意してそこへのリンクをつける程度がいいのかもです
Gist だと Githack を通さないとページとしてアクセスできないので 1 リポジトリにまとめて Pages 機能でもいいかも

(流し見するとタイトルが「コードレビュー」に見えてしまう)



その後

GitLab は VSCode の UI で編集してコミットできるようになってて便利だったので GitLab Pages にしようと思ってます
ただ 気になるところとして Gist を Githack で公開する場合はリビジョンも URL に含まれます
それに対して GitLab Pages は常に最新版です
各バージョンをフォルダとして用意すればできなくもないのですが バージョンで管理するようなちゃんとしたものでもないです

バグ修正レベルなら最新でもいいのですが 大きく変更したりフォルダの移動が起きると当時のバージョンがあったほうがいい気もします
はっきり決まらないですが ブログ記事の古いものなんてそんな見返すこともないので深く考えなくていいかな(諦めた)
Vite で動的インポートにしたらファイル数が思ってたよりかなり増えた
基本フロントエンドのビルドでファイルをまとめるとき 1 ファイルに全部をバンドルしているのですが 結構重たくなってきました
とりあえずページごとに動的に import するようにして分けたのですが 想像以上にファイル数が増えました

例えば こういう依存関係の場合

index.js
A.js
X.js
B.js
Y.js
Z.js
C.js
Z.js

A, B, C をそれぞれ動的なインポートにします
メインである index.js 以外に A, B, C の 3 つのファイルができて 4 つかなと思ったのですがそうじゃなかったです

Z は B からも C からも使われます
B と C の両方に Z を入れると両方を使うページでは二重になってムダですし 別モジュールとして扱われてしまう実害も出ます
なので Z は独立したモジュールとして自動的に分割されます
X は A だけで使われ Y は B だけなので これらはそれぞれ A と B に含まれることになります

実際のところ Z みたいに複数箇所で使われるモジュールってかなり多いです
各ページを開いたときにロードするファイルはメインのファイルとそのページ用のファイルの 2 ファイルになるくらいに考えていたのに 実際はそれよりはるかに多かったです

それでも最適化はされてるようで export せず console.log だけみたいな場合は一つのファイルにまとめられたり B と C の両方が Y と Z を読み込む場合は Y と Z は一つのファイルにまとめられるなどしてました

でも実際の大きめのプロジェクトになると まとまらないことも多そうですし ESM で直接ロードするのとそう変わらないくらいファイルをロードすることになりそうです
それならファイルをまとめる必要あるのかなと思いますね
インストールしたいコマンドのパッケージ名がわからないとき
ps コマンドってデフォルトで入ってないこともあって 追加でインストールしたいことがあります
ps コマンドをインストールするのに必要なパッケージは RPM だと

procps-ng

です

多くのコマンドはコマンド名のままなのに特殊過ぎます
覚えられません

そういうときはインストールするコマンドのパスを指定すればいいです

dnf install /usr/bin/ps

これで procps-ng がインストールできます
大抵のコマンドは /usr/bin にインストールされるので困ったときはやってみるといいです
転職のスカウトの話
すごく 「それ」 って思う話題のまとめを見かけたので
https://togetter.com/li/2250590

就職活動で スカウトが来たのに落とされたというやつ
スカウトされたから話を聞きに行ってみたら企業側がスカウトしたと認識してなかったという話もあるようです
やっぱりスカウトってあまり意味ないみたいですね
実際 Paiza なんか有効期間内のスカウトで 100 件以上届いてるとかよく見るくらいですし
内容見てると希望条件も使用技術も全然あってないところからも来てたりします
半分というかほとんどは 来てほしいと思ってないでしょ という感じです

Paiza で言えばスカウトまでいかなくても 「気になる」 みたいなシステムがあるわけですし 軽い気持ちや自動でするのならこういうのに収めておいてほしいです
スカウトを送るくらいなら 面接してみて極端に社風と合わないみたいなレベルじゃなければ即合格くらいであってほしいし そのレベルじゃないなら送るのはやめてほしいです
迷惑メール並みに溢れるので必要なものを見逃すんですよね
Chrome 120 で CSS のネスト時にセレクタをタグ名から始められるようになる
https://chromestatus.com/feature/5070369895743488

h1 {
a {
color: dodgerblue;
text-decoration: none;
}
}

これができるようになります

これまでは 内側のセレクタは記号から始める必要がありました
ID やクラスなどのセレクタは記号から始まるので問題ないですが タグ名を書くときは簡単に書けませんでした

上記の CSS だと a のセレクタは無効です
:is() を使ったり & を使う必要があります

h1 {
:is(a) {
color: dodgerblue;
text-decoration: none;
}
}
/* or */
h1 {
& a {
color: dodgerblue;
text-decoration: none;
}
}

事前に CSS を変換するツールだと 基本的にこの一手間が不要だったので少し不便です
事前に変換するなら速度がそこまで重要視されないので 先の方まで読んでしまって セレクタなのかプロパティ名なのか判断できます
しかし ブラウザだと先まで読むことを基本許可しないので タグ名から始められないのは仕方ないものでした
この辺は実装されるまで仕様を決める段階でも色々議論されてたようです

なので無理なものと思っていたのですが Chrome 120 からはそれが緩和されてタグ名から始められるようになったようです
パフォーマンス面ではやはり劣るみたいですが できる限り高速になるようしているそうです
基本は declaration (color: red; みたいの) としてパースし始めて プロパティでないと判断でき次第 rule (h1 {} みたいの) として再パースするようです
詳細な説明は仕様の Implementation note のところに書かれてました
https://drafts.csswg.org/css-syntax/#consume-block-contents

ShadowDOM を使ってるとスコープが小さくなるので タグ名だけのセレクタを使うことが多くなります
毎回 :is() を書くのに疲れてきていたので これはとても嬉しいですね
React などで表示切替をアニメーションさせるとき
アニメーションで少し違和感を感じるところがありました
detail と summary タグ的なもので 高さを切り替えて表示の有無を切り替えているものです
UI ライブラリだとアコーディオンとか呼ばれてたりもします

devtools でゆっくりにしてみると 閉じてる途中で中身が消えていました
開くときには途中までアニメーションしてそこからは一気に本来の高さに切り替わっていました

React などのフレームワークならでは感がありますね
単純な開閉ならともかく 選択したものを表示する系だと 閉じてるときは選択中のものがありません
こういう感じで作ってると自然とそうなりそうです

<Accordion open={!!data}>
{data && <Internal data={data}/>}
</Accordion>

DOM を直接操作するなら選択が解除されても閉じるだけでよく わざわざ中身まで更新するのは面倒なので中身はそのままで放置だったりします
しかし React 的なものだと残すほうが大変です
開閉と状態と選択状態を別に保持して 閉じるのが完了したイベントで選択を解除するとか 選択と画面表示を別にして 選択されてなくても画面表示用のデータは最後の選択を保持するとかしないといけないです

開くときは その時点では開いたときの高さが不明なのでうまくアニメーションできないです
これは useLayoutEffect などでアニメーションさせればできそうな気はしますが ライブラリを使ってる箇所ではうまくいってないようでした
高さ不明なので適当な高さまでアニメーションで開いて 完了後に height を auto に切り替えることで実際の高さになるのですが そこで急にサイズが変わってました

DOM を直接触るとき以外は基本アニメーションはさせないようにして UI ライブラリのデフォルト挙動のみにしてましたが ちゃんとアニメーションさせようとすると大変そうです
なぜ人は働かないといけないのか
最近は働きたくなさの現実逃避で色々新機能とかライブラリに触れてみたりしてたので 記事に書く予定のものが多くなりすぎて土日では処理しきれなかったです
もう少し本文を減らしたり 書く内容を厳選したりすべきですね

働きたくないでござるとか働いたら負けのネタ T シャツを着て面接に挑みたい(落ちる)
Temporal がもうすぐ使えるようになりそう
JavaScript の不満点のひとつでもある Date オブジェクト
色々と使いづらく dayjs 等のライブラリが必須に近い状況です
一々ライブラリを入れたくないところでは自分で簡単な関数を作るのですがそれも面倒です

そんな Date に変わるものとして期待の日付系の新機能 Temporal というのがあります
https://github.com/tc39/proposal-temporal

この機能は 2021 年 3 月には Stage3 になっていたのに その後全然 Stage4 にならず ブラウザでも実装されない状況でした
理由はタイムゾーンやカレンダーの標準化待ちで Temporal 自体の API に変更予定はないものの これが終わるまではブラウザでフラグ無しに使えるようにしてはいけないとされていました
この標準化は IETF という別のところでやってるようで それに時間がかかってるようでした

その作業も先週完了しドキュメントが承認されたようです
関連する issue もクローズされました
https://github.com/tc39/proposal-temporal/issues/1450

あとは実装待ちですが 構文が変わるものでもなく polyfill が存在するものなのでもうすぐ使えるようになりそうですね
input イベントは ShadowRoot を突き抜ける
入力系のコンポーネントを WebComponent で作ってるときの change などのイベント
change イベントの場合は event.composed が false なので内部の input などを更新してもイベントは ShadowRoot で止まる
コンポーネント外で検知できないので 必要なら自分でイベントを起こす

this.dispatchEvent(new Event("change"))

input イベントの場合は event.composed が true なので ShadowRoot で止まらず window まで伝わる
入力途中の無効な状態でイベントを伝えたくないなら 自分で伝播を止める必要あり

this.shadowRoot.addEventListener("input", (eve) => {
eve.stopPropagation()
})

あとは change のように自分でイベントを起こす

全部伝えてもいいかもしれないけど input の date を参考にすると 有効な状態になるまでは input イベントは起きない
未入力状態で 年だけ埋めても input イベントは起きない
年月日全部埋めてはじめて起きる
Lit で Directive にホストの CustomElement の参照をもたせる
Lit の Directive は LitElement と統合されてないので Directive からホストのカスタム要素にアクセスできません
一応 Part にアクセスすれば RenderOptions の値を options プロパティで持ってるのでアクセスできるといえばできるのですが Part は公開されていないため __part に無理やりアクセスするような方法になります
minify で名前が変わるかもしれませんし あまりアクセスしたいところではないです

自分で持たせようにも Directive のインスタンスを作る処理に手を加えることはできないです
render の処理の中で directive の関数を呼び出すときに都度 this を渡せば Directive の render メソッドの引数として渡せますが 全てにそれを書くのは避けたいです

render() {
return html`<div>${directive1(this, foo, bar)}</div>`
}

自動で this を追加で渡すようにラップした関数をプロパティで持たせておくこともできます
constructor でこういう感じのことをします

this.directive1 = (...args) => directive1(this, ...args)

これだと Directive の render メソッドの第一引数で host を受け取ることになります
Directive のインスタンスのプロパティに入ってるほうが自然な気がするので directive 関数を呼び出して directive の関数を作るところでクラスも作ります
インスタンスごとに異なるクラスになります
このクラスを作るときにスコープ的にホストのカスタム要素を見れるようにして これを参照させます

コードにするとこんな感じになりました

import { html, LitElement } from "lit"
import { directive, Directive } from "lit/directive.js"

const customElementDirective = (element, Directive) => {
return directive(
class extends Directive {
host = element
}
)
}

class Dire extends Directive {
host = null

constructor() {
super()
console.log("Directive created")
}

render(name) {
console.log("Directive rendered", this.host, name)
return "a"
}
}

class ExampleElement1 extends LitElement {
d = customElementDirective(this, Dire)

render() {
return html`
<div class=${this.d("A")}>${this.d("B")}</div>
`
}
}

class ExampleElement2 extends LitElement {
static properties = {
n: {}
}

constructor() {
super()
this.d = customElementDirective(this, Dire)
this.n = 0
}

render() {
return html`
<button @click=${() => this.n++}>${this.n}</button>
<div>${this.d("A")}</div>
${this.n % 2 === 0
? html`<div class=${this.d("B")}>${this.d("C")}</div>`
: null
}
`
}
}

customElements.define("example-element1", ExampleElement1)
customElements.define("example-element2", ExampleElement2)

example-element1 と example-element2 が使用例です
2 の方では ボタンを押すとプロパティを更新して表示内容を切り替えています
下の div の表示の有無が切り替わります
一旦消えてから DOM が再作成されるときに Directive のインスタンスも作り直されます

これを使えば画面の更新時にホストの要素も更新する Directive が作れそうです
関数の代わりにコンポーネントで渡すことでイベントとして扱えるようにする
前記事を書いていて関数のかわりにコンポーネントで渡すメリットもあるなと思ったので

まず前提ですが 関数を受け取り実行して結果を内部で使うコンポーネントがあります

const Component1 = ({ getValue }) => {
const [state, setState] = useState()

useEffect(() => {
getValue().then(value => setState(value))
}, [getValue])

return (
<div>{state}</div>
)
}

const getValue = () => {
return new Promise(resolve => setTimeout(resolve, 100, "VALUE"))
}

これの嫌なところは useEffect が入るところです

関数の代わりにコンポーネントで渡すと コンポーネントに渡した関数が呼び出されてイベントベースな動きにできます

const Component2 = ({ GetValue }) => {
const [state, setState] = useState()

return (
<>
<GetValue onValue={value => setState(value)} />
<div>{state}</div>
</>
)
}

const GetValue = ({ onValue }) => {
useEffect(() => {
getValue().then(value => onValue(value))
}, [])
return null
}

GetValue の中に useEffect はありますが これはそれをするためだけのコンポーネントです
Component2 の中に useEffect が存在しなくなるのが良いところです

元の Component1 がほぼ何もしないコンポーネントなのでこれだとあまり違いは感じられませんが もっと色々し始めると useEffect での呼び出しが減るのは少し助かります
API からのデータ取得はどこでする
WebComonents を使ってると やっぱり標準に合わせたくなります
img や iframe などを見ると URL だけ渡せば後はやってくれます
データを取得するのもコンポーネントに任せたいところです

ただ取得もやると 1 コンポーネントが重たくなるなーと思ったり
またデータはすでに別の理由で手元にあるときがあり そういうときでも URL から再取得するのはムダです
取得と使用で分けようかなと思ったものの どうしようか

書きやすさ的に React で考えます
React ならとりあえずフックでしょうか
フックがデータを取得し それを渡します

const data = useData(url)

return data.state === "loaded"
? <DataViewer data={data.content} />
: <Loader />

コンポーネントとして考えると取得するコンポーネントの props に取得したデータを使用するコンポーネントを渡すとかでしょうか

<FetchData url={url} Viewer={DataViewer} />

DataViewer に追加の props も渡したいことを考えると render 関数として渡す方がよいかもしれないです
内側に表示するものなので見た目的に children として渡します

<FetchData url={url}>
{data => <DataViewer data={data} />}
</FetchData>

これらだとすでにデータを取得している場合は DataViewer だけを使うことができます

反対に DataViewer の方を外側に持ってきて FetchData を渡す方法も考えられます

<DataViewer FetchData={FetchData} />

DataViewer が内部で FetchData へ onData 関数を渡して これが呼び出されたら自身の state に受け取ったデータをセットする感じです
FetchData は中でデータを取得し 取得したら props で受け取った onData 関数を呼び出します
DatViewer の props に data も用意してこっちがあればそのデータを使い なければ FetchData を使ってデータを取得とできます
この場合はわざわざ FetchData をコンポーネントにする必要もないので fetch に関数を渡すだけでもいいと思います

<DataViewer fetch={fetchData} />

ただこうするとイベント時の処理ではなく 自身で関数を呼び出すわけなので API を直接呼び出すのと大差ないです
useEffect とかが入ってきます
API を直接呼び出すのと比べて 受け取ったあとのフォーマットやエラー時の処理などをやらなくて済むくらいです

となると FetchData を外側に持ってくるものか フックを使うものです
子コンポーネントに渡すレンダー関数を渡すというのは React ならではのものなので フックの方でしょうか
フックも React 固有になるので実際にはただの関数として用意するようになりそうです

エラー表示を考えるとコンポーネントである FetchData の方が表示も管理できて便利かもしれません
ただエラー表示が固定であればです
使う側でカスタムするなら 結局エラー表示を制御するのでそれなら 関数にしてもいいかもです
ただデータの管理を親コンポーネントでしないといけなくなるので DataViewer がいっぱいあるところだと 使うほうが面倒にもなります
難しいですね
Git 自身の更新履歴を見たい
Git の性質もあって ググっても Git を使って変更履歴を管理するとかそういうのばかり出てくる
公式ドキュメントのサイトを探しても変更履歴ぽいのが見つからない
Github のプロジェクトの Releases では変更履歴が管理されてない
リポジトリのトップレベルのファイルを見ても ChangeLog や History 的なものがない

諦めかけたところで発見
リポジトリの Documentation/RelNotes というフォルダの中にまとまってた
https://github.com/git/git/tree/master/Documentation/RelNotes

ググったときに一応見かけたけど一番下が最新版じゃないのでもう古いやつだろうとスルーしてた
数字と文字列ソートの都合で最新版が一番下じゃないだけで今でも更新されてた
Windows で拡張子なしのファイルを規定のプログラムで開く
https://superuser.com/questions/13653/how-to-set-the-default-program-for-opening-files-without-an-extension-in-windows

メモ帳で開く場合
管理者権限コマンドプロンプトで以下コマンドを実行

assoc .="No_Extension"
ftype "No_Extension"="C:\Windows\System32\notepad.exe" "%1"

notepad.exe のパスのところを好きなエディタに変える
サクラエディタなら

C:\Program Files (x86)\sakura\sakura.exe

拡張子なしは基本的に設定ファイルなどテキストファイル
とりあえずテキストエディタで問題ないはず



assoc コマンドは拡張子とファイルタイプを関連付けるもの
拡張子に対して 適当に名前をつけれる
引数無しで assoc コマンドだけを実行すると登録済みの一覧が見れる


.htm=htmlfile
.html=htmlfile
.jpe=jpegfile
.jpeg=jpegfile
.txt=txtfile

ftype コマンドはファイルタイプとそれを開くデフォルトのコマンドを関連付けるもの
assoc でつけた名前に対してコマンドを指定できる
引数無しで ftype コマンドだけを実行すると登録済みの一覧が見れる


htmlfile="C:\Program Files\Internet Explorer\iexplore.exe" %1
txtfile=%SystemRoot%\system32\NOTEPAD.EXE %1

ただ .html は Chrome で .txt はサクラエディタになってる PC でも↑
ファイルのプロパティから関連付けたものはここに反映されなくて そっちが優先されるようになってるみたい
拡張子なしの場合はプロパティで設定できないのでこれで設定するしかないみたい

基本的にファイルを右クリックすれば◯◯で開くがメニューにあるのでわざわざ設定する必要性は薄いけど zip の中のファイルとなると右クリックメニューで◯◯を開くを選べないのでそういうときに便利
Yarn4 が出た
正式リリースされたようです
rc は 53 まであったようです

大きな変更はゼロインストール機能がデフォルトで無効になってるようです
ゼロインストールはそれ自体の ON/OFF はなくて PnP とオフラインミラーの両方を使うことで実現できるものです
PnP は Yarn4 でもデフォルトで有効なので オフラインミラーがオフになったようです
具体的には .yarnrc.yml の設定の enableGlobalCache のデフォルト値が true になりました

enableGlobalCache が true だとインストールするパッケージは 各プロジェクトのフォルダ内に保存されず全部のプロジェクトで共通になります
Linux 環境だと 「~/.yarn/berry/cache/」 に入ります
同じパッケージを複数のプロジェクトで使うときも実体はひとつなので効率的です
なのでデフォルト挙動としてはこっちで良いと思います
false にすると各プロジェクトフォルダに保存されるので Git にコミットしてゼロインストールにできます

他には yarnPath の代わりに Corepack が推奨されるようになったようです
とは言ってもまだ yarn init で作られる .yarnrc.yml に yarnPath の設定が記載されています
package.json には packageManager が追加されるようになっています

{
...
"packageManager": "yarn@4.0.0",
...
}

Constraints 機能にも変更があって JavaScript で書けるようになったようです
これまでは Prolog でしたが なぜ Prolog という状態でしたからね
記載する内容に適した言語なのでしょうけど Yarn を使うユーザー層を考えると全然違う言語過ぎて難しすぎました
気軽に使えるようになったのは良いですね
Chrome 119 で select タグに hr が使えるようになる
来週に Stable リリースされる Chrome119
新機能に地味だけど便利かもしれない機能があった

select タグに hr タグを入れて横線を表示できるようになる

<select>
<option>Option1</option>
<option>Option2</option>
<hr/>
<option>Option3</option>
<option>Option4</option>
<hr/>
<option>Option5</option>
<option>Option6</option>
</select>

これまでは option 以外の関係無いタグを入れても自動で削除されてた
Chrome 119 からは hr タグのところに横線が表示されるので 区切りをわかりやすくできる

ただ 見た目はあまりきれいじゃない
スタイルに height や margin などを設定しても無視される
hr タグの有無しか反映されてなさそう
hr タグを連続して入れることで太い線にできるけど 見た目のためにそれはどうなのって思う
HTML 要素の innerText
HTML から見た目通りの改行のテキストを取り出したいです
innerText を使うと見た目に合わせて文字列が受け取れます
例えばこういう HTML があるとき

foo
bar<br/>baz
<div>div</div>
<p>p</p>
text

受け取る文字列はこうなります

foo bar
baz
div

p

text

HTML 中の改行はスペースになるので スペースに変換されています
br タグは改行に変換されています
また div タグでも改行され p タグでは前後に空行が追加されます

この変換を大きめのファイルにまとめて行いたいです
Node.js でやろうかと思って jsdom を使ってみたのですが innerText はサポートされていませんでした
レンダリングエンジンありきみたいな機能なので実装されてないそうです
issue で要望は出てましたが 未だに実装されていないのであまり期待はできません

happy-dom ならどうかなと試してみたらこっちはサポートされていました
しかし 思ったような出力になりません

foo
barbaz
div
ptext

br タグは単純に無視され 改行がそのまま反映されています
また div は前後で改行されていますが p は改行されず直後の改行も無視されて text とくっついてしまっています

しかたなくブラウザを使うことにしました
ただブラウザを使っても期待する結果を得るにはドキュメントと接続されて画面に表示されている必要があるようでした
document.createElement を使って div を作った直後に innerHTML を設定して innerText を取り出してもこうなります

foo
barbaz
div
p
text

HTML の改行がそのまま反映されて br タグは無視されています
happy-dom と同じ感じです
p タグのあとに改行ができるところだけ違っています

ブラウザでも一旦表示させないといけないのが面倒ですね
速度にも影響出そうですし

一応簡単に調べてみたところ 8 ~ 10 倍くらいの時間がかかるようになってました
適当なページから持ってきた HTML で文字数は 6500 くらいでタグ数は 150 くらいのもので確認しました
Lit で CSS-in-JS したい
Lit の 3.0 が正式リリースされたので久々に Lit を使っています
React で CSS-in-JS ライブラリを使っていると Lit でスタイルを適用したいときに少し不便です
一応 styleMap という directive があるのですが 単純に style 属性を設定するものです
最近ブラウザで使えるようになったネストするセレクタは書けません

やりたいものはこういうのです

customElements.define("foo-bar", class extends LitElement {
render() {
return html`
<div
class=${style`
border: 2px solid #aaa;
padding: 12px;
:is(input) {
padding: 4px;
}
`}
>
<label>name</label>
<input>
</div>
`
}
})

div の class のところでスタイルを書いて 自動生成されるクラス名が class に渡されて設定されるというものです
だいたい Emotion です
ただ そこまで複雑なことはしなくてもいいです
ネストはブラウザ側の機能で実現できるので 単純にクラス名を作って設定するだけでいいです
それなら自分で簡単にできるかなと試してました

LitElement のクラスでは styles という静的プロパティを用意しておくことで 自動でそのスタイルをコンポーネントに適用してくれる機能があります
しかし こういう動的に更新するものには使えないです
なので自分で adoptedStyleSheets を追加します
コンストラクタのタイミングでは ShadowRoot を作ってくれないので createRenderRoot をオーバーライドして ここで追加します
あとは style 関数が呼び出されたタイミングで中身を更新します

customElements.define("foo-bar", class extends LitElement {
constructor() {
super()
this._cssss = new CSSStyleSheet()
}
createRenderRoot() {
const root = super.createRenderRoot()
root.adoptedStyleSheets.push(this._cssss)
return root
}
style(template, ...values) {
const class_name = "c"
this._cssss.replaceSync(`.${class_name} {${String.raw(template, ...values)}}`)
return class_name
}
render() {
return html`
<div
class=${this.style`
border: 2px solid #aaa;
padding: 12px;
:is(input) {
padding: 4px;
}
`}
>
<label>name</label>
<input>
</div>
`
}
})

これで動くようになりました
ただこれだと最後に呼び出された style 関数の分しかスタイルを保持していません
replaceSync の代わりに insertRules にすれば追加できますが 増えていく一方です

同じ場所の更新ならルールを更新できるといいのですけど
テンプレートリテラルの機能を使ってみようかとも思いました
しかし あくまでソースコード上の同じ場所ということしかわかりません
CSSStyleSheet が CustomElement ごとなので 別インスタンスと混ざることはないですが map で繰り返すようなケースは対応できません

Lit の directive が使えるかと思いましたが directive は lit-html 側の機能なので LitElement と統合されていないです
CustomElement の参照は得られず 対応する Part の更新だけに焦点が当てられています
また Part が消えた場合の処理を記述する方法も用意されていません

いい方法がみつからないので とりあえず全部更新する方法にしました
update をオーバーライドすれば render の前後に処理を入れられるので 更新時に使ったルールだけを残し 他は削除します
スタイルの更新のための処理が増えてきたので 間にクラスを設けることにしました

class StylitElement extends LitElement {
constructor() {
super()
this._cssss = new CSSStyleSheet()
this._style_num = 0
this._style_used_rules = new Set()
}
createRenderRoot() {
const root = super.createRenderRoot()
root.adoptedStyleSheets.push(this._cssss)
return root
}
style(template, ...values) {
const class_name = "c" + (this._style_num++).toString(36)
const index = this._cssss.insertRule(`.${class_name} {${String.raw(template, ...values)}}`)
this._style_used_rules.add(this._cssss.cssRules.item(index))
return class_name
}
update() {
this._style_num = 0
this._style_used_rules.clear()
super.update()
const rules = [...this._cssss.cssRules]
for (const rule of rules.toReversed()) {
if (!this._style_used_rules.has(rule)) {
const index = rules.indexOf(rule)
this._cssss.deleteRule(index)
}
}
}
}

このクラスを使います
複数の style を使って map の中でも使うようにしています
また再レンダリングを兼ねて input の入力を全体で同期するようにしました

customElements.define("foo-bar", class extends StylitElement {
static properties = {
name: {},
}
constructor() {
super()
this.name = ""
}
render() {
return html`
<div class=${this.style`display: flex; flex-flow: column; gap: 10px;`}>
<div
class=${this.style`
border: 2px solid #aaa;
padding: 12px;
:is(input) {
padding: 4px;
}
`}
>
<label>name</label>
<input .value=${this.name} @input=${event => this.name = event.target.value}>
</div>
<div
class=${this.style`
border: 2px solid #f8d;
padding: 20px;
:is(input) {
padding: 4px;
}
`}
>
<label>name</label>
<input .value=${this.name} @input=${event => this.name = event.target.value}>
</div>
${["#f00", "#0f0", "#00f"].map(color => {
return html`
<div
class=${this.style`
border: 2px solid ${color};
padding: 12px;
:is(input) {
padding: 4px;
}
`}
>
<label>name</label>
<input .value=${this.name} @input=${event => this.name = event.target.value}>
</div>
`
})}
</div>
`
}
})

とりあえず動くようになりましたが render の呼び出しのたびに style で作るルールは全部新規に作り 古いのは消すという処理です
あんまりパフォーマンス的に優れてそうにないです
でもこれ以上がんばるならもう Emotion 使ったほうがいいような気もしてきます
ただ Emotion にしても同じルールが存在するかを確認して差分更新をするのでそこまで改善されるのかは疑問です

簡単にできそうで効率良くなるかもってところだと やっぱりルールの更新方法でしょうか
差分更新はがんばらないにしても もう少しなんとかできるかもと思います
CSSStyleSheet の削除って index 指定でしか消せず ひとつひとつ index を取得してから消すのって効率悪そうです
ルールの全置き換えなら 最初から新規の CSSStyleSheet を作って それと置き換えてしまう方がよいかもしれないです
それか 配列でルールの文字列を保持して最後に replaceSync を使うかでしょうか
Office に月額プランがあった
Office は普段全く使わないので PC を買うときは付属してないのを選んでます
たまにつかうときも OneDrive から開ける無料のウェブ版の Excel などを使ってます
場合によっては Google の方の Spreadsheet だったりです

ただ 今回印刷したり PDF にしたり他の人と共有したりがあります
ウェブ版でも印刷や PDF 化はできるようですが 少し不安があります
こういうときは安定のデスクトップ版にしておきたいです

短期間なのでサブスク版の Office365 を 1 ヶ月だけ契約できたりしないかなと探してみたら 月額払いもありました
現状では月 1490 円です
年だと 14900 円なので 10 ヶ月分

1 年使うのだと 1 年分で買ったほうが安いですが 1 ヶ月や 2 ヶ月程度ならこっちの方がいいですね

https://www.microsoft.com/ja-jp/microsoft-365/buy/compare-all-microsoft-365-products
Node.js 21 がリリースされた
Node.20 の LTS 化は来週みたい

Node.js 21 の新機能
https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V21.md#21.0.0

ブラウザ互換の WebSocket クライアントが追加されたみたいです
まだ実験的なのでフラグが必要です
Node.js をクライアントとして使いたいケースはパッと思いつかないですが テストには便利かも

fetch が stable になったみたいです
18 からフラグなしで使えてたのでもう普通に使ってましたが そういえばまだ experimental でしたね

glob 機能が追加されたみたいです
ただテスト関連の機能で内部的に使ってるだけみたいで公開されてないので fs モジュールをインポートしても直接使うことはできないようです
glob 機能だけでも使えるようになるといいですね

一番気になるところは --experimental-default-type オプションの追加
これでデフォルトを ESM に変更できます
これまではコマンド実行時に ESM と指定できず 拡張子を .mjs にしたり package.json を作って type に module を指定しないといけなくて面倒でした
これで ESM が使いやすくなる と思ったのですが これには問題もあるようです
ここのサイトで議論されている内容など詳しく説明されてました
https://jser.info/2023/10/18/node.js-roadmap-esm-by-default/

自分のコードの範囲だけ考えてましたが デフォルトが変わると node_modules のパッケージにも影響があります
古いパッケージは package.json に type が記載されていなくて CJS で書かれているので デフォルトが ESM になるとエラーになるということみたいです

たしかにそうですね
でも 現状で困っていてデフォルトを変えたいのは package.json を使わないスクリプトを実行するときです
npm パッケージは使わず 自分で書いたモジュールを読み込むときです
対象を package.json が無いところだけ にして node_modules の中の package.json があるところはこれまで通りで type を見て 無ければ CJS でいいと思うんです

package.json を作るところなら type: module の記載をするようにすればいいですし パッケージマネージャーなどが package.json を作るときにデフォルトで type: module になっていれば不便に思うところも無い気がします

package.json があって type が無いものまで ESM にするのだと 過去パッケージは使えないことにするしかないですし 中身を見て判断するというのはパフォーマンスが悪くなるなどが理由で .cjs と .mjs の拡張子に分けると決まったときに拒否されてたはずです
非同期処理と依存関係のアルゴリズム
先日書いた内容で 複雑で難しそうだし もっと楽なやり方で解決したというのがあったのですが やっぱり気になったので元々やろうとした方法でやってみたという話です

複数の対象が配列に入っていて 非同期処理で並列に処理します
それぞれの処理では 対象に基づいてデータを外部から取得して そのデータを使ってなにかするというものです
最後のなにかするというところはどうでもよくて 今回注目するのは取得部分です

楽な方法では 先に全ての対象で必要になるデータの一覧を作ります
それらをまとめて取ってきて終わったら各自の処理を行うというものです
これでもいいのですが 1 つ取得に時間がかかると それに依存しないものの処理の開始も遅れます

そうならないように各処理の中で必要なデータを取得するようにします
必要なデータは複数の対象で共有してるケースがあります
そういうときに 2 回取得しないようにしたいです
fetch やモジュールならキャッシュされてるから別にいいような気もしますが そうでもない場合も考えて効率化します

面倒なのは依存関係があることです
A には B, C が必要というものです
A の取得開始時に C がすでに取得を開始してるかもしれません
そういうときには C の取得は行わず すでに行われてる取得処理を待って それが終わったらその取得できたものを参照したいです
また B には A が必要ということもあります
そうなると循環参照になります
A は B の完了を待って B は A の完了を待つので いつまで経っても終わらないということになります

そういうことがあるので複雑なのですよね
嬉しいことに今回は依存関係は取得する前からわかっています
依存関係全部の取得開始を同期的に行えるので 取得開始は他の並列する処理と混在しません

コードで書くとこういうものです

const data = ["a", "b", "a", "c", "b", "a"]
const deps = {
a: ["b", "c"],
b: ["a"],
}
const load = (item) => {
return fetch(`http://localhost/${item}`).then(res => res.json())
}
const loadWithDeps = (item) => {
// ココの処理
}
const processItem = (item) => {
return loadWithDeps(item).then(console.log)
}
data.map(item => {
return processItem(item).catch(console.error)
})

とりあえずこんな感じでできました
試した感じは問題なく動いてそうです
確認しやすいように load はディレイを挟んで引数を返すものにしてます

const global_promises = {}

const load = (item) => {
return new Promise(r => setTimeout(r, 10, item))
}

const loadWithDeps = (item) => {
if (global_promises[item]) return global_promises[item]

const promises = {}

const resolveDeps = (item) => {
if (global_promises[item]) return global_promises[item]
if (promises[item]) return promises[item]
promises[item] = load(item)

const dep_items = deps[item] || []
const dep_promises = dep_items.map(resolveDeps)

return global_promises[item] = Promise.all([promises[item], ...dep_promises])
}
return resolveDeps(item)
}

const processItem = (item) => {
return loadWithDeps(item).then(console.log)
}

const shuffle = (arr) => {
const a = arr.slice()
for (let i = 0; i < arr.length; i++) {
const rand = ~~(Math.random() * a.length)
const tmp = a[i]
a[i] = a[rand]
a[rand] = tmp
}
return a
}

const data = shuffle(["a", "b", "a", "c", "b", "a"])

const deps = {
"a": ["b", "c"],
"b": ["a"],
}

console.log(data)

data.map(item => {
return processItem(item).catch(console.error)
})

デバッグを兼ねて 結果として出力するものは再帰の呼び出しの構造になってます
呼び出し順で結果が違うので実際に使うなら名前と取得したデータのオブジェクトにするなど必要です
Node.js のバージョンアップでフロントのビルドが動かなくなった
Node.js ってけっこう互換性は保たれてるので メジャーアップデートしても問題なく動くことがほとんどでした
特にフロント側は Webpack や Parcel や Vite などでビルドするだけなのでバージョンの違いはほぼ影響ないです
なので フロント側のビルドではプロジェクトごとに Node.js バージョンの管理はしてないです
Windows に入れてる共通の Node.js でビルドしてます
新機能が使いたくなったら LTS の範囲で更新してるものです

最近はそこまで使いたい新機能もなかったので その環境では Node.js 16 が EOL になる少し前くらいに Node.js 18 にしました
それからしばらくして 1 年近くビルドしてなかったプロジェクトを少し更新してビルドしようとしたところエラーになってました

node_modules は前回のビルド以降更新してなくてどうしてエラーが起きるのか最初は原因がわからなかったのですが 調べてみると Node.js のバージョンが原因でした
Node.js 16 から Node.js 18 に上げると package.json の exports フィールドの動きに違いが出ます
Node.js 18 では exports で公開してるもの以外は直接 import/require できなくなります
このせいで一部のライブラリ間の読み込みで失敗していました
正確に書くと postcss 系です
postcss の exports に書かれていないファイルを postcss 系のパッケージのモジュールが直接読み込もうとしてエラーでした

バージョンアップで解決されるはずですが こういう内部で依存関係として自動的にインストールされているものって そこだけを更新し辛いのですよね
このプロジェクトは全体的に古いもので構成されていて できるだけ変更はしたくないです
いつもはとりあえず全部最新にしてるのであまり困らないですが 古いもので最低限の更新だけするというのはやりづらいです
lock ファイルで問題のパッケージのバージョンだけ書き換えることも考えましたが 依存関係が複雑なところだとおかしくなることもありそうで直接触るのは避けたいです

ビルドにしか使わないなら EOL でも別にいいので Node.js のバージョンを古いままにするのが正解なのかもですね
Docker を使って古いバージョンの Node.js でビルドしてみました
ビルドはファイルの読み取りが多い処理なのでファイルが Windows 側にあるとかなり遅いです
もしかすると コンテナ内でリポジトリをクローンしてビルドして push するほうが早いかもしれません
メジャーアップデートとマイナーアップデート
semver 準拠だと 互換性がなくなる破壊的変更はメジャーアップデートで 互換性がある機能追加ならマイナーアップデートです
そういうこともあってか メジャーアップデートは破壊的変更だけを行って 機能追加はしないという方針のプロダクトもあるようです

メジャーアップデートというと 大きく変わって新機能もいっぱいで期待できるものという印象でした
互換性がなくなるデメリットはあっても それを上回る追加機能で便利になるのでコストを掛けてでも更新したいというものです
そのプロダクトとしては ◯周年みたいなものと近い感じで お祭りというかお祝いというかそういう雰囲気だと思ってました

ですが 機能追加はマイナーアップデートで行って メジャーアップデートでは非推奨機能の削除や次のマイナーアップデートのための互換性のない変更を行うだけとなると嬉しいものではないですね
むしろ嫌なもの

段階的に更新できるメリットがあるのかもしれませんが メジャーアップデートという言葉に嬉しさを感じられなくなってきて残念です
highlight.js を更新
ブログのコードハイライトがところどころ崩れてました
JSX や ${} が入ってくると崩れやすいようです
highlight.js はメジャーアップデートが進んでたのでアップデートしてみました
軽く見た感じでは改善されてるようでした

これに合わせて highlight.js は CDN から動的に取得するよう変更しました
これまでは CDN が落ちてたり重かったりすることも考えて 一応ブログのサーバーにライブラリをアップロードしていました
CDN が落ちることはめったに無いとはいえ 画面表示に関わる部分ですし インポートに失敗すると初期化処理が中断されるのでサイドバーなど関係ない部分もちゃんと表示されなかったり動作しなかったりすることになりますし
ですが そこまで重要なブログでもないですし ライブドアブログのストレージにライブラリを配置すると更新するのが結構面倒だったので CDN に頼ることにしました



highlight.js の言語モジュールについてですが 画面に必要なもののみインポートするようしました
最初は手抜きで 全部のページで同じファイルのロードで考えてました
しかし ごく一部のページでしか使ってないマイナー言語も多くありました
nim とか elm とか vbs とか
こういうのがあるたびにロードする言語を追加するのは面倒です
それに CSS しかハイライトしないページでも 関係ない言語のモジュールをいくつもロードするのはムダです

ということで highlight.js は言語モジュールを含まない core 版をロードして 言語モジュールはすべて別途ロードさせるようすることにしました
ただこうすると 本来は highlight.js 側で管理してくれているエイリアスを自分で管理しないといけなくて面倒でした
JavaScript の言語モジュールは javascript.min.js というファイル名です
js という言語名から自動でこのファイルをインポートできないので 自分でエイリアス情報を管理して js を javascript に置き換えてからモジュールをインポートします

また code タグごとに並列して処理すると 同じ言語の code タグが複数あるときに少し面倒です
言語ごとに Promise を保持するようにして 最初にロードするときに Promise を作って 2 つ目以降では その Promise が解決されたら処理という感じにしないといけません
また 依存関係もあります
HTML だと内部で CSS や JavaScript も表示するという感じです
JavaScript が内部で HTML を表示することもあるので循環参照します
上記のような単純な Promise だと循環参照したときにデッドロック化することもあり 対応がとても面倒です

ここを頑張る気力もなかったので 2 段階の処理にしました
先に今のページに存在する言語とその言語に依存する言語の一覧を作ります
それらを全部インポートしてから ハイライト処理に移ります
全部の言語モジュールをインポートするまでハイライト処理が始まらないので少し遅くなりますが 2 回目以降はキャッシュされてますし 全部の言語と言ってもそのページで使われるものだけで ほとんどのページでは 2, 3言語くらいです
それくらい別にいいかなと楽な方にしました



ところで ライブドアブログのファイルマネージャーは相変わらず使いづらいです
まとめてアップロードやまとめて削除する機能がないです
10 くらいなら手動でやってもいいですが 100 近くもあるとさすがに嫌です
ひとつひとつボタン押してられないので 自動化しました

ブラウザでファイルマネージャーの画面を開いてコンソールで下のコードを実行します
これで今開いてるフォルダ内の全ファイルを上から順番に削除します

while (true) {
const img = document.querySelector(`.ftpFileList img[alt="削除する"]`)
if (!img) break
img.click()
await new Promise(r => setTimeout(r, 1000))
const ok = document.querySelector(`input[value="削除"]`)
if (!ok) {
alert("削除ボタンがみつからない")
break
}
ok.click()
await new Promise(r => setTimeout(r, 1000))
}

最初の img が×アイコンのボタンなので ここのセレクタのところで条件に一致するものだけにしたり 残したいものを除外したりします
いつの頃からかフォルダの削除はできるようになってたので 本当に全部削除だとフォルダを消したほうが早いです

一応 API も用意されてるのですが 使いやすくないし面倒なのでこれのほうが楽だったりします
React で props で渡しても再レンダリングされないとは限らない
React で再レンダリングを防ぐために props として ReactElement を渡すといいとか言うのを見かけました
Parent コンポーネントの内側に Child コンポーネントを配置するとき Parent コンポーネントの中で Child を作るんじゃなくて Parent を作る側で Child も作って Parent に渡すのが良いとか
コードにするとこういうのです

const App = () => {
return (
<Parent />
)
}

const Parent = () => {
console.log("Parent")
const [state, setState] = useState(0)
return (
<div>
<button onClick={() => setState(state + 1)}>{state}</button>
<div><Child /></div>
</div>
)
}

const Child = () => {
console.log("Child")
return (
<div>child</div>
)
}

これの代わりに

const App = () => {
return (
<Parent>
<Child />
</Parent>
)
}

const Parent = ({ children }) => {
console.log("Parent")
const [state, setState] = useState(0)
return (
<div>
<button onClick={() => setState(state + 1)}>{state}</button>
<div>{children}</div>
</div>
)
}

const Child = () => {
console.log("Child")
return (
<div>child</div>
)
}

にします
App コンポーネントで Child をつくって Parent に渡しています
それで再レンダリングが防げるんだっけ?と思ったのですが Parent が返す ReactElement のツリーで {children} の位置が変わらないなら防げてます
例えば上記のコードだと 上側はボタンを押すたび Parent と Child がコンソールに表示されますが 下側だと Parent だけになっていて Child の再レンダリングを防げています

こういう場合には良さそうなのですが children として ReactElement を渡すところって children の表示条件や表示箇所が固定じゃなかったりします

const Parent = ({ children }) => {
console.log("Parent")
const [state, setState] = useState(0)
return (
<div>
<button onClick={() => setState(state + 1)}>{state}</button>
{state % 2 === 0 && (
<div>{children}</div>
)}
</div>
)
}

とか

const Parent = ({ children }) => {
console.log("Parent")
const [state, setState] = useState(0)
return (
<div>
<button onClick={() => setState(state + 1)}>{state}</button>
{state % 2 === 0 ? (
<div>{children}</div>
) : (
<span>{children}</span>
)}
</div>
)
}

上側はボタンを押すたびに children の表示非表示を切り替えます
非表示になると DOM の実体が消えるので 再度表示されるときに Child は再レンダリングされます

下側は常に children を表示していますがラップするタグが変わってます
親のタグが変わるだけでも同じものとして再利用されず再レンダリングされます

なのであんまり再レンダリングを防げてないんですよね
たしかに防げるケースはあるものの これで確実に防げるというものでもないです

以前 React 18 が出る前くらいにアンマウントされても state などを維持して再利用できる機能が紹介されてた気がしますが React 18 の新しいドキュメントではそれらしいコンポーネントやフックを見ないですし どうなったのでしょうね
Windows の VBScript のサポートが終わるらしい
自分には関係ないかなと思ってましたが そういえば使ってるところがありました
ちょっとしたツールでダイアログで入力するために使ってました

bat ファイルはこんな感じで書いておきます

wscript xxx.vbs

xxx.vbs の方はこういう感じです

Dim shell, txt
txt = InputBox("テキスト")

IF Len(Trim(txt)) Then
Set shell = WScript.CreateObject("WScript.Shell")
shell.Run "cmd.exe /c some-command --option " & txt & " & pause"
End If

これで入力用ダイアログが出て ユーザーの入力を受け取ってコマンドに埋め込めます
地味に便利なんですよね
コマンドラインを使えない人向けにもできますし

ただ VBScript が将来的に消えるなら PowerShell に置き換えたほうがいいのかもしれません
たぶんこんなので同じ動きになりそう

bat ファイル

powershell -NoProfile -ExecutionPolicy Unrestricted xxx.ps1

xxx.ps1

Add-Type -AssemblyName "Microsoft.VisualBasic"

$txt = [Microsoft.VisualBasic.Interaction]::InputBox("テキスト")
if ($txt.trim().length) {
some-command --option $txt
pause
}

VBScript より読みやすいしこっちでいいかな
getElementsByXXX の速度(続き)
前記事から続きます
HTMLCollection という特性上 遅そうに思ってましたが速かったです
プロパティにアクセスするたびに 検索していたらこの速度にはならないと思うので 工夫はされてるはずです
思いつくのだと DOM の更新時に変更が内部的に通知され その変更が HTMLCollection の監視対象であれば内部状態を更新している とかでしょうか?
それだと有効な HTMLCollection があれば DOM の更新速度が遅くなりそうです

試しに数百の HTMLCollection を作成してから body の子孫要素を大きく書き換えてみました
そのときの DOM 更新にかかった時間と HTMLCollection を作らない場合の DOM 更新にかかった時間を比べてみました

結果は差があるとは言えないくらいのものでした
どのタイミングで処理されてるのでしょうね
getElementsByXXX の速度
querySelector より getElementById のほうが速いという話題を目にしました
たしかにそうですね
でも普通のウェブページで 1 回のイベントあたりで呼び出す回数程度で体感できる差はまずないのでどっちでもいいと思います
という感じで流そうとしてましたが 気になったところがありました

querySelectorAll と getElementsByTagName (getElementsByClassName) の比較です
これも getElementsByTagName のほうが高速とありましたが 本当にそうなんでしょうか?
これらはどちらも複数の要素を得られますが 重要な違いがあります

querySelectorAll で得られるものは NodeList で だいたい配列みたいなものです
querySelectorAll を実行した時点で一致するものが入っていて ツリー構造に変化があっても NodeList は変化しません

getElementsByTagName で得られるのは HTMLCollection で Proxy オブジェクトみたいなものです
getElementsByTagName を実行した時点ではなく HTMLCollection のプロパティにアクセスした時点で一致するものです
常にその時点の状態に更新されています

HTMLCollection の動作的に getElementsByTagName を行った時点で検索する必要はありません
検索条件(ByTagName ならタグ名)を内部で保持するオブジェクトを作るだけでいいです
なので getElementsByTagName を行うだけなら 実際に検索する querySelectorAll よりも圧倒的に速いでしょう
ですが 実際に使う場合は検索した結果を取り出します
各要素にアクセスまでしたら getElementsByTagName のほうが速いとは言えないような気もします

ということで試してみました

const caseGetElements = () => {
const elems = document.getElementsByTagName("div")
const items = []
for (let i = 0; i < elems.length; i++) {
items.push(elems[i])
}
return items
}

const caseQuerySelector = () => {
const elems = document.querySelectorAll("div")
const items = []
for (let i = 0; i < elems.length; i++) {
items.push(elems[i])
}
return items
}

const measure = (fns, rep, mrep, cut) => {
const cases = fns.map(fn => ({ fn, results: [] }))

for (let i = 0; i < mrep; i++) {
for (const { fn, results } of cases) {
const start = performance.now()
for (let j = 0; j < rep; j++) {
fn()
}
const end = performance.now()
results.push(end - start)
}
}

return cases.map(({ results }) => {
const targets = results.toSorted((a, b) => a - b)
.slice(cut)
.slice(0, -cut)

const round = (x, d = 3) => +x.toFixed(d)
const len = targets.length
const min = round(targets[0])
const max = round(targets.at(-1))
const avg = round(targets.reduce((a, b) => a + b, 0) / len)
const med = round(len % 2 ? targets[~~(len / 2)] : (targets[(len / 2) - 1] + targets[len / 2]) / 2)

return { len, min, max, avg, med, results, targets }
})
}

console.log(measure([caseGetElements, caseQuerySelector], 100, 100, 10))

div タグを検索します
検索結果を for 文でひとつずつ取り出して配列に追加しています
この処理を 100 回したときにかかった時間を計測します
ブラウザでは最適化などで実行で速度にばらつきが出るので 100 回計測して速い遅いの両端 10 回分ずつを除外した 80 件分で 最大・最小・平均・中央値を出してます

結果はページによって違いが大きいです

このブログトップだと

  getElementsByTagName
    6 ~ 7.1ms, a: 6.47ms, m: 6.45ms

  querySelectorAll
    9.2 ~ 11ms, a: 9.646ms, m: 9.6ms

  div: 446, *: 4036

getElementsByTagName のほうが速いようです
1.5 倍くらいです

メインの方のブログでは

  getElementsByTagName
    5.1 ~ 5.7ms, a: 5.27ms, m: 5.3ms

  querySelectorAll
    18.2 ~ 19.7ms, a: 18.48ms, m: 18.3ms

  div: 374, *:4858

更に差が出ました
3 倍以上です

MDN のページでは近い結果になりました
いくつか試した限りではこのページが一番近かったです
https://developer.mozilla.org/ja/docs/Web/API/Performance/now

  getElementsByTagName
    2.5 ~ 3ms, a: 2.624ms, m: 2.6ms

  querySelectorAll
    2.6 ~ 3.4ms, a: 2.925ms, m: 2.9ms

  div: 177, *: 940

10% ちょっとくらいしか差がありません

Google の検索結果ページでは逆の結果でした

  getElementsByTagName
    15.3 ~ 17.6ms, a: 15.74ms, m: 15.6ms

  querySelectorAll
    11.9 ~ 14ms, a: 12.559ms, m: 12.3ms

  div: 1149, *: 2069

querySelectorAll のほうが速いです

検索対象のタグ数と一致するタグ数で考えると
検索対象のタグ数が多いほど querySelectorAll が遅くなり
一致するタグが多いほど getElementsByTagName が遅くなるようです

内部の最適化次第で変化しそうなものなので ブラウザやバージョンでも変わりそうですね
この結果は Chrome 117 のものです



ところで 各要素にアクセスせず getElementsByTagName と querySelectorAll を呼び出しただけの場合も一応計測しました

const caseGetElements = () => {
return document.getElementsByTagName("div")
}

const caseQuerySelector = () => {
return document.querySelectorAll("div")
}

結果はこうなりました
平均と中央値だけ載せてます
それぞれ上が getElementsByTagName で下が querySelectorAll のものです

このブログのトップ
  a: 0.005, m: 0
  a: 5.705, m: 5.5

メインのブログのトップ
  a: 0.007, m: 0
  a: 15.49, m: 15.3

MDN のページ
  a: 0.005, m: 0
  a: 1.113, m: 1.1

Google の検索結果
  a: 0.006, m: 0
  a: 4.411, m: 4.3

予想どおりですが圧倒的な差ですね
TypeScript の ! は競合しないのかな
TypeScript のコードを見ていたときのこと

fn(value!)

みたいなコードがありました
TypeScript の ! って JavaScript の ?. の ? みたいなポジションだと思ってたのですが違ったようです
!. でまとまってる必要はなくて ! だけの単体で使えて 直前の式の nullable を解除するようです

任意の式の末尾に ! が書けるようです
それって JavaScript の構文と競合するようなしないような

JavaScript での ! は式の前に書く単項演算子です
! の次は式である必要があります
一方 TypeScript の ! は式の末尾に書きます
競合するには 「expression expression」 が有効でないとだめです
「(() => {}) ``」 はどちらも式で満たしてますが特殊なもので後者はテンプレートリテラルである必要があります
! が入ることができないので競合できないです

意外と大丈夫なのでしょうか

1 つ特殊なケースで競合を見つけることができましたが 構文的に発生する場所でどっちかが明確なので問題にはならないものでした
そのケースというのはこれです

yield!+1

ひとつめの解釈方法はこうです

function* gen() {
yield !+1
}

このときの yield はキーワードで function*() {} の中でのみ書けます
yield false と同じです

もうひとつはこれです

const yield = 1
yield! + 1

function*() {} の外なら yield を変数に使えるので 変数に ! をつけたものと 1 との足し算です

function*() {} の中か外かで完全に分かれるので競合にはならないです
あと strict mode なら yield は変数に使えないです
プライベートメソッドをテストするのかの話
時々目にするプライベートメソッドのテストの話題
久々にまた見かけたので
テストすべきかどうかよりしたいかどうかだと思います

個人的にはクラスをほぼ使わないのでモジュールに置き換えます
モジュールが公開する関数と公開しない関数があって公開しない関数もテストするかです

少し極端なケースですが 2 つの非公開関数と 1 つの公開関数があるとします
非公開関数の片方は外部からデータを取得するもので もう片方はデータをフォーマットするものです
公開関数はこれらを組み合わせたものです

もう少し具体的な例

const getData = (opt) => {
// ...
}
const formatData = (data, opt) => {
// ...
}

export default (opt) => {
return formatData(getData(opt), opt)
}

こういうときに公開されてる関数だけで多くのパターンをテストするのって大変です
getData がデータベースや API やファイルからデータを取得する場合は フォーマットのテストのために毎回データを取得しないといけないです
そうなると 1 回 1 回の処理が遅くなります
getData と formatData をそれぞれテストすればフォーマットのテストのために毎回データの取得は不要です

中の処理がわかっていれば それをモックに置き換えることもできます
getData が API を呼び出すなら fetch を置き換えたり Service Worker 使うなどして 実際のリクエストを避けつつ公開関数を使って formatData をテストできます
でも面倒ですし formatData を直接テストすれば不要な苦労をしています

こういう場合や非公開関数に複雑なロジックがあればそこだけは単体でテストしておきたいです
非公開関数がたいしたことをしてなくて公開関数を数パターンテストすれば十分ってレベルならわざわざ非公開の関数までテストする必要ないと思います

まぁ JavaScript の場合はモジュール内の非公開関数や class のプライベートプロパティ機能で隠されたものはアクセスできないのでテストするのが難しいのですけどね
__privates みたいな名前でオブジェクトをエクスポートして 環境がテストなら非公開の関数を全部このオブジェクトに追加とか考えましたが そこまでするのもどうなのという気持ちです
こういうテストを考えると Python みたいな全部オープンの方針の方が助かるのですけどね
実際に動かす上で不要な処理であるテストのためにコードを書き換えるのはできるだけ避けたいですし
input と textarea のフォント
input や textarea のフォントってどうするのがいいんでしょうか

デフォルトを見てみると input と textarea では異なるようです
またブラウザでも異なります

Windows 環境で Chrome/Edge (117) を見ると

input:
  ユーザーエージェントの font-family: 空
  表示されるフォント: Arial, Meiryo

textarea:
  ユーザーエージェントの font-family: monospace
  表示されるフォント: MS Gothic

Windows 環境で Firefox (118) を見ると

input:
  ユーザーエージェントの font-family: MS Shell Dlg 2
  表示されるフォント: tahoma, Meiryo

textarea:
  ユーザーエージェントの font-family: monospace
  表示されるフォント: consolas, MS Gothic



どちらも等幅かと思ったら input はそうではないようでした
また等幅フォントのデフォルトはギザギザして汚い MS Gothic でした

monospace で表示されるフォントはブラウザの設定で変えることができ 私は別フォントにしてたので全然気づいてませんでしたが 設定してないブラウザでみるとすごく残念な画面になってました
(Chrome のフォント設定画面は chrome://settings/fonts)
基本的にはデフォルトのものにしておきたいですが さすがにこれは汚すぎで見るに堪えないです
変えておこうかなと思っても標準フォントで等幅ってこれといったのが思いつかないです

UI ライブラリの入力コンポーネントや世の中のページを見てると そもそも等幅にしてるところがそんなに多くないようでした
等幅にしているところはだいたいウェブフォントで独自のフォントを使っているようです
可変幅のところでは inherit にして親と一緒 つまり div や p などの通常のテキストと同じフォントにしてるのを見かけました
また input と textarea でフォントを分けず まとめて同じ可変幅のフォントを設定していたりです
たしかに分ける必要って特に無いと思いますし 等幅じゃなくて困ったということもありません

とりあえず本文と同じ Meiryo でいいかなと思いました



この考えでいくと テキストエディタも可変幅でも良さそうに思えてきました
プログラムで 「=」 や 「:」 や 「//」 の位置など 行頭以外の場所を縦に揃えたがる人は抵抗がありそうですが 行頭以外は基本揃える必要ないって私からすると別に問題無い気もします
考えてみると 可変幅になってる textarea でコードを書くことって普通にあります
「見直してみたら可変幅だね ここ」 くらいに気にしてなかったです
また 日本語がない半角フォントが設定されていて 半角のみ等幅になっていて 日本語が入ると可変幅になるところもありました
フォントの違いが気になりはしますが 一時的なところなら実害はないので放置でした

VSCode のフォントを Meiryo にしてもいいかななんて思いましたが 考えてみたらプログラミング用フォントは等幅なだけではなく 紛らわしい文字の差別化もしてくれてるのですよね
やっぱりプログラミング用フォントを使うのが無難そうです

他には 等幅のほうがパフォーマンス的にも優れてそうな気はしました
文字関係なく幅が固定のほうが事前にサイズ計算ができて重たいファイルを開いて画面に表示するときは差が出そうです
と思いましたが等幅でも半角全角が存在しますし 絵文字みたいなところでは完全に等幅になっていないです
それらも考慮しないといけないので あまり変わらないかもです

あとは 矩形選択したいようなものを扱うときでしょうか
ただこれはマルチカーソル機能があればそっちのほうが高度なので 重要でないかもしれません

どちらかというと SQL の結果など 表形式のテキストを貼り付けてきれいに見えないほうが困りそうです
インストールするソフトが減ってる
Chrome が勝手に Youtube や GoogleDrive のアプリ (Chrome を開くだけのリンク) を Windows PC にインストールしたようで Windows メニューの最近の追加のところに出てきていたので消しました
そのときインストール済みプログラム一覧のページに飛んだのですが リストの内容が思ってたより少なかったです
この PC はもう 1 年以上使ってるのですけど スクロールせずに収まるほど
昔はけっこうな量が並んでいたので 時代は変わったなと思います
最近は何するにもブラウザだけでほぼ終わりますからね

もしかすると ストアアプリのほうが増えてるのかも と思いもしましたが 数はありましたがデフォルトでインストールされてるもので使わないものばかりでした

今のところ自分でインストールして使ってるものはこれくらいです

ブラウザ
    Chrome
 
テキストエディタ
    VSCode

IME
    Google 日本語入力

キーボードユーティリティ
    AutoHotKey

アーカイバー
    7-Zip

画像ビュワー
    IrfanView

ペイント
    Paint.NET

その他
    WSL
    Node.js



以前は軽量のテキストエディタとしてサクラエディタを入れてましたが PC の性能が高めでメモリも十分に余ってれば VSCode で十分かなと この PC では入れてないです
ただ VSCode はフォルダを開く概念があるので プロジェクトに属さないテキストを見たり編集したりするときは少し扱いづらくて シンプルなテキストエディタを別に入れておいてもいいかなという気はしてます

Google 日本語入力は 入れなくても標準ので大丈夫かもと思って試してみましたが 無理でした
標準のは使いづらすぎな上にいい感じの予測変換が出てきてくれません
予測変換に大きく頼ってる自分としては必須なものでした
また 「今日」 と打って日付が出たり 「zh」 で 「←」 が出たり 郵便番号から住所が出たり と言った隠し便利機能も Google 日本語入力のほうが充実してます

画像の表示は Windows10 から標準のアプリが重たくなり さらに写真向けなのかスクショみたいなものの等倍表示がきれいに表示されなかったりで微妙なんですよね
以前は Windows7 の頃のビュワーに戻してましたが それなら軽量で高機能なフリーソフトを使ったほうがいいかと思って IrfanView を入れるようになりました
Windows11 では標準のビュワーが改善されてるのでしょうか

ペイントソフトは標準のが使いづらいので昔から Paint.NET にしています
リサイズ・トリミング・フォーマットの変更くらいなら問題ないのですが 中身をいじったり色を調整したりオブジェクトを切り抜いたりしようとすると機能不足感が強いです
本体はシンプルでプラグイン任せが強いソフトで 昔は色々なプラグインがありましたが 度重なるプラグインの互換性をなくすメジャーアップデートでほとんどが使えなくなりました
Firefox の拡張機能みたいな状態ですね
追従している作者のプラグインでも新しいバージョンで動作が変わっていて求めてるのは古いバージョンだったりすることもあり 昔できていたことができなくなっていたりします
GIMP 等への乗り換えを検討中

プログラミング言語系は WSL 内に入れることが多いですが Node.js のみ Windows にも入れています
Windows 側で動かしたいことが多めなのと Node.js は PHP などより Windows/Linux の差が少なく Windows 側に入れて動かすのに抵抗が少なめです

この PC では Windows Terminal や新しい PowerShell は入れてないです
VSCode のターミナル機能があれば 勝手に落ちてる Windows Terminal はなくてもいいかなという思いで Windows Terminal は入れませんでした
PowerShell はなんだかんだ古い方で困ってないので 必要なったら入れればいいかと思って入れてないままです
条件つきメソッドチェインしたい
最近はメソッドチェインを長く書くようなライブラリをあまり使うことがなかったのですが 久々につかうとやっぱりチェインに条件をつけたくなります

こういうの

const val = new A()
.method1()
.method2() // ← flag が true のときだけ追加したい
.method3()

ライブラリによっては考慮されていて チェイン内で分岐できるようなメソッドがあったりします
しかし分岐したいなら変数にいれて if を使ってというスタンスのライブラリもあります
そうするとメソッドチェインできれいに書けるメリットが失われます

JavaScript の機能が増えてきているといってもやっぱりこういうのはいい方法がないです
ありがちなのがラップして独自の分岐メソッドを追加する方法

const val = $(new A())
.method1()
.$(flag, $ => $.method2())
.method3()
.unwrap()

しかし method1 などはラップしたオブジェクトが持っていないので Proxy が必要になります
また最後にアンラップして中身を取り出さないといけないです
このタイプは過去何度か作ってみても 利便性のいまいちさや Proxy に抵抗があって実際には使ってないのですよね

全てのオブジェクトにメソッドを追加する prototype 拡張がもっとシンプルになるのですが prototype 拡張は積極的に使うのはどうかなというところです
特に全てに影響する Object.prototype ですし

ただ キーがシンボルなら 影響はほぼなさそうですし ありなのかなと思ったり

const val = new A()
.method1()
[chain_if](flag,
$ => $.method2(),
)
.method3()

チェインの記法がちょっと変わるのが専用構文ぽくて ひとつ上の例よりは好きかもしれないです

動かす用のサンプル

Object.prototype[Symbol.for("if")] = function(cond, fn) {
if (cond) {
return fn(this)
} else {
return this
}
}

class A {
arr = []
method1() {
this.arr.push(1)
return this
}
method2() {
this.arr.push(2)
return this
}
method3() {
this.arr.push(3)
return this
}
}

const flag = true // or false
const val = new A()
.method1()
[Symbol.for("if")](flag,
$ => $.method2(),
)
.method3()

console.log(val)
state と input のフォーマットをどこで揃えるか
DOM 外に state としてフォームの入力を保持する系ライブラリを使うとき今でも迷うところ

input は基本 値を文字列で扱います
type が number とか date でも value は文字列です
一応 valueAsNumber とか valueAsDate とかありはしますが 扱いづらいところがあるのでこれらは使いません
state 側では数値や Date 型など文字列以外で持っていることも少なくないです

そういうときにどうするかです

ひとつは input の入出力の際に毎回変換することです
value に渡すときに文字列に変換して 変更のイベント時に文字列から state の型に変換して state を更新します
それぞれの input の受け渡しのコードが長くなるのと 都度変換が必要なところが微妙です
日付型を文字列にフォーマットするくらいなら パフォーマンスに影響することはほとんどないと思いますが ユーザーの入力のたびにやるのってとてもムダに思えます
また 一度の再レンダリングで全部の input 分の変換を行うので 数が多い場合はパフォーマンスに影響無いとも言い切れないです
それに input は input そのものじゃなくてコンポーネントになってることもあります
その場合は変換がもっと重い処理のこともあります
別のオブジェクトを参照して内部の値を照合して とか

ベストな方法とは思えないなと思いつつ仕方ないか でやってる方法です

別の方法は フォーム用に state を用意するというものです
編集中として 編集開始時に state をコピーします
そのときそのままのコピーではなく input 用の表現に変換しておきます
そうすれば編集中 state は input と同じ型なので 編集中に変換不要です
state の値を そのまま input にセットして 変更後の値をそのまま state にセットできます
保存ボタンなど編集を完了するイベントで 編集中 state を本来の state に変換して更新します

変換は編集開始と終了の最小限なので こっちのほうが良さそうに思ってます
ただ state が増えるのはデメリットもあります
編集中に元 state が変わったときの扱いが難しくなります
全部新しい state で置き換えるならいいですが state 内の変更があったプロパティだけとなると自分で差分を見つけてそこだけを編集中 state に反映しないといけないので面倒です
React だと state 全体を更新だとしても変更を検知するための useEffect が必要になるのでローカル state を増やすこと自体が気が進まないです

また 編集中 state は基本的にそのフォーム内だけのものです
入力途中の state を参照したいとしてもほとんどの場合はそのフォーム内部の話です
なので問題なさそうに見えるのですが 稀にフォーム外の場所にも反映したいことがあります
例えば画面テーマの色を選べるとします
選んだ色をフォーム内でプレビューとして表示することができますが 実際にヘッダーとかサイドバーとかをその色にして確認したいなんてこともあります
そういうフォーム外にも編集中 state を反映したい場合は面倒が増えます

常にどっちの方がいいとは言えないので 未だにどっちにするか迷うところです
一応今のところは React は 1 つめの方法で lit (WebComponents) は 2 つめの方法にしてることが多めです

他にはフォームの機能によって変えてたりです
保存ボタンが存在しないフォームだと state を分けても都度 編集中 state を本来の state に反映することになって分ける意味がないです
なので 1 つめの方法です
キャンセルやリセットボタンがあるフォームなら 初期 state と編集中 state の 2 つを保持する必要があります
なので 2 つめの方法です
リセット機能はなく 保存ボタンだけがあるフォームだとどっちでもいいのでやっぱり迷います
ReactNative ってこれから流行るの?
以前は ReactNative が話題になることも結構あったと思うのですが 今ではほぼ見ることがないです
人気どころではなく Angular 的な使いたい人がひっそり使い続けるようなポジションになったと思ってます
比べて Flutter は Google のイベントで大々的に発表していて たまにニュースで見かけます
以前に比べてどんどん改善されていってるようですし クロスプラットフォームでモバイルをサポートするなら もう Flutter 一強かなと思ってました

しかし 最近ネットで見かけたものでは これから ReactNative が流行ると言ってる人がいました
昔の情報かなと思って日付を見たら今年の中頃でした
公式サイトを見てみてもこれといった大きな更新の発表もないですし そうは思えないのですけど
ReactNative が流行る理由があるのでしょうか

ウェブで React が流行ってると言っても ReactNative になると HTML や CSS は使えません
比較的に似てるコンポーネントは提供されていますが ウェブほど使いやすくないです
コンポーネントなど React の考え方が使えるくらいにしかメリットはないと思います
あと言語が JavaScript/TypeScript というところも Dart よりは良いのかもです

しかし ウェブを前提としたライブラリは使えませんし ReactNative ユーザーはウェブよりはるかに少ないのでライブラリもそれほど充実してるように思えません
あと React という名前のせいでウェブの情報が混ざっていたりして紛らわしいです

またあくまで View 部分のみしかやってくれないので ReactNative だけだと自分でネイティブレイヤーを制御しないといけないです
期待してるのは JavaScript だけですべて行えて Android や iOS の固有の知識が要らないものです
そうしようとすると Expo というのを使わないといけなくなりますが これもまた癖があります
それに更新が速すぎて全然安定してないので たまにしか使わないアプリを更新しようとしたら SDK のサポートが終わっていて SDK を更新するには変更点が多すぎます
Expo 機能のほんの一部しか使ってない場合でも影響範囲が多かったりです
ググってみてもこの辺が問題で 試しに作ってみる段階には使えても本番としては使えるものじゃない みたいな感想を目にします

それらが改善される何かが出たのかなと思って調べても特になにもないのですよね
何年も前の Chrome を使い続けてる会社がある?
アクセスログを眺めてたときのこと 異常にアクセス数が増えてるページがあって 生のログを見てみると同じユーザーから 10 回以上の連続したリクエストが来てました
詳細を見るとリファラなしの直接アクセスみたいで UA を見ると古い Chrome (80 くらい)
クローラーとか自動でアクセスしてきてるやつで UA をそのときの Chrome に設定したままなんだねーと思ってました

そんなこともあるかーで流しておこうとしたら 一番最後だけ UA が変わってるのに気づきました
ほぼ最新の iPhone から同じページへのアクセスです
見直してみると連続アクセスは機械的にリクエストしたにしてはタイムスタンプが奇妙です
1 秒に何回もあるわけではなく数秒おきでアクセスされていました
アクセス間隔は等間隔ではなくばらつきがあります

もしかして本当に古い Chrome を使って人がアクセスしていたのでしょうか
80 だと 3 年半ほど前のバージョンなので たぶんページが正しく表示できないと思います
表示されないのをサーバー負荷などの問題と思って何度もリクエストを繰り返して 最終的に諦めて iPhone からアクセスしたということなんでしょうか

アクセス元のドメインを見るとお堅そうな日本の会社でした
割りとあり得るかもしれない……

今の時代ブラウザは自動更新されるものなのに古いバージョンに固定なんてありえないですよね
IE が消えて 世の中のページも最新機能を取り入れるところが増えているので見れないページが多くなります
インターネットに接続せず社内システムを見るために使うというならまだわかりますが こっちのサイトにアクセスしてる以上 インターネットにつながってます
古いバージョンは脆弱性のパッチも当たってないのにそれでネットしてるって セキュリティ的にどうなの?って思います
開発者ツールで要素を確認しようとしたら要素が消えるときの対処方法
ポップアップで表示される部分の HTML 構造を確認したいとかありますよね
でも右クリックの「検証」を押したり devtools にフォーカスするとポップアップが閉じてしまうということがあります

原因は blur や focusout イベントを使って 要素がフォーカスを失ったらポップアップが閉じるようになっているから
devtools にフォーカスをあてるとページからはフォーカスが失われて このイベントが起きてポップアップが消えてしまいます

このためにけっこうな力技で対処していました

devtools の Elements タブですべての blur と focusout イベントのリスナを削除してから ポップアップを開きます
ポップアップの要素を消す処理が行われないので ポップアップは開いたままになります
本来の動作が行われなくなるので 確認後にリロードが必要です

別の手段では setTimeout で遅延して debugger を実行します
3 秒後に debugger を実行するようコンソールで実行してから 3 秒以内にポップアップを開きます
debugger が実行されると JavaScript の処理は実行されず固まるので ポップアップが開いたまま devtools を操作できます

こんな方法を使ってましたが 正当な方法がありました

devtools で Rendering タブを開きます
出てないならメニューの More tools から選択します
リストから「Emulate a focused page」を探してチェックを入れます
これでページにフォーカスが残るので devtools にフォーカスを当ててもポップアップは消えずに残ります

デフォルトで有効になってて欲しいくらいの機能です
コンポーネント関数と普通の関数
コンポーネントの関数って props を受け取って JSX の要素を返します
入出力は普通の関数と同じで 違うのはコンポーネント関数は React が呼び出して中でフックが使えるということ
普通の関数でフックを使うこともできますが それはフック関数という扱いになってフックのルールに則って使う必要があります

フックを使わないならコンポーネントにする必要がないようにも思います
例えば

{user && <UserInfo user={user} others={others}/>}

の代わりに

{user && UserInfo({ user, other }) }

と書いても動きます

そう思ったきっかけは逆で コンポーネント内で処理をまとめるために関数化していた部分があって 引数は 1 つのオブジェクトで JSX の要素が返ってくるものでした
これならコンポーネント化しても良さそうと思ったのですが むしろコンポーネントにする必要があるのかと疑問に思いました

コンポーネントだと React.memo が使えて props が同じなら再レンダリングを避けれます
ですが React.memo ってそんなに使うことがなくて 明らかに遅いようなところだけです
フックも使わないような小さいコンポーネントなので基本は memo しないものです

扱いやすさ的には普通の関数のほうが好きです
ただ その利点は React に依存しないという点が大きいですが フックを中で使わないなら命名規則くらいの差しかありません

見やすさ的には 上の例みたいに {} の中で使うならどっちもどっちですが そうでないなら普通の関数は実行するために必ず {} が必要になり 読みづらくなります
コンポーネントのほうが HTML ライクな記法で書けて見やすいです

迷うところですが 将来的にフックを使いたくなるかもと考えればとりあえず最初からコンポーネントにするのが無難かもしれないです

開発時に限れば コンポーネントにすると strict モードで複数回レンダリングされます
マウントされるコンポーネントが増えるほど重たくなります
普通の関数だとこれがないので少しですが速いです
本番ビルドには影響しないものなので これを理由に選ぶのはどうかとは思いますけど
セミコロン猛者
ちょっと前に見かけた JavaScript のセミコロンの話題
初心者だとどこに書けばいいのかわからない という話でした

たしかになくても動いてしまう以上 初心者はどこで必要なのか分かりづらい気がしますね
C や PHP など類似の構文でセミコロン必須の言語に触れたことがあれば そこではセミコロンが無いとエラーなのでちゃんとルールを把握してなくても使っていれば感覚で必須な場所はわかってきます
その流れで JavaScript を書いていれば セミコロンを文末に書く場合に必要な場所には困らないと思います
しかし JavaScript が初めてなら 要らないところにつけても 必要な場所につけなくても動きます
ちゃんと構文を理解してなければ難しそうです

基本は文末に書く と言っても if や for 文や関数宣言では不要です
セミコロンを書かない部分は 改行もセミコロンもなく 1 行にまとめて動く部分です

function a() {} a()

const a = function() {} a()

上は動きますが 下は構文エラーで動かないです
なぜかというと 上は関数宣言なので 「}」 が来るとそこで文の終わりと判断できます
なので自然と次のトークンの a は新しい文の始まりとわかります
下だと代入式なので 「}」 で文が終わるかは不明です
なので次のトークンの a も読み取ってみて ここで構文エラーになります

それならここで自動で文を分割してくれれば と思わなくもないですが 先を読んでみないとわからないのはパフォーマンスやパーサーの複雑度に影響しそうですし 構文エラーのバグが意図せず動いてしまうとかあるのでしょう

話を戻して 初心者にこういうところまで理解してというのは無理があると思います

構文チェックのツールやフォーマッターを使えば良いという意見もありそうです
ですが これも初心者がいきなり使うにはハードルがあるように思います
初心者というのは環境を準備というのが一番苦労するところです
JavaScript はメモ帳レベルのテキストエディタでも HTML を書いて script タグに JavaScript コードを書けばとりあえず動くという手軽さが魅力なのに こういうのの準備ってそういうメリットが失われますからね

なので個人的にはセミコロンは書かないほうがいいと思ってます
コード上入れるとしてもフォーマッターが入れて 人が書く必要はないと思います

ただその議論では予想外の方法を取ってる人がいました
すべての文にはセミコロンを入れるルールにしているようです
if や for のあとに入れても意味ないだけで別に問題ないですからね
たしかに統一感があり 迷うこともないので それならそれでいいんじゃないかなと思えました

こんな感じでしょうか

function a(value) {
if (value > 1) {
for (let i = 0; i< value; i++) {
console.log(foo(i));
};
} else {
console.log(value);
};
return {
foo: {
bar: {
baz: () => {
}
}
}
};
};

ただ最後に } が並ぶとき セミコロンなしのほうが全部が揃って見た目がきれいには思います

				}
}
}
}
}

意味が違う } でも全部一緒だとむしろ分かりづらいという考え方もできますが
React だとリアルタイム反映のほうがやりやすい
React などのライブラリを使わず DOM 操作で作ってるページだと なにかが変わったときにリアルタイムにあっちもこっちも反映って面倒です
だから確定ボタンを押したみたいなときに全体に反映させて それまでは外部に影響させないとか最小限の部分だけに反映させたりということが多いです

そうなってるものを動きを変えずに React で置き換えそうとすると 逆に面倒なんですよね
React だと state に持ってる情報から今の画面状態を作るので普通にやると全部が変わってしまいます
確定ボタンを押すまでは他に影響しないようにしたいなら 新たに state を用意する必要があります
グローバルの state と ローカルの state を用意して 普段はローカルの方だけを更新して ボタンが押されたらローカルをグローバルに反映するみたいなことになります
(グローバルと言ってもその state を使う範囲でのグローバルなのでページ内だったりタブ内だったりで プログラム的なグローバル変数というわけではないです)

React だと state 特にコンポーネントローカルの state はあまり増やしたくないのですよね
ローカルに state を持つことでそれを更新するための処理も必要になります
特に props や別フックの更新に応じて state を更新する必要があれば 変更を監視して更新するための useEffect もセットで必要になります

フォームや state 系のライブラリを見てみても 他の値の更新で state を更新する必要がある以上 どれもこの問題あるように思うのですが いい感じに解決できるものがあるのでしょうか
StackBlitz の Node.js のサーバーフレームワーク
StackBlitz で Backend 側テンプレートを見てると 見慣れないものがありました

Egg.js
https://github.com/eggjs/egg

Feathers
https://github.com/feathersjs/feathers

H3
https://github.com/unjs/h3

Nitro
https://github.com/unjs/nitro

ラインナップには Express や Koa はあるのに Hapi や Fastify はありません
それらよりもここにあるのは有名なのでしょうか?

ただ Egg.js と Feathers は言われてみると結構前にも見た覚えがなんとなくあります
2018 年か 2019 年くらいにフレームワークを探してたときだったかもです

たしか Egg.js は Koa を中で使ってて 中国でよく使われてるやつだったかと思います
Feathers はリアルタイム系らしいので WebSocket 系のようです
ソースコードを見てみると Socket.IO を使っていました

今でも StackBlitz のテンプレートに並ぶくらいには使われてるのでしょうか

H3 と Nitro は聞き覚えがないので新しいものかもしれません
リリース履歴をみるとどちらも去年からみたいなので新しいようです

H3 の方は Minimal H(TTP) framework らしく 小さめのライブラリみたいです
ミドルウェア系で Express/Koa に近い感じです
Router がついてるので Koa よりは高機能かもしれません
Express と Koa の違いみたいにハンドラで受け取るオブジェクトが異なっています
ただ Express と互換性のある形にもできるそうです
そうしないと Koa があまり流行らなかったみたいなことになりそうですからね

Nitro の方は Nuxt 関連のもののようです
Nuxt を強化できるらしいですが Nuxt なしでも使えるようで Nuxt を使う場合と使わない場合で設定の書き方が分かれてました

どれも今のところは特に使うことなさそうです
Effector
Stackblitz のテンプレートを見てると Effector というのを見つけました
ビジネスロジックを記述するためのライブラリだそうです
フレームワーク依存ではなく React/Vue/Solid で使えるようです

CounterEffects の例をみるとわかりやすいですが ロジックをコンポーネント外に書くことができて コンポーネントがスッキリするのがいいところみたいです

たしかにロジックはコンポーネントとは切り離したいですしね
でも実際はコンポーネント外でロジックを書くと面倒も多かったりします
フックで得られる値を参照できないので全部引数で渡す必要があったり
そのまま渡すとフックなどの API にロジック側が依存するので それを避けるなら間の層を設けないといけなくなったりします
なのでコンポーネント内に全部書いてしまうこともけっこうあって コンポーネントが大きくなりすぎて見づらい ってなったりします

このライブラリではその問題を解決してくれるようです

ですが 簡単な例を見る限りでは良さそうかも?と思うくらいでしたが複雑な例になってくるとコードもわかりづらくなってきます
そういうのを見てるとそこまでいいのだろうか?と疑問にも思えてきます
このライブラリを使うコード自体がパッと見てすごくわかりやすいとかシンプルにかけそうというのではなく 前提知識がないと難しそうなものですし

もっと知名度が出てユーザーも多くなってくれば考えますが現状だと使わないかなというところでした
Github のマルチアカウント
少しは涼しくなってきたし 転職活動でも再開しないとなぁとか思ってます
面倒だし考えるだけで憂鬱です(働きたくないでg

求人では ウチこういう技術使ってますよー とか書いてます
見てると Github を業務で使ってるところもあるようです
たしかにそういうところもあるよねー くらい思ってました

……が それに関連している ある書き込みを見かけました
個人ブログだったか Qiita みたいなプラットフォームだったか 場所まで正確に覚えてませんがこういう内容です
「ウチの会社では Github は個人アカウントを使うことにしています」

そういえば Github のアカウントは規約で複数アカウントを禁止しているから 仕事用と個人で同じのを使わないといけないという話題が何度かネットで話題になってましたね
話題に上がっても はっきりした結論はなく 問い合わせた返答で規約的にはダメというのと問題ないというのの両方あるらしくはっきりしない感じで終わってました

ググって出てきたのを見てる感じでは 仕事でも個人のを使わないといけないという主張は
「規約的にそう読める」
「(日本の?) Github で働いてる人に聞いた」

アカウントを分けて問題ないという主張は
「英語で本社に問い合わせたら問題ないと解答された」(複数あり)

のようです

問い合わせて問題ないと言われてるなら問題ないでいいと思います
普通に考えて 仕事で個人用アカウントを使うなんてありえないですし 仕事で個人のアカウントバレなんてもっとありえないです

コメントで見かけた
「アカウントを切り替えるのが面倒な人や 仕事でやったことも個人のコントリビュートにしたい人が 自分に都合良くしたいがためにそう広めてる」
というのがしっくりきました
英語圏ではそもそもこれが問題として議題に上がることもない(私が調べたわけではない)というのもこの説の信憑性感じられます

何にしても迷惑な話です
転職した先が個人アカウント使ってなんて方針だと 絶対お断りですが 事前にそういう情報がわからないというのも怖いですよね
Github を使ってるところは避けたほうが無難という気もしてきます
display: contents でラップするときの問題
display に contents を指定すると DOM のツリーとしては存在するのに存在しないように扱わせることができます
React の Fragment に近いイメージです
要素をまとめるための要素が存在するのに 無いものとして扱わせたいときに使います

例えば

<style>
.row {
display: flex;
}
</style>

<div class="row">
<div>A</div>
<div class="wrap">
<div>B</div>
<div>C</div>
</div>
<div>D</div>
</div>

div.row は display: flex なので横並びになりますが 2 つめの子要素は普通の div なのでここは縦並びになります

ABD
C

B と C を囲む div.wrap を無いものにしたいときは これに display: contents をつけます
これで

ABCD

という並びにできます
flex だと div.wrap も flex にすればいいのであまり必要なかもですが grid だともっと役立ちます

そんな display: contents ですが 完全に無いものとしては扱えず不便なときもあります
例えば 子要素にスタイルを当てるとき

<style>
.container > * {
margin-top: 10px;
}
.contents {
display: contents;
}
</style>

<div class="container">
<div>A</div>
<div class="contents">
<div>B</div>
<div>C</div>
</div>
<div>D</div>
</div>

こういうケースでは div.contents にマージンは効かず この div を透過して B と C にマージンが設定されることもありません
A と D にだけマージンが設定されます

React などをつかっていると div.container の子要素はコンポーネントで生成していて イベントをまとめて受け取るためなどで display: contents の要素でトップレベルをラップしてるとかもありえます
そうなるとこういう子要素に共通でスタイルを当てたいというときにうまく動かないケースが出てくるのですよね

CSS セレクタの > や + などでも display: contents は透過してくれるといいのですけど
ドット絵を拡大表示するときは image-rendering: pixelated
IE 時代と違って 画像の拡大縮小が起きても自動できれいに表示してくれるので ブラウザ上で画像を拡大縮小表示するときのアルゴリズムを気にすることは特になかったです
ですがドット絵を拡大するときはなめらかにされると微妙なことになるので そのままドット感を残して拡大してほしいです
IE 時代は CSS に独自プロパティがありましたが Chrome でもあるのかなと調べると image-rendering を使えば良いみたいです

元のサイズ

dots

拡大サイズ(通常)

dots-large

拡大サイズ(pixelated)

dots-large
React コンポーネントと値の初期化
今でもスッキリしない React での初期化処理の扱い方

const Component = (props) => {
// ...

return (
<div>
<Edit
value={value}
onChange={onChange}
/>
</div>
)
}

Edit コンポーネントがあって何かを編集するものです
React なので value を渡して編集があれば onChange で受け取って親側で更新するという 親側で state を管理する作りです
これだと value の値を初期化やチェックして修正するときに親側でやらないといけなくなります
ですが そのロジックは Edit のものなので Edit でやってもらいたいです
そうなると ユーザーの操作なしで Edit のマウント時に onChange を呼び出して更新することになります
仕方ないのですがそれはなんか気持ち悪さがあります

親で state 管理しないものなら 初期値を渡すだけで修正した値は Edit の中で持っておいて 親が値を使いたいときに Edit から取り出します
これだと自然なのですよね

Edit コンポーネントのモジュールから関数もエクスポートすれば ロジックは Edit モジュールの中で持てます
しかし Edit を使うのに 初期値を作る部分とコンポーネント部分で 2 つの処理が必要になるのが面倒です
例えば API で選択肢を取得して初期値が null なら最初の選択肢を初期値にする場合 初期値のために関数で API を呼び出して コンポーネント内でも選択肢を作るために API を呼び出す必要があるので 関数とコンポーネントの両方で API を呼び出すことになります
それを避けるなら関数は初期値の他に選択肢も返して それもコンポーネントに渡すという作りになりますが やることが増えます
やりたいのは単純に Edit コンポーネントを使って props を渡すだけにしたいのですけど

onChange の場合でも困るところはあります
変更イベントを beforeunload などと関連付けていれば ユーザーは何も編集してないのに変更ありとして扱われます
初期化が null の場合に値を入れたり 不正文字を除去など繰り返し行っても問題ないものならいいですが そうでない場合は判断するのが難しいです
入力値を制御するライブラリでは onChange を即呼び出すものを見かけますが 組み合わせるライブラリの相性とかもあるのか ときどきうまく動いてなかったりするのですよね
レンダリング中に同期的に呼び出すと問題ありそうですし useEffect 通しても その他の useEffect との順序が影響しそうです
.NET Framework と .NET を混ぜて使う
.NET Framework で作られたレガシーなアプリがあって サポート期間とか色々な事情で .NET には移行しない
そのアプリの一部機能を使う別アプリを新規に作るけど 最新 LTS の .NET 6.0 を使いたい
そんなことできるのかなと思って試してみたら普通にできた

ソリューションの中に新規プロジェクトを作るとき .NET のプロジェクトを選ぶ
作ったプロジェクトの依存関係の設定でソリューション内から .NET Framework のアプリを選ぶ

あとは .NET Framework 側のメソッドを単純に呼び出すだけ

WindowsFormsApp1.Class1.something()

これで動いた

ただし WinForms のアプリの Form を開く機能はコンソールアプリからは呼び出せない
コンパイル時にエラーは出ないけど実行時にアセンブリが見つからないってエラーが出る
.NET Framework 側のプロジェクトのビルド時に依存アセンブリは考慮されてそうだけど 見つからないらしい

.NET 側もアプリの種類を WinForms にすれば問題なく開ける
.NET アプリから .NET Framework 経由で Form を開くと .NET Framework アプリでも WinForms のアセンブリは .NET 側の SDK のものが使われてるのかな?

コンソールアプリとしてプロジェクトを作ってしまった場合はプロジェクトのプロパティを開いて 「アプリケーション > 全般」 の部分で以下のように設定すれば使えるようになる

出力の種類 → Windows アプリケーション
ターゲット OS → Windows
Windows フォーム → チェック入れる

WPF なら Windows Presentation Foundation の方にチェック