Cybozu Frontend Monthly #5

タイトル画像

イベント概要

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

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

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

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

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

zennのpublicationにてウィークリーのまとめも投稿していますので、ぜひこちらもご覧ください。 https://zenn.dev/p/cybozu_frontend

開催日

2020年11月24日

イベントページ

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

配信URL

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

メンバー


コンテンツ

We rendered a million web pages to learn how the web breaks

Web ページがどのようにして壊れるかを学ぶため、実際に発生しているエラーを調査、考察した記事。

トップ 100 ドメインのルートページ 100 万件をレンダリングするスクリプトを使用して出力される未処理エラーを調査した結果、85%が ReferenceError TypeError SyntaxError 。 また、上記のエラーが実際に発生している理由として、そのほとんどがリソースの読み込み失敗に起因していると述べています。

ほとんどのエラーと強く関連しているコードは webpack で、サンプルにした Web ページの 12%が 1 件以上のエラーを発生させている。

エラーの出にくい Web サイトを作るためには静的検査が欲しいが、遅延バインディングの必要性もあってなかなか難しい。 その点、TypeScript は検査時と実行時で性質を完全に分離しているので良いトレードオフかもしれない。 最近は WebAssembly が登場し、JS レスで静的検査に寄せられる土台ができつつあるが、実行時にランタイム外との対話が必要なことにわ変わりなさそう。

ランタイムの動的性質を許可しつつ、部分的に静的検査によって安全性を担保することが壊れにくい Web ページを作る鍵になるかもしれないと締めていました。


Performance · microsoft/TypeScript Wiki

この wiki 自体は TypeScript チームがコンパイル速度や実装の体験をよくするための情報として、去年ごろに公開していました。

今年の秋ごろ「Writing Easy-to-Compile Code」の項目が追加されています。 この項目はコンパイルパフォーマンスがいい書き方を示してくれています。

項目は下記 3 つに別れています。

interface と Type に関する記事は最近「TypeScript: Prefer Interfaces」も出ており、 Type はインライン化されるので d.ts のサイズが肥大化するから interface を推奨するという内容の記事になっています。 これは d.ts を書く場合に覚えておくとよさそうです。

TypeScript の Type か interface を使うか、型推論に任せるかは時と場合によるというのを前提での紹介でした!


Vue Ref の糖衣構文と Svelte

vue の rfc にrefの糖衣構文である(Svelte inspired な)ref:構文についての rfcが出されました。この記事ではメリデメや Svelte との比較などについて書かれています。

rfc の背景

vue ではリアクティブな変数を定義するためにref()という関数を使います。

import { createComponent, ref } from "vue";

export default createComponent({
  setup() {
    const count = ref(1);

    function inc() {
      count.value++;
    }

    console.log(count.value);

    return {
      count,
      inc,
    };
  },
});

ref()で作成した変数の取得/更新はcount.valueのように.valueを経由する必要があります。TypeScript を採用していれば.valueを経由しないで参照しようとするとエラーなりが出るので問題ないですが、そうでない場合はref()で作られた変数と通常の(reactive じゃない)変数を区別する必要があります。

提案された構文

この.valueを使うのが面倒で冗長だよねというところから次のような糖衣構文が提案されています。

<script setup>
  // declaring a variable that compiles to a ref ref: count = 1 function inc(){" "}
  {
    // the variable can be used like a plain value
    count++
  }
  // access the raw ref object by prefixing with $ console.log($count.value)
</script>

https://github.com/vuejs/rfcs/blob/script-setup/active-rfcs/0000-script-setup.md#2-ref-sugar-makes-ref-usage-more-succinct

ラベル付き文の構文を使って reactive な変数を定義し、.valueなしに参照しています。

このコードはコンパイルされて 👆 にあるようなコードに変換されます。

JavaScript のセマンティクスとトレードオフ

.valueを経由するようになっているのは JavaScript の言語的な制約からきているもので、vue は JavaScript のセマンティクスを変更/拡張することなくリアクティブを実現するためにこうしています。 今回の rfc は、JavaScript のセマンティクスから逸れてると認識しながらも、開発者の体験を向上させるためにトレードオフを行う余地があるのではという考えから提出されました。

感想

自分は次の理由からあまりいい提案ではないように思います。

この糖衣構文によって開発者の体験が短期的には向上するかもしれませんが、解決したい課題に対してデメリットが上回っていると感じています。(この rfc に限らず vue が<script setup>など、独自の糖衣構文的なものを追加してくる傾向があること自体にあまりいい印象を持っていないです。)


How Web Apps Work | Mark’s Dev Blog

Redux のメンテーであるMark Eriksonによる、Web 開発の概念や用語、ツール、技術について説明するシリーズ。Web 開発は様々な技術が出てくるので、それらがなぜ必要なのか、どう関係してるのかを説明してる。そして、必要に応じて深く調べれるように別記事への導線も用意されている。

個人的に、Redux のドキュメントの英語読みやすいなーと思ってたので、網羅的に書かれていて、かつ読みやすい記事でありがたいなと思いました。

テーマごとに全 5 記事ある


tc39/proposal-js-module-blocks

JS Modules Blocks は先日の TC39 ミーティングで Stage 1 になった プロポーザルです。

このプロポーザルは、ファイルを隔てずにモジュールを定義できるインラインモジュール式(InlineModuleExpression)という新しい構文を導入します。

const m = module {
  export const foo = "hello";
}

この module {} という部分が新しく導入される構文です。式なので、結果をそのまま値として扱うことが可能です。

ここで定義されたmimport(...) で import できます。

const m = module {
  export const foo = "hello";
}
const m1 = await import(m);
console.log(m1.foo); // hello

まだ Stage 1 なのでこれから仕様が大幅に変更される可能性はありますが、実用性もあり面白い提案なので、注目しておくと良さそうです。


WebAssembly ハンズオン: 実際に動かして基礎を学ぶ(翻訳)

原文は 8 月頃に投稿された Hands-on WebAssembly: Try the basics — Martian Chronicles, Evil Martians’ team blog という記事。

ハンズオンと書いてますが手を動かす成分は少なめ。環境構築も Docker で提供されてるものを使うので楽です。(何をインストールしてるのかわかりづらいというのはある)

内容としては

という感じで、C メインで Rust はおまけ程度。

大部分は C で wasm 書く予定がなければ必要ない情報かなーと思いましたが、個人的には

あたりが面白かったです。

元の C の関数

int sign(int x) { return (x % 2) * (2 - (x % 4)); }

を wat にすると、

(func (;1;) (type 0) (param i32) (result i32)
  i32.const 2
  local.get 0
  i32.const 4
  i32.rem_s
  i32.sub
  local.get 0
  i32.const 2
  i32.rem_s
  i32.mul)

となり、これは

  1. 整数値 2 をスタックに push する(i32.const 2
  2. 関数の最初のパラメーターをスタックに push(local.get 0
  3. 整数値 4 をスタックに push(i32.const 4
  4. スタックから 2 つの値を削除して 1 番目の値を 2 番目の値で割った余りをスタックに push する(i32.rem_s

x % 4 に相当します。


Dropping Support For IE11 Is Progressive Enhancement · The Ethically-Trained Programmer

IE11 をサポートする労力を JavaScript を無効にしているユーザーのために使ったほうがいいんじゃないかという話。

※余談:ブログが「The Ethically-Trained Programmer(倫理的に訓練されたプログラマ)」で favicon が「倫」で笑った


Use CSS Variables instead of React Context | Epic React by Kent C. Dodds

CSS in JS の React Context を使ったテーマ機能(例:styled-components の ThemeProvider)より、CSS Variables を使おうという話。


Building a type checked url router from scratch

SPA のルーティングに設定する URL の型を TypeScript 4.1 で新しく追加された Template Literal Types を使って検査する方法の紹介です。

Template Literal Types についてはAnnouncing TypeScript 4.1の記事で紹介されています。

記事中の型宣言から一部抜き出すと次のように Template Literal で文字列から infer で値を抜き出すことが出来ます。

type TemplateVariables<T> = T extends `${infer Start}/:${infer Variable}`
  ? TemplateVariables<Start> | Variable
  : never;

type param = TemplateVariables<"/api/users/:id">;
// param = "id"

この記事の最終的な目的はコンポーネントに紐付けたパスのパラメータとコンポーネントの Props の型が同じになるようにしたいということです。

記事中から一部抜粋した例をあげます。 以下のようなコンポーネントがあったとき、

type MyDogProps = {
  name: string
  breed: string
}

const MyDog = ({ name, breed }: MyDogProps): string =>
  `<p>My dog's name is ${name} and it's a ${breed}</p>
   <p>${Link({ route: routes.home, children: "Back to home" })}</p>`

以下のようなルーティング定義をしておきます。

const routes = {
  home: route([Home, "/"]),
  dog: route([MyDog, "my-dog/:name/:breed"]),
  search: route([Search, "search/:query"]),
};

注目してほしいのが、my-dog/:name/:breednamebreedです。これえは MyDogProps のキーと同じです。なので、たとえば以下のように MyDogProps のキー名が違う場合エラーになります。

type MyDogProps = {
  name: string;
  breed_name: string;
};

const routes = {
  dog: route([MyDog, "my-dog/:name/:breed"]),
};
// breed というプロパティはないためエラー

また、Props には定義されているのにパスには存在しない場合もエラーになります。

type MyDogProps = {
  name: string;
  breed: string;
  age: string;
};

const routes = {
  dog: route([MyDog, "my-dog/:name/:breed"]),
};
// age がないためエラー

逆に Props にないパラメータをパスに含めてもエラーになります。

type MyDogProps = {
  name: string;
  breed: string;
};

const routes = {
  dog: route([MyDog, "my-dog/:name/:breed/:age"]),
};
// Propsにageはないためエラー

このようにコンポーネントが期待するパラメータが正しく渡ってくることを事前にチェックすることができます。


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

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