Cybozu Frontend Monthly #7

イベント概要

サイボウズフロントエンドマンスリーは、サイボウズ社内で行っているフロントエンド情報共有会「フロントエンドウィークリー」の公開版です。

その月に気になったフロントエンドの情報を、サイボウズのフロントエンドエキスパートチームのメンバーが共有していきます。

このイベントのハッシュタグは #サイボウズフロントエンドマンスリー です。

※フロントエンドウィークリーとは

毎週火曜の 17:00 〜 18:00 で社内向けに行っているフロントエンドの気になる記事を紹介する会です。2016年3月15日から行われています。
ハッシュタグ #サイボウズフロントエンドウィークリー で実況しています。

開催日

2021年01月26日

イベントページ

https://cybozu.connpass.com/event/202029/

配信URL

https://www.youtube.com/watch?v=MMFzKjIzdRA

タイムテーブル

07:50 - 08:00配信開始
08:00 - 08:05オープニング
08:05 - 08:55本編
08:55 - 09:00クロージング

メンバー


紹介記事

JavaScript アンケート周り

両方とも 2020 年の流れを把握するのに使えそうです。

State of JS は、ビルドが esbuild や sbowpack、フレームワークは引き続き svelte に興味持ってる人が多かったです。 しかし実際に使用してるものは、webpack などになっており、新しいものはまだプロダクションで使用するには踏み込めない印象。 各項目で色々な人が 2020 年の個人的ベストを紹介してるのが面白かった、changesetsInsomniaRedwoodなど知らないものを知れました。

2020 JavaScript Rising Stars は、あくまでスター数なので State of JS 2020 と見比べて世間がどこに注目しているか照らし合わせるとよさそうです。 各セクションで説明もあるので、なぜスター数が増えたのかもわかりやすいです。


Don’t Use Inverted Color Cues on Toggle Buttons

  • 共有者: @pirosikick

トグルボタンでボタンの文字色と背景色を反転させた ON・OFF ボタンを作りがちだが、 色のコントラスト比が同じになるのでどちらが ON で OFF なのかわからず誤操作が増えがちなので気をつけなはれや、という内容。 ブログの画像を見ると分かりやすいですが、色の反転の代わりに奥行きで ON/OFF を表現するといいよとのことです。 最近、tailwind.css を使っていて悪い例と同じようなことをしていたので、タイムリーで参考になりました。


Classi にフロントエンドエキスパートチームを作った話

  • 共有者: @nakajmg

Classi さんがフロントエンドエキスパートチームを作った話。 弊チームを参考にしてくれたとのことで、私達の活動が界隈に影響を与えられていると思うと嬉しいですね。

記事中ではFrontendOpsについて触れており、フロントエンドの運用が開発者だけの関心事ではなく、チーム全員にとって重要であると説明しています。 今後ますますフロントエンドの運用は重要な関心事となっていくと思いますので、弊フロントエンドエキスパートチームとしても真摯に向き合って行きたいです。


Front-End Performance Checklist 2021 — Smashing Magazine

  • 共有者: @shisama

パフォーマンスに関するチェックリストです。
77 個のチェックリストがありますが、長大なので挫折しそうな人はサマライズされている PDF 版 を読んでみて気になるところは記事の方を読んでみるのが良いかと思います。

記事の最後にはQuic Winsとして 1 時間で改善できる 17 個のことを記事中からピックアップされているので、まずはこれからはじめるのも良さそうです。
個人的には「パフォーマンスの文化を作る」が最初に来ているところがいいと思いました。

パフォーマンスに関連する話で今月知ったものもあわせて紹介しておきます。

この記事の中でも CoreWebVitals を使って計測する話があり、DevTools で計測したり、Chrome 拡張を使っている人が多いと思いますが、Chrome 90 からは Experimental ですが Chrome 本体でもメトリクスを常に監視できるようになるようです。
AddyOsmani.com - A Performance Heads-Up Display (HUD) for Chrome

JS のサイズについては、モダンブラウザで使える JS の構文を使うことでバンドルサイズを減らせると紹介されています。
JS のネイティブに置き換えることでどれくらいバンドルサイズを減らせるかをチェックするツールがあるので、試してみるといいかもしれません。
EStimator.dev: the modern JavaScript savings calculator


Agenda for the 80th meeting of Ecma TC39

TC39 の1月のミーティングのアジェンダです。25 日~28 日にかけて行われます。

このミーティングでは多くのプロポーザルについての議論が行われる予定です。

Intl 系(ECMA402)のプロポーザル

新しい構文を追加するプロポーザル

新しい API を追加するプロポーザル

その他

数が多くて全部は紹介しきれないので、僕が個人的に気になったもの、多くの JavaScript 開発者にとって影響が大きそうなもの、かつステージが進みそうなものを紹介していきます。

Class static initilization Blocks for Stage 3

https://github.com/tc39/proposal-class-static-block

これは、クラスのスタティックプロパティの初期化を行うことができるブロックの構文を導入するプロポーザルです。

今のクラスの仕様だと、何かしらの外部の計算に依存してスタティックプロパティを決定したいとき、クラスの中だけでは不可能でした。

class C {
  static x = ...;
  static y;
}

try {
  const obj = doSomething(C.x);
  C.y = obj.y;
} catch {
  // doSomething が失敗したときのフォールバック
  C.y = ...;
}

このプロポーザルで追加するクラススタティックブロックを使うと、このような処理をクラス内のブロックに書くことができます。

class C {
  static x = ...;
  static y;
  static {
    try {
      const obj = doSomething(this.x);
      this.y = obj.y;
    } catch {
      this.y = ...;
    }
  }
}

JS Module Blocks for Stage 2

https://github.com/tc39/proposal-js-module-blocks

ちょっと前のマンスリーでも紹介しました。

インラインでモジュールを定義できる新しい式の構文を導入するプロポーザル。

const moduleBlock = module {
  export let y = 1;
}
let moduleExports = await import(moduleBlock);
assert(moduleExports.y === 1);

module 式で作ったモジュールは、Worker コンストラクタにそのまま渡すことができます。

let workerCode = module {
  onmessage = function({data}) {
    let mod = await import(data);
    postMessage(mod.fn());
  }
};

let worker = new Worker(workerCode, {type: "module"});
worker.onmessage = ({data}) => alert(data);
worker.postMessage(module { export function fn() { return "hello!" } });

著者のツイートからすでにステージ 2 にあがっているようです。

do expression for Stage 2

https://github.com/bakkot/do-expressions-v2

長らく Stage 1 のままだった do expression です。

ブロックの中で最後の評価されたものが式の結果になります。また、変数のスコープは do のブロックの中で閉じます。

let x = do {
  let tmp = f();
  tmp * tmp + 1;
};

読みにくいネストした三項演算子の代わりに if else で書けるようになります。

let x = do {
  if (foo()) {
    f();
  } else if (bar()) {
    g();
  } else {
    h();
  }
};

特に、JSX などの中に書くと便利です。

return (
  <nav>
    <Home />
    {do {
      if (loggedIn) {
        <LogoutButton />;
      } else {
        <LoginButton />;
      }
    }}
  </nav>
);

Relative indexing method

https://github.com/tc39/proposal-relative-indexing-method

Array.prototype.at のことです。

配列の後ろから要素を取得するのが楽になります。

const arr = ["a", "b", "c", "d"];
arr.at(0); // "a"
arr.at(1); // "b"
arr.at(-1); // "d"

JSON modules for Stage 3

https://github.com/tc39/proposal-json-modules

JSON を JavaScript からモジュールとして読み込むときの挙動についての仕様です。

もともとは、Import Assertionsの一部でしたが別の仕様として切り出されました。

ブラウザ上では JSON modules を使う場合は、セキュリティ上の都合から Import Assertions の構文を使って import する必要があります。

import json from "./foo.json" assert { type: "json" };
import("foo.json", { assert: { type: "json" } });

ただ、これはブラウザ特有のセキュリティの問題なので、JSON modules を import するときにアサートを必須にするかどうかは各ランタイムに委ねられています(Import Assertions の構文を解釈できる必要はある)。

次のような JSON を考えます。

{
  "foo": "ふー",
  "bar": "ばー"
}

これを JavaScript から import するときは default export として解釈します。

import json from "./foobar.json";

console.log(json.foo); // ふー
console.log(json.bar); // ばー

JSON のキーには JavaScript の識別子として妥当でないものを使用できるため、named export はサポートされていません。

{
  "32": "さんじゅうに"
}
// 数字は識別子として認められていない
import { 32 } from "numbers.json";

Enabling Popups - MicrosoftEdge/MSEdgeExplainers

  • 共有者: @zaki___yama

以前の Frontend Monthlyで "Enabling Custom Control UI" というプロポーザルを紹介したんですが、その続編みたいなプロポーザルです。 といってもその後の動向を熱心にウォッチしてたわけではなく、例によって Edge の PM のツイート から知りました。

概要

  • <popup> という新しい HTML 要素の提案です
  • 他の HTML 要素と同じようにマークアップを書き、 show()/hide() メソッドで表示/非表示を制御します
<button aria-haspopup="true" aria-controls="menuPopup" id="menuButton">
  Menu
</button>
<popup id="menuPopup" role="menu" anchor="menuButton">
  <!-- Markup for menuitems goes here -->
</popup>
document.getElementById("menuButton").addEventListener("click", () => {
  document.getElementById("menuPopup").show();
});
  • popup は独立した要素の可能性もあるし、combobox のようなより大きな複合的な(composited)コンポーネントの一部の可能性もあります

面白いと思ったポイントいくつか

  • popup は以下の挙動をする。これらの挙動をプロポーザル中では "light dismiss" と表現している

    • ユーザーが Escape キーを押した
    • popup またはそのアンカー要素の layout が変更された
    • フォーカスが外れた
  • [Anchored positioning] popup の位置を他の要素に紐付けられるよう、CSS anchored positioning scheme という別のプロポーザルも策定予定。ただ現状も anchor 属性を指定すると別の要素に紐付けることができる
  • [Alternate Solutions Considered] 別のソリューションとして <dialog> 要素の拡張も考えたけど、popup と dialog で期待する機能が違うのでやめた。たとえば

    • popup は軽量な UI で、ユーザーが他の UI を操作したときなどに自動的に消えてほしいけど、dialog は一般的にユーザーが明示的に閉じないと消えない
    • popup は一度に 1 つしか表示できないが、dialog はそうとは限らない
    • dialog を表示したとき popup は非表示になるべきだけど、逆はそうとは限らない
  • [Areas for exploration] 表示・非表示を手続き的に書かないといけないのかーと思ったけど、宣言的に書けるような属性も検討するかも

How We Improved SmashingMag Performance

  • 共有者: @b4h0-c4t

一行まとめ

JAMStack 上で React を使って構築した Web サイトのパフォーマンスを最適化し、Core Web Vitals のメトリクスを改善した手法の話。

序文

Web パフォーマンスについての話の多くはオーバーホールから始まる。 一方で、時間経過による機能追加の結果コードの断片化やサードパーティスクリプトの読み込み高速化のための "fourth-party" スクリプトが入ってしまう。

実際、smashing 社もパフォーマンスへの目標をしばしば忘れていた結果、Lighthouse スコアがボロボロになっていた。

That’s Where We Were

対象サービス(おそらくブログ)は JAMStack 上で Hugo + React を用いて構築されている。 記事のビルドには 6 分近く掛かり、CSS の注入、Webpack のコード分割、広告・昨日の動的挿入 RSS 生成、エッジに配信するための AB テスト等のビルドプロセスが走っていた。

Identifying Bottlenecks

ボトルネックを特定するためにいくつかのパフォーマンス評価を実施した。

LCP の結果、5-9 月あたりからパフォーマンスの悪化が見られた。 この時期はナビゲーションバーのリリースを実施していたことから、この指標からボトルネックを特定するのは容易だった。

サービスのリクエストマップを調べた当初は特に問題はなさそうだった。 しかし、特定の条件下で DOM のサイズが爆発的に大きくなることを発見した。 主要なページのみを計測対象とした結果発見できていなかったが、埋め込み等が多く用いられている記事を計測した際は膨大なリクエストが行き来していることがわかった。

やったこと

  • head の読み込み順序改善
  • 特に重要な CSS の手動生成
  • Web フォントの読込変更
  • モノリシックな JS の特定と分解
  • サードパーティコンテンツの適切な割り当て(レイアウト)
  • 体験の向上(AVIF 生成や著作画像のプリロード等)

どうなったか

  • Lighthouse のスコアはかなり改善した。 インタラクティブタイムとブロッキングタイムが今後の課題で、CSS サイズの縮小やモバイル用のバンドルも検討している。

Upgrading DevTools' architecture to the modern web

  • 共有者: @toshi__toma

2020 年の Chrome DevSummit の動画で、Chrome DevTools が取り組んだアーキテクチャの移行についての動画。

取り組んだもの

  • モジュールシステム

    • Custom Module System を JavaScript Modules へ
  • 型システム

    • Closure Compiler を TypeScript Compiler へ
  • ビルドシステム

    • Python Scripts を Rollup へ
  • コンポーネント

    • 独自の Custom Components を Web Components へ

DevTools は、大規模で歴史の長い Web アプリケーションなので、それらの移行をどのように進めたのか、移行での学びなどがまとまってる。 移行の計画・実施・管理の 3 つについて説明されていて、とても参考になるものが多かったです。


フロントエンドエキスパートチームについて

https://speakerdeck.com/cybozuinsideout/frontendexpert-team