JSConf JP 2024 ノート
- 日付: 2024/11/23
- スケジュール: https://jsconf.jp/2024/schedule/
- 資料まとめ: https://zenn.dev/yumemi_inc/articles/2024-11-24-jsconf-jp-2024
- 🧠: 感想・思ったことなど
- 🔎: 追加で調べたこと
おかわり
日付: 2025/2/28
アーカイブ: https://www.youtube.com/watch?v=q5ZkrvP46RQ
Promise.try: シンプルで強力な同期/非同期統合の未来 - 実装の深層とPromiseの進化
-
2024/10 TC39 104でStage4 (8年!)
-
いままでの課題
- 同期・非同期どちらの結果ももたらす可能性がある関数に対して、同期的なエラーが発生したときcatchメソッドのコールバックが呼び出されない
- TS Playground
function syncOrAsync(id) { if (typeof id !== "number") { throw new Error("id must be a number"); } return db.getUserById(id); } // 同期的なエラーが起きた場合catchメソッドのコールバック関数は呼び出されない syncOrAsync("42") .then(console.log) .catch(err => console.error("Async error catch (then-catch): " + err));
-
いままでの解決策
-
Promiseを生成してその中でsync/asyncな関数を解決するやり方
- 不要な非同期処理が挟まれる
- 冗長
new Promise((resolve) => reoslve(syncOrAsync())) .then(console.log) .catch(console.error)
-
Promise.try()
以前にも同様の機能を提供するライブラリはあった -
Promise.try()
が解決するものPromise.try(syncOrAsync, "42") .then(console.log) .catch(err);
- 不要な非同期処理が挟まれない
- シンプルな構文
- Promise.tryは Promise.prototype.thenが Promise不要で使えるようなもの
-
- わずか7行だけど、知らないといけない概念がいくつか出てくる
- NewPromiseCapability (Abstract Operation)
- PromiseCapability
-
JSCではJavaScript上で実装されている
-
当時は仕様として用意する需要があるかがわからなかったため、Stage4まで8年かかった
- async/awaitでも同様のことが実現できそうだった(AIIFEを使う)
- しかし、Promise.try相当の
p-try
の需要が多いことがわかった
-
その他Promise動向
- Cancellation in Promise
- ブラウザ依存のDOMExceptionを起こすAbortController互換のCancellationでないと進展しにくいので議論が難航中
- 👀今後が気になる
UI 開発における ヘッドレス UI ライブラリの重要性とデザインシステムへの取り入れ方
- https://youtu.be/E3yTtaGr7V8?t=2381
- UI開発難しい
- アクセシビリティに注目
- セマンティクスも保つためのARIAガイドライン(ARIA Authoring Practices Guide)
- 例: Tab
- 左キー・右キーでタブ間フォーカスを移動できる
- UIコンポーネント開発には開発者がアクセシビリティについて知っていないといけないといけない課題がある
- ヘッドレスUIライブラリ
- スタイルをもたずに「振る舞い」だけを提供する
- アクセシビリティ面はあらかじめ提供されている
- スタイル面はユーザが自由に定義できる
- ヘッドレスでないUIライブラリの課題
- 独自のデザインを必要とする場合、ライブラリの詳細を知る必要がある
Connecting the dots – Web Accessibility through Internationalization and Localization
-
言語が違うと理解ができないということはアクセシビリティ上の問題ともとらえられる
-
Disabilitiesとは人々の能力と人々が置かれている環境のミスマッチ
-
l10nはアプリケーションを特定の文化・地域・言語に適応させること
-
映画Inside outでは子供が嫌いな食べ物の表現が地域ごとに変えられている
- US: ブロッコリー 日本: ピーマンl
- US: ブロッコリー 日本: ピーマンl
-
ユーザストーリーとして重要な部分から改善していくのがよい
JavaScriptを支えるエコシステム(漫才)
- https://youtu.be/2BXwigWGjWQ?t=3119
- パッケージマネージャ
- Yarn v4
- 公式プラグインがデフォルトになった
- TypeScriptの@typesパッケージが自動インストールされるなど
- Yarn v4
- Edge Worker
- Fastly Compute
-
StarlingMonkey
- WASM最適化されたSpiderMonkey -> ランタイムがWASMになっている
- 🧠 JS on StarlingMonkey on WASM Runtimeということか
-
StarlingMonkey
- Fastly Compute
A Tour of Anti-patterns for Functional Programming
- https://youtu.be/E3yTtaGr7V8?t=3058
- JavaScriptで関数型プログラミングを行うことに対する課題についての発表
- 関数型プログラミングをどう使うか
- 良い設計をするため
- 考え方はプログラミング言語によらず使うことができる
- 例外 vs Either/Result
- Either/Resultは型で作用を可能な限り明示的に扱うようにする
- 常に使えばよいのか?
- 欠陥(Defects)
- 事前条件・事後条件が満たされていないもの
- 例外はメソッドレベルとアプリケーションレベルの中間のレベルの欠陥に対して使える
- Railway Structure
- Effectがない世界とある世界をつなぐ操作
-
of
,map
,chain
-
- くるまれた値を取り出す仕組みがないとネスト地獄が起きる
- fp-tsではユーティリティがある(右コード)が、直観的ではない
- Reilway Structureはコードの書きやすさが怪しい
- Effectがない世界とある世界をつなぐ操作
- 複数の作用を組み合わせる
- TypeScript上でうまく扱える仕組みがない
- Scala, Haskell, Rustでは解決している
- 🧠言語レベルで仕組みが足りないと、それっぽいライブラリを使ってもコーディングレベルでは限界がありそうだという感想
Web can be fun!
- https://youtu.be/ew1zmA7y9q8?t=1969
- ブラウザのWeb APIを使うといろいろおもしろいことができるよという事例集(役に立つとは言っていない)
- https://theuselessweb.com/
-
Battery Status API
- 用途: ユーザ端末のバッテリー状況によってアニメーションをオフにしたりなどの省力化するなどができる
- MIDI API
- Bluetooth API
- Gamepad API
- Shape Detection API
- カメラから人の顔を検出して、自動で顔が中央に来るようにカメラを回転させるデモ
- 何か作れたらオープンソース化することで、他の人がそれからインスパイアしてさらに面白いものを作ってくれるかもしれない!
日本の住所CSVデータを活用した英語住所変換ライブラリを作った話
- https://youtu.be/2BXwigWGjWQ?t=4125
- romajip
- 日本語を英語にしたかった
- デジタル庁から配布されている市町村データは150MB
- 苦労したところ:どのように圧縮するか
- 小字(こあざ)削除
- 古い土地の小区画を表す表現だが、住所体系から廃止
- データ量としても大きい
- 町・村の接頭辞はコード側で付与するように
使い方(romajipレポジトリより)
- 小字(こあざ)削除
import { romajip } from "romajip"
const japaneseAddress = "東京都渋谷区神南1丁目23-1"
const parsed = romajip(japaneseAddress)
if (parsed) {
console.log(parsed)
/*
Output:
{
japanese: [ '東京都', '渋谷区', '神南', '1丁目23-1' ],
romaji: [ 'tokyo', 'shibuya-ku', 'jinnan', '1-chome23-1' ]
}
*/
} else {
console.log("Unable to parse the address.")
}
-
🔎 辞書データのサイズが4.5MBとのことだったが、デモサイトではJSは1.2MBだった
- br圧縮されているから
- br圧縮されているから
-
🔎Longest Matchどうやっているのか
- 県 > 市 > 区 みたいな階層の辞書を用意
- 文字列が長い順にソートして、最初にマッチしたものを返してLongest Matchを実現している
ステップバイステップで進めるYahoo!知恵袋のフロントエンドリアーキテクト
- https://youtu.be/2BXwigWGjWQ?t=4675
- 質問件数 2億8000万、解答総数 6億5000万
- 様々な問題があるが時間が限られているとき、どのようにカテゴライズして対応していくか
- 🧠リファクタリングデー、改善デーみたいのは結構いろいろなところでやっているのを見るな
- Fat Controllerへの対処
- コード設計を行っても、実際のコードがないとイメージがわかないのでリファレンス実装は重要
- リアーキテクトでよくなった点
- コード見通しがよくなった
- ユニットテストが書きやすくなった(テスト容易性が上がった)
- JavaScriptコードを徐々にTypeScriptに置き換えていく戦略
- 🧠js側で機能開発されていくところをdiffで確認してts側に追いつき統合していくのは大変そう
- ユニットテストがメンテナンスできていない課題
- mockが乱用されている
- 副作用のない関数だけmockを利用するように改善
- mockが乱用されている
Storybook との上手な向き合い方を考える
- https://youtu.be/E3yTtaGr7V8?t=4049
- よく考えずにStorybookを導入すると後々大変になるかもなので目的を明確にして導入しましょうという話
- zero-configがうれしいときはパッと作りたいとき
- Storybookはzero-configをうたっている 🤔
- Storybookはプロジェクト依存の何かと組み合わせる設定がたいてい必要
- 導入することによる恩恵は大きいがメンテナンスをする覚悟が必要
- Storybookが使われる目的
- Storybookはテスト方針としてブラウザを使わないテストからブラウザを使うテストの形式に最近はなってきている
- 🧠Storybookを採用するときに考えないといけないことがいろいろありそうだということが理解できた
- 🧠Storybookに限らずライブラリ導入や設計方針などを決めるときにプロジェクトのフェーズやチーム構成、大変になることとうれしいことのトレードオフを考慮すべきだと思う
あなたの知らない Function.prototype.toString() の世界
- https://youtu.be/E3yTtaGr7V8?t=4691
- Function.prototype.toString()がどのように使われているかの紹介
- Function.prototype.toString()って?
- 関数のコードを文字列で返すメソッド
const add = (a, b) => a + b;
console.log(add.toString());
// (a, b) => a + b
- ユースケース: 関数の転送
- Puppeteerの Page#evaluate()で使っている
- 🔎Puppeteerドキュメントには文字列化していることが書いてある
- ソースコード上でtoStringの箇所があるかというとそこまで追えなかった。。
- ユースケース: ネイティブコード判定
- 組み込み関数をtoString()すると
native code
を含む文字列を返すので、それでpolyfill判定できる - core-jsのpolyfillは
native code
を返すようtoStringをオーバーライドしている
- 組み込み関数をtoString()すると
- ユースケース: Dependency Injection
- AngularJS(Angularの前身)でコールバック関数の引数から何をDIするかランタイム側で判定するのに利用されていた
- minifyで引数名が変わってしまうと壊れる(当時はminifyが一般的でなかった故)
30 Minutes to Understand All of RegExp Syntax
- https://youtu.be/E3yTtaGr7V8?t=7665
- oxcコントリビュータの方
- ECMAScript仕様では Pattern が正規表現の構文
- RegExp = Pattern + Flag
/hello/vs
----- -- flag
pattern
-
Flag
- ES2024時点で8種類
- u/vフラグは正規表現を拡張したり、制限したりする
- vフラグ使うことをお勧め
- Literal Pattern
- 正規表現文法にエラーがあるとそこでエラーになる安全よりの挙動をする(堅牢性)
- RegExp Constructor
- 文字列をわたすのでエスケープが必要
- 特になければ、Literal Patternを使うべき
-
Pattern
- 仕様はあいまい性を排除するために、すごい冗長に書かれているのですべて知っている必要があるかというとそうでもない
- エスケープが多くなると何をエスケープしているかがわからなくなる(可読性の低下)
- 正規表現の制御文字をエスケープしてくれるAPIが提案中(stage3)
- JSの文字列が内部的にはUTF-16として扱われていることによる問題
- 見た目上の文字(grapheme)が、内部データでは複数になることがあって、正規表現1文字でマッチしないケースがある
- vフラグによりそれを複数のバイトになっている文字を単一のUnicodeとしてみなす扱いにできるので、見た目上との乖離をなくすことができる
/#.#/.test("#🔎#") // false /#.#/v.test("#🔎#") // true
-
- 上の問題は2つ以上のUTF-16 Code Unitで構成される文字の問題だったが、そもそもUnicode上でも複数のCode Pointで文字を構成するものがある
-
\p
Binary Unicode properties of stringsを使うと解決できる
-
Character class
[]
- よく使っているやつだけと名前は知らなかった
- vフラグ使うと
-
--
でグループから特定の文字を除外したもの という定義ができる
-
-
- 文法的に正しくても、意味的に不正な場合にエラーとなるもの
-
a{3,2}
とか
-
- 文法的に正しくても、意味的に不正な場合にエラーとなるもの
-
ES2025以降追加仕様
-
RegExp Modfiers
- 一部分だけフラグを有効無効にしたいやつ
-
Buffer boundaries (
\A
,\z
) (stage 2)
-
RegExp Modfiers
-
- 仕様上あるけど、昔のWebブラウザとの互換性用なので開発者は使わないほうがよいもの。。
React への依存を最小にするフロントエンドの設計
- https://youtu.be/2BXwigWGjWQ?t=5942
- Reactに限らずフレームワークへの依存を最小にする考え方が学べる
- 技術はさまざまな紆余曲折を経て今の形になるため、どういう経緯で現状こうなっているかを知ることは大切
-
Remixはランタイムへの依存を最小にしているのでアダプタを経由すると多くの環境で動く
- 🧠 Remixさわれてないので雰囲気理解するためにも触っておいた方がいいかも
- 依存を最小にする理由
- エコシステムの変化に追随する負担・切り替え負担は大きい => 減らしたい
- プロダクトの寿命 > フレームワークの寿命 => フレームワーク切り替えは必ず起こる
- フレームワークを切り替えるタイミング
- エコシステムが寿命を迎えるとき(フレームワークのサポートがなくなる)
- プロダクトで実現したいことができないとき
- 問題点: 特にuseEffectが入っている問題のある状態をテストでの再現が難しい
- カスタムhooksなどでロジックはコンポーネントの外に出す
- 🧠コンポーネントからどう使われるかは意識しないhooksにしたい、hooksから返す関数はclick時に呼ばれるかdrag時に呼ばれるかは意識しない
- 技術選定
- ライブラリ自体がReactに依存しないライブラリ
- swr -> Tanstack Query
- Recoil -> Jotai (v2以降React依存しない部分が切り出された)
- Vanilla JSで使えるライブラリ
- フレームワーク
- 規約よりAPI
- 明示的なAPIは参照個所を終える
- 🧠 ファイルベースルーティングは規約だけどファイル構成見ればわかるので明示的(?)
- エスケープハッチがあるもの(フレームワークのレールから外れたいときがある)
- 規約よりAPI
- ライブラリ自体がReactに依存しないライブラリ
- 設計
- 🧠VS Codeの開発者はデザインパターン(gang of tour)の方だったのか Erich Gamma
- 依存性の逆転(DI)
- 腐敗防止層
- 一休社での実践
- hooksにロジックを押し込む
- 🧠
style = sva({})
はなんだろ?- panda cssのAPIかな https://panda-css.com/docs/concepts/slot-recipes
- 🧠イベントハンドラもhooksで作ってしまうんだな
- Dateは標準APIだけどラップしている(腐敗防止層)
- そのうちTemporalが来るので
- 標準APIでもAPIが変わるようなことはある(Javaが通った道)
- テスト
- コンポーネントに依存しない状態遷移のテストが書けるのが良い
- 🧠transitionはXStateのAPI?
- hooksにロジックを押し込む
React CompilerとFine Grained Reactivityと宣言的UIのこれから
-
React Compilerは何を解決するもので、他のフレームワークではどうしているかということが学べる
-
宣言的UI
- 最近のUI定義手法としては「状態をもとにUIを宣言する」という考え方がweb,モバイル問わず主流
- 仮想DOMは宣言的UIを実現する仕組みの一つ
- React, Vue.jsで採用
- 仮想DOMの問題
-
仮想DOMは純粋なオーバーヘッド
- (1) 差分検出はコストがかかる
- ReactではO(n)のアルゴリズムとなっているのでそこまで大きくない
- (2) 仮想DOMの構築自体コストがかかる
- React Compilerは「(2) 仮想DOMの構築自体コストがかかる」を解決するもの React Compiler
- (1) 差分検出はコストがかかる
-
仮想DOMは純粋なオーバーヘッド
- React Compiler Playground
- React CompilerがやっていることはuseMemoとReact.memoを使えば実現できるが、開発者が意識して利用する必要がある
- React Compilerはそれを自動で行ってくれるところが良い
- Vue.js SFCもコンパイルで最適化されている
-
JSX
- 独自文法のテンプレート(vue, svelte)よりも、既存のツールチェインの恩恵を得やすいのがメリット(独自文法だとツール側(biomeとか)に対応を後回しにされがち)
-
Fine-Graiend Reactivity
- 通知がきたところだけDOMを更新するという方針
- 仮想DOMのような大きな木構造を構築しない
-
SolidJS
-
JSXだが、仮想DOMを使わない
-
SignalsはFine-Grained Reactivityを実現する仕組み
-
Signalsは宣言的UI上で使うときにインタフェースと柔軟性のバランスがとれているところが好まれている
-
-
SolidJSの注意点
- レンダリングは1回のみ
- React脳だと起こる問題: 算出プロパティは関数にする必要がある
- 例
const [count, setCount] = createSignal(1); const increment = () => setCount(count => count + 1); // const double = count() * 2; // ❌ const double = () => count() * 2;
- 早期リターンができない
-
SolidJS/Svelte 5/Vue Vapor
- Fine-Grained Reactivity
- 開発者の書いたコードはコンパイルされ実際のDOM操作を直接行うコードに変換される
-
Signals標準化の動き
- 現状は各フレームワークが独自に実装していて、互換性なし
- proposal-signals
-
まとめ
- Reactは仮想DOM方式を継続しつつ、React Compilerは仮想DOMオーバーヘッドの解消を目的としている
- 仮想DOMを使わない方式(Fine-Grained Reactivity)方式で宣言的UIの実現するものも増えてきている
-
-
Vapor Revolution: Unleashing Vue evolution and Potential with Vapor Mode
- https://youtu.be/2BXwigWGjWQ?t=13125
- Vapor Mode
- パフォーマンス上の最適化
- オプトインで使える
- Compilerの変更点
- Runtime
- ランタイムでのコードサイズも50%ほど小さくなる
- Virtual DOMモードとの互換性
- Vapor Mode: Composition API/<script setup>が使える
- Virtual DOMモードとVapor Modeを共存できる
- Vapor IR
- IRのユースケース
- クロスプラットフォーム
- 最適化
- 別の言語への変換
- 🧠IRでも木構造を表現している。Vue ASTはVue Templateの木構造を表現するものだからVue Templateに依存していて、IRは依存しないというのが違い?
- IRのユースケース
- Vapor ModeはVue Template以外のコードも対応
- JSX, Svelteとか
- inclusion-vapor
- 現在はブラウザのみ対応だが、プラットフォーム依存しないものを目指している
- vuejs/core-vapor を読む
幸せの形はどれも似ているが、不幸なプロジェクトはそれぞれの形がある
-
計測できる環境になっていることが重要(可観測性)
-
Google Developers Japan: パフォーマンスバジェットのご紹介 - ウェブパフォーマンスのための予算管理
- 一度作ったものは放置すると劣化する
-
パフォーマンス改題が発生する状況の傾向
- ドキュメントをちゃんと読まずに機能を開発するとアンチパターンになりやすい
- ドキュメントをちゃんと読まずに機能を開発するとアンチパターンになりやすい
-
不幸なプロジェクト = 何らかの形でパフォーマンスの予算を食いつぶしたもの
- 「何らか」のはそれぞれの事情がある
- タイトルの意図
-
「推測するな、計測せよ」 by Rob Pike
-
フロントエンドの計測 = Synthetic monitoring (合成モニタリング) - MDN Web Docs
-
計測するときはコードから意味を推測しない
-
本来はアプリケーションごとに指標があるべきだが、WebVitalsはよくできているのでそれを指標としましょう
-
Lighthouse
- DevTools > Lighthouse > Analyze page load
- SIは全部の総計なので見る意味なし
- LCPを追う
- LCPの対象が表示されるまでの経路を追う
- LCPの対象が表示されるまでの経路を追う
- DevTools > Lighthouse > Analyze page load
-
縦のグループがRTT
-
Chrome DevToolsの学び方
- chrome for developpers
- More tools全部動かすべし
-
高度なツールは環境依存しがちなのでプリントデバッグを使う
- いろいろなプロジェクトを横断的に見る立場だとこれがよい
-
問題点を完全に除外して何点上がるかが理論値
- そこにどれだけ近づけるかを考える
-
初期設計でミスると簡単には改善できない
- 「安易なハック」を起点ことが多い
-
関連
リアルアセットとWebのシームレスな活用のためのパスキー
- https://youtu.be/2BXwigWGjWQ?t=17366
- ログインに手間取って電車に乗り遅れてしまった人を救くうために何をしたか
- タイトルにパスキーとあるが、Webの機能や運用努力をフル導入してログイン体験を改善した話
- 2要素認証のワンタイムコードまちがボトルネック
- 「パスキー」をユーザに認識させるかさせないか
- パスキーについて認識させ説明することを選択
- どのようにユーザにパスキーを認識させるようにしたか
- ユーザ登録後にパスキー導入を促す
- ログイン問い合わせにたいしてパスキー登録を促す
- 通知メールでお知らせ(メールを開く率もそこそこ多かった)
- スクロールしないでも全部見えるようにする
- パスワードマネージャ使っているかでログイン体験を変える
- パスワードはvisibility: hiddenをしておくと、パスワードマネージャ経由で入力されるとパスワードが自動で入るので判定できる
- 3rd Party系のGA, Recaptchaを入れるとwebvitalsのパフォーマンスはどうしても落ちてしまう
-
- チーム構成: フロントエンド開発x1, バックエンド開発x2, QAx1, デザイナーx1, PdMx1
JavaScriptのモジュール解決の相互運用性
-
CommonJS (CJS)
-
-
// 🔎 // 3. If X begins with './' or '/' or '../' // a. LOAD_AS_FILE(Y + X) // b. LOAD_AS_DIRECTORY(Y + X) // c. THROW "not found" require("./foo"); // LOAD_AS_FILE(X) 1. ./foo.{js,json,node} // LOAD_AS_DIRECTORY(X) ("foo" + pakcage.json mainフィールド) 2. ./foo/package.json#mainを見る -> ./foo/package.json#main // LOAD_INDEX(X) 3. ./foo/index{.js,.json,.node}
- index.js使うのは非推奨
- ES Modules (ESM)
- 拡張子は必須
- ブラウザ側にたって考えると、CommonJSみたいないろいろな拡張子付きのファイルを探すのに毎回HTTPリクエストを送ることになる
- 拡張子は必須
-
ESMとCJSはどう識別されるか
- 直近package.json見る > ファイルの拡張子 > ファイルの構文
-
exprot
フィールド- "import", "requre", "module-sync" ("module sync"はnot top level await ESM file)
- https://nodejs.org/api/packages.html#conditional-exports
- 拡張子なしでimportしているとき
- Fake import
- トランスパイラがいい感じに処理してくれてる
- Sloppy import
- ESMとして動作
- 拡張子だけ補完される
- Deno
- Fake import
- Sloppy ESMはここでの定義DenoではSloppy importと呼ばれる
-
-
各フレームワークどうなっている?
-
アプリケーションではSloppy ESMでもいいのでは。自分でランタイムを決められるので。
-
デュアルパッケージの管理はライブラリ開発者としてはダルい
-
import m from "./foo.ts"
はOK?- TS 4.7では.js onlyだった
- TS 5.0
--allowImportingTsExtensinos
- バンドラがある前提であればOK (だいたいフロントエンドアプリはこれ)
- TS 5.7
--rewriteRelativeImportExtensions
-
--rewriteRelativeImportExtensions
- tscの結果をそのまま実行する場合
- TypeScript的にはちゃんとESMで書け
-
import path aliasは面倒になるケースが多い
- 使っているツール(Linter, Bundler, Transpiler, exc...) がすべて動くように設定する必要があるので
- 各ツールが参照するSingle Source of Truthにできるとよい
- Node.jsのsubpaths module
-
ESMへの移行は少しずつ動いている
- ESMでなくても動いてる・ツールが裏でうまくやってくれる状況がありモチベーションとしては大きくないため
JavaScriptのイテレータとイテラブルの概要と課題、未来
interface IteratorResult<T> {
done: boolean;
value: T;
}
interface Iterator<T> {
next(value): IteratorResult<T>
return(value): IteratorResult<T>
throw(e): IteratorResult<T>
}
interface Itrerable<T> {
[Symbol.iterator](): Iterator<T>
}
- Array, Set, MapもIterableなので
Symbol.iterator
メソッドを持ってる
> new Array()[Symbol.iterator]()
Array Iterator {} // Array Iteratorはnextメソッドを持っている
-
for ... of
では Symbol.iteratorが関数であるかでIterableかを判断している - ジェネレータはイテレータでもありイテラブルでもある
- bunのconsoleはイテラブル
- イテレータあんまり使われてなさそう
- 🧠 asyncイテレータは使えそうな機会が見つけられそう
- イテレータを操作するメソッドが少なく、便利に使えない
- Iterator Helpers
- %IteratorPrototype% -> %ArrayIteratorPrototype% -> Array Iterator
- %IteratorPrototype%/%ArrayIteratorPrototype%は直接アクセスできない隠れオブジェクト
- getPrototypeOfを使えば取れる
- Iterator HelpersによるECMAScriptの仕様変更
- 独自オブジェクトに Iterator Helpersで追加されたメソッドを継承できる(Iterator.from)
const iter = { next() { if (Math.random() > 0.8) { return { done: true, value: null } } else { return { done: false, value: 42 } } }
const o = { [Symbol.iterator]() { return iter } }
console.log(o.map) // undefined
Iterator.from(o).map(x => x * 2).toArray() // [82, 82...]
Web標準の進化を止めない!Baselineというブラウザサポートの考え方
-
https://youtu.be/ew1zmA7y9q8?t=20226
- Baselineの現状についての話
- Interoperable: すべての主要ブラウザで対応
- The interop project
- Webのinteroperabilityを増やす取り組み
- 年始にその年に通すWeb Platform Testを決めてがんばる
- 開発者の1/4の人がWebの新機能をキャッチアップするのが大変だと思っている
- Push APIがinteroperableになるまで7年かかった
- Newly available 20XX
- その年にinteroperableになった
-
text-wrap: balance
知らなかった - Baseline
- Webアプリケーション開発者側としてブラウザAPIのサポート状況を確認できる仕組み
- Widely available
- interoperableになってから2年半(30か月)たったら安定とみなされる
- Limited available
- 一部のブラウザサポート
- W3C WebDX Community Group で議論
- State of HTMLもやってる
- web-features
- YAML
- BCDの細かい機能をカテゴライズして整理したもの
- 人がマニュアルでBCDをカテゴライズしている
- @mdn/browser-compat-data (BCD)
- JSON
- ブラウザ開発側
- 本当の最新情報はBCD
- 2つに分かれているのはなんで?
- 1つの機能とはという考え方の違い
-
Web Platform Status
- いろいろクエリができる
- BCD ->web-featuresへマッピングが完了した割合
- BCDとweb-featuresの関係イメージ
- baselineのそれぞれの状態をどう考えればよいか
- <baseline-status> web component
- Webサイト上に埋め込めるタグ
- WebRUM Insights
- 2025年はBaselineのツールが増えていく!
React Server Componentsとは何であって何でないか
- https://www.youtube.com/watch?v=k68gPt0LI_g
- React Server Components(RSC)に対する誤解を解く発表
- RSCはPHPとかGraphQLっぽいと言われる
- RSCコード書き味
-
use client
,use server
でクライアントとサーバのコードを区別
-
- React Server Componentsはアーキテクチャ名
- Server Components / Client Components / Server Actions
- React Server Componentsの大体の機能はNext.jsで先行的に実装されていた
- Next.js独自の機能はそこまで多くない
-
- バンドルサイズ
- マークダウンパーサーの例
- 従来はクライアント側でパースするのにクライアント側にパーサライブラリもバンドルする必要があった
- RSCではサーバで処理した結果だけを送ればよい
- マークダウンパーサーの例
- バンドルサイズ
- PHPとの違い
- コンポーネント思考 HTML/JS/CSSをまとめて扱える
- データフェッチのメモ化
- GraphQLとの違い
- RSCはGraphQL精神的後継
- RSCのRFCはRelay/GraphQLを先導してきた方
- 従来とのアーキテクチャの違い
Data Fetch | Client <- Rendering Server | |
---|---|---|
MPA | Server | HTML |
React CSR | Client | - |
React SSR (初回画面表示以外はCSR) |
Server | HTML |
RSC (SSR) | Server | HTML chunk コンポーネント単位 |
RSC (Navigation) | Server | RSC Payload Stream |
- RSC今後
- 現状は Next.js, Waku がサポート
- 他のフレームワークもサポートに対応中
- RSCが開発された背景
- Metaレベルの規模(10万コンポーネント!?)のアプリケーションでスケールできるような目的
静的解析で実現した効率的なi18n対応の仕組みづくり
-
条件分岐などすべてのコードベースでもれなくi18n対応が難しい
-
一部のページだけi18n対応なので既存の全コードベースを検証するESLintが使えなかった
-
一部ページからだけ依存関係のあるi18n対応できていないリソースを特定するESLintを実装
-
ESLint CLIを直接実行ではなくスクリプトから実行するようにした
- ESLint CLIだと大本のファイル(今回でいうとページのファイル)に対するエラーとして出力されてしまう。
- どのファイルが原因かがわからない
- そこを調整するためにスクリプトでラップしている
-
今度は時間がかかる問題
-
ESLintだと起点ごとに解析が実行されるので起点が多い、依存関係が多いだと重複した処理が大きくなる
- ESLint内でも訪問済みファイルリストを持っているが、それも起点ごとなので使えない
-
ESLintのScope Analysis(変数のスコープを解析する機能)にも時間がかかる(今回の要件ではいらない機能)
-
ESLintをやめて、翻訳漏れ検出に特化したツールを作る
- 🧠 汎用性と特化のトレードオフ
-
スプレッドシートで管理された翻訳情報が更新されたとき、どの画面に影響出るかがわからない(翻訳情報でコードを検索する必要があった)
-
翻訳キーに対して対象コンポーネント・起点ページをマッピングするツールを実装
-
ベースブランチにマージされるたびにCIで更新
-
i18n時の課題
- 翻訳キーでないキーを指定できてしまう
- スプレッドシートのキー一覧から型を生成
- i18nライブラリの型定義自体をpnpmのpatchコマンドで更新してしまう
- 型の削除( string型から literal型にするようなもの)はTSでは難しいので
-
コードの書き方が統一されない問題はESLintのカスタマイズで積極的に対応している
WYSIWYGウェブページビルダーを支える技術とSever Driven UIへの拡張
- https://www.youtube.com/watch?v=vGntvLkVHQ0
- ノーコードでLPを作成する社内ツール
- コンポーネントの状態をJSONで管理
- コンポーネントは1階層に並んでいるが、階層構造はchildrenという属性で管理
- Webview <=> Mobile間で双方向通信したい
- カスタムURLスキーム
- 単方向・セキュリティリスクも有る
- Channel Messaging APIを利用した仕組みを実装
- カスタムURLスキーム
- 同時編集
- Yjsの利用を検討
-
Server Driven UI
- UIの構成を完全に構造データにしてクライアントでそれを元に描画する方式
- プラットフォームが多様な場合向け(?)
- クライアント側の実装を簡素化できるってどういうことだろ?
- プロトコルを自前で定義 (Card UI Protocol)
- Tree Node, Style, Event handler
- Nocodeツールで編集した内容を Card UI ProtocolのJSONに変換してサーバで保存
- 各プラットフォームごとに読み込んだJSONを元に描画
クルマのサブスクサービスをNext.jsで内製化した経験とその1年後
- https://www.youtube.com/watch?v=d84gAFv6hyQ
- 外部の会社が開発したシステムの内製刷新
- 新機能開発も並行で開発
- 金額が大きいので金額関連の機能は注意深く
- Atomic Designが合わなかった
- 通信が入るのはOrganismsに入れていたが、膨れてしまう
- featuresに切ることを考えたがどちらに入れるかまよってしまう
- BCCD Designへの切り替えを検討
- 技術上の属性ベースでのディレクトリレイヤベースのディレクトリたどづらい
- Storybookの運用は良好
- 🧠コード変更時にどういう開発ステップを踏んでいるか気になる
- 最近のUIライブラリでないと新規参加の開発者のハードルが上がる
- フロントエンド・バックエンド分かれたことで、UIに関する体験向上をフロントエンドチームだけでできるようになった
徹底解剖!医療業務システムのReactコンポーネント設計
-
動くかつ正しいコードが書ける(保守性がある)ことが重要
-
-
200ページ以上の規模のアプリケーションの話
-
-
コンポーネント分割粒度
-
-
- 作成と編集の共通化はやってしまいがちだが、内部分岐が多くなってしまう
- 仕様が同じレベル同士で共通化はOK
-
-
機能ベースのディレクトリ構成だと各機能の中の構成のコントロールが聞かせづらい
-
Storybook
- Storybook作りこみ -> コンポーネントテスト -> アプリケーションへ統合
-
ネットワーク処理まで含んだコンポーネントが作れるのは非同期処理ライブラリ側で重複リクエスト排除機能を利用しているおかげ
-
時間のかかるテストは適宜可能タイミングでのみ実行する(毎朝1回など)
-
🧠Container/Presentationパターンは昔からあるけど分けとくとテストしやすそう
- hooks以前はContainerコンポーネントににロジックを全部書く感じだったはず
- Render Hooksパターンを使っているpresentation用hooksとcontainer用hooksに分けている
-
🧠 理由やねらいが全部のことがらで説明されているのがよかった
ESLintのカスタムルールで治安維持活動をする
-
規約が暗黙だと開発者の負担になることが多い
-
規約をドキュメントとして残す
-
カスタムESLintを作るのは難しくない
- 積極的にカスタムルールを使っていく
-
ルールの作り方
- 自分のやりたいことに対応するAST Nodeが何なのかがわからない
- AST ExplorerでJSコードをASTに変換して確認できる
- ESLint自体にカスタムルールテストの仕組みがある
-
どんなルールを作ったか
- 物理遷移なら aタグを強制
- LPのアフィリエイトスクリプトがそのまま動いてしまうのが問題(?)イメージができなかった
- render()関数の禁止
- Class時代のReactの名残で使ってしまっているが、useCallback
- Immutable.jsのプロパティ取得方法の統一
-
.get
の判定にTypeScript Compiler API使わないといけなかった
-
- 物理遷移なら aタグを強制
-
まとめ
- 完璧を求めず、早期導入が重要
- 公開されているルールのアプローチは参考になる
- TypeScriptの型系のルールを作るのにTypeScript Compiler APIの知識が必要になるがドキュメントが少ないのでつらい
-
🧠簡単とはいっているけど躓きそうなポイントは結構ありそう
@effect/schemaによる型安全な軽量DDDの実践
-
Effect SystemはCSの概念
-
Effect-TSはそれをTypeScriptで実現したライブラリ(?)
-
Effect-TS自体はEffect Systemを知らなくても使える
-
EffectはパワフルなPromise
-
Promise: 非同期であることがわかる
-
Effect: さらにどんなエラーがあるか、Effectを解決するにはどういう依存が必要かがわかる
-
Effectの課題
- アダプタレイヤが必要(JSの標準ではないので)
- IO系(fetchをwrapしないといけない 等)
- Effect側で提供されているものもある
-
@effect/platform
HttpClient
-
- すべてEffectのEffectというライブラリというよりランタイムを導入するに近い
- 🧠RxJSのときの感じに似ているなと思った
- 知見がまだあまりない
- アダプタレイヤが必要(JSの標準ではないので)
-
Effct同士を組み合わせる方法
-
pipe
,Functors,Monads -
function*
,yield
-
-
開発チームがこれらの概念に慣れている必要がある
-
Effectから提供される機能ですでに他ライブラリなどで受け入れられているもの
- Option/Either(Result)
- パターンマッチング
- スキーマバリデーション
- valibotに似ている書き味
-
どうしようもないエラーは型レベルでは追跡しない(OptionやResultにしない)で、例外を投げる
-
何がうれしいか
- エラーや状態遷移を型で表現することで型でカバーできる範囲が増える
-
TypeScript is Javascript that scales, Effect is TypeScript that scales
-
Effectランタイム全部をアプリケーションにいれるのはまだ検証が必要
-
Effect Schemaあたりの標準ライブラリ的なところから導入してみるのがおすすめ(not Effect type)
The challenges of web apps: what we’ve solved and what’s next?
-
UX/DX視点での改善の歴史についての話
-
UXとDXの2軸でどのように進化してきたか
-
Webサイトはアクセス時にサーバからの読み込みが必要だが、データをロストしたくないという要望のため受け入れられた
-
インタラクティブ性(UX+)
-
コンポーネント思考(DX+)
-
SPA初期読み込み (UX-)
-
SSRの登場 (UX+, DX-)
- インタラクションのところはハイドレート
- 開発者の負荷は上がる
-
rerenderingの遅さ(UX-)
- 変更がったコンポーネント以下のコンポーネントすべてが再レンダリング
- 解決策
- Compile-Time Reactivity: コンパイル時に必要部分のDOMを直接操作するようにする
- React Concurrent Mode: ランタイムでレンダリングの単位を細かくする
- React Compiler: memo化
- signals
- 同じコンセプトはEmber.jsであったが、当時はProxyなどのAPIがなかったため、使い勝手が悪かった
-
hydrateの遅さ(UX-)
- Progressive Enhancement
- JSロード前からinteractiveに使えるように
- Partial Hydration
- すべてのパーツが同じようにinteractiveである必要はない
- Progressive Enhancement
-
Edge Server(UX+)
-
Multi-Runtime(DX+)
-
All-in One Toolchain(DX+)
-
Local First (UX+?)
- WASM
- FileSystem API
- CRDT (Conflict-free Replicated Data Type)
- 共同編集
-
AI
- In Browser AI
Boost your Test Implementation in Javascript by 10x?
- https://www.youtube.com/watch?v=s_iJA5WEe2Y
- 同じ言語同じフレームワークでも同じテストを達成するのに記述の方法はいろいろある
- Code Duplicationが発生する
- 一つのサービスにしよう
- Objectives
- Improve Confidence
- Quantity: Coverage カバレッジだけでは測れない
- Quality: Escaped Bugs 防いだバグ(?)
- Support Fast Delivery
- Automation
- Execution duration (数十分かかるテストは困る)
- Improve Confidence
- 仕組みを作ったらそれが有効であるかちゃんとモニタリングしよう
WebAssembly Unleashed: Powering Server-Side Applications
-
現状のWebAssemblyについて
-
Porability: ソフトウェア開発でもっとも重要なゴールの一つ
-
Server-side WASMの課題
- I/O操作はできない
- type-safety development <- 🧠WASM自体が持つ必要がある? <- type-safetyのセクションで語られていた
-
WASI
- ブラウザから組み込みデバイスまであらゆるところで利用できるインタフェース規格
- 現状可能なもの
- FileSystem: Read-via-stream, Wirte-via-stream, Sync-data, Read-directory etc..
- HTTP API
- Socket API
- CLI, Random, Clocks
-
WIT (WASM Interface Type)
- ゲスト言語(WASMモジュールを実装する言語)によらない型定義できる仕組み
- Interfaces: 名前が付いた型, 他の型から参照できる
- Worlds: モジュールのimport/exportを定義
-
WASM, WASM Runtime, WASI, WIT どう組み合わせる? => Component Model
-
Components: WebAssembly binaries (.wasm files)
-
なんでComponents? moduleではだめなのか
- moduleはプリミティブな数値しかあつかえない
- よりリッチなType: strings, recordsなどを扱いたい
- module間でリッチなTypeをどう表現するかの合意が必要
- component modelではWITでそれを定義
- WASM componentは core moduleのimports/exportsをWITで表現したものでラップしたものといえる
-
例: HTTP Clientの定義(JSで実装)
- @bytecodealliance/jcoでWASMにコンパイル
- WIT定義
- JSで実装 fetchを使えばよい
- jcoでWASMコンパイル
- jcoでグルーコード生成
- JSから生成された読み込んで実行
- 型定義されたフィールドだけ抽出される
- 🧠jcoがWITを見て、必要なフィールドだけ抽出するようにコンパイルしている?
-
WASM世界観
-
丁度興味深い記事が
Encrypting data in the Browser - Exploring Web Crypto APIs
- https://www.youtube.com/watch?v=SwiPLoN4HQE
- ブラウザのCrypto APIでE2E暗号化を実現する話
- 通信中に暗号化してもサーバ側で複合したデータを保存してると、クラックされると漏洩するという課題がある
-
Crypto API
- getRandomValues
- Math.randomの実装で使われているアルゴリズムから次の出力を予測する話
- XorShift128+
- Math.randomとよりもかなり遅い、10倍くらい差がつくことも
- randomUUID
- subtle
- 低レベルなAPI
- Authenticity(真正性): 正しい出どころからデータが来ていること
- AES-CTR, AES-CBCでは完全性・真正性が保証できない, AEC-GCMはできる
- AES-GCMではAuthentication tagを追加で付加している
- Crypto Key
- デフォルトではキーのバイト列はJavaScript APiから取り出せないが、
extractable
をtrueにするとキーのバイト列が取り出せる
- デフォルトではキーのバイト列はJavaScript APiから取り出せないが、
- IV:
- 暗号を強くするため
- 同じ入力に対して違う暗号化されたデータを生成できる
- 隠すものではないので、データにくっつけてOK
- 鍵の配布
- Crypto Keyを
crypto.subtle.exportKey
で出力- exportKey/importKeyは 4つのフォーマットに対応
- 今回はJWK
- サーバに飛ばないURLフラグメントを使って渡す
-
crypto.subtle.importKey
でCrypto Keyに変換
- Crypto Keyを
- Excalidrawで使われている
- パスワード付きデータにする
- パスワードからCrypto Keyを生成 (Salt + (PBKDF2 + SHA-256) * 10,000 iteration)
- getRandomValues
- Demo: https://cryptmoji.in/
- 発表で利用したコード