Frontend Weekly 2023-02-22
Safari 16.4 beta、Chrome 110、TypeScript 5.0 Beta、Next.js v13.2、ES2023、Lighthouse 10、Squooshについて紹介する。
- # Safari 16.4 beta
- # Web Pushのサポート
- # Many ECMAScript features supported
- # AV1のサポート
- # Chrome 110
- # 古いWindows OSのサポートが終了
- # PIP擬似クラスのサポート
- # TypeScript 5.0 Beta
- # Decorators
- # const Type Parameters
- # Next.js v13.2
- # Metadata APIによるビルトインSEOサポート
- # Custom Route Handlers
- # Statically Typed Links (Beta)
- # ES2023
- # Array find from last
- # Hashbang Grammar
- # Change Array by copy
- # Lighthouse 10
- # TTIの削除
- # Squoosh
- # Squoosh CLI及びlibsquooshが非推奨に
Safari 16.4 beta
2023年2月にリリースされたiOS 16.4 beta 1及びSafari 16.4 betaについて、いくつか紹介します。
Web Pushのサポート
今まではAndroidなどでしかサポートされていなかったWeb Pushが、iOS 16.4 beta 1でサポートされました。 ホーム画面に追加されたWebアプリから、Push API、Notifications API、Service Workerなどを駆使して、ユーザーにプッシュ通知を送ることが可能になります。
またホーム画面に追加されたWebアプリでカウントを表示できるBadging APIなどもサポートされ、今後PWAの利用が広がると期待されます。
https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/
Many ECMAScript features supported
Safari 16.4 betaは、非常に巨大なリリースです。Web PushだけでなくECMAScriptの多くの機能が新たにサポートされました。
https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes
以下に、サポートされた機能のいくつかを軽く紹介します。
Array.formAsync
Array.fromがfor相当なのに対して、Array.fromAsyncはfor awaitと見れば理解しやすいと思います。 Async iterableを処理する方法として、Promise.allなどがありますが、Promise.allが並列実行であるのに対して、 Array.fromAsyncはfor await相当であり、順次実行されます。
https://github.com/tc39/proposal-array-from-async
Array#group, Array#groupToMap
Array#groupは、配列を指定した関数の戻り値でグルーピングしたオブジェクトを返します。 一方、Array#groupToMapは、配列を指定した関数の戻り値でグルーピングしたMapを返します。
https://github.com/tc39/proposal-array-grouping
コードを見た方が理解しやすいと思います。
const animals = [
{ name: "たま", type: "猫" },
{ name: "みけ", type: "猫" },
{ name: "ぽち", type: "犬" },
];
console.log(animals.group(animal => animal.type));
/* result
{
猫: [
{ name: 'たま', type: '猫' },
{ name: 'みけ', type: '猫' },
],
犬: [
{ name: 'ぽち', type: '犬', },
],
}
*/
console.log(animals.groupToMap(animal => animal.type).get("猫"));
/* result
[
{ name: 'たま', type: '猫' },
{ name: 'みけ', type: '猫' },
]
*/
Import Maps
ブラウザで実行されるimportによって読み込まれるパッケージのURLを指定する手段です。
https://github.com/WICG/import-maps#multiple-import-map-support
<head>
<script type="importmap">
{
"imports": {
"react": "https://unpkg.com/react@18/umd/react.development.js",
"react-dom": "https://unpkg.com/react-dom@18/umd/react-dom.development.js"
}
}
</script>
</head>
<body>
<script type="module">
import { createRoot } from "react-dom/client";
import React from "react";
</script>
</body>
AV1のサポート
AV1コーデックがSafari 16.4 betaでサポートされました。 しかし、AppleのM1/M2はAV1用のハードウェアアクセラレータを搭載していないため、利用できる環境はほぼありません。
Chrome 110
2023年2月にリリースされたChrome 110について、いくつか紹介します。
古いWindows OSのサポートが終了
Chrome 110で、Windows 7/8/8.1などのOSのサポートが終了しました。 なお2023年1月に、MicrosoftはWindows 7/8/8.1のほとんどのバージョンのサポートを終了しています。
また最後のサポートバージョンであるChrome 109へのセキュリティ修正も2023年10月までとなっています。
R.I.P.
PIP擬似クラスのサポート
PIP(Picture in Picture)とは、再生している動画を画面の隅に別ウィンドウで表示する機能です。
PIPを利用しているvideo要素に対してスタイルを適用できる:picture-in-picture
疑似クラスがChrome 110でサポートされました。
:picture-in-picture
は:
が1つだけであり、::after
や::first-letter
のような擬似要素ではありません。
PIPで表示される別ウィンドウを表すものではなく、PIPを利用しているvideo要素を表す点に注意してください。
https://developer.chrome.com/blog/new-in-chrome-110/
Chrome Developersの記事では、PIPモードであるvideo要素のデフォルトの見た目を上書きするデモが紹介されています。
video:picture-in-picture {
opacity: 0;
}
.video-container {
background: #000;
position: relative;
}
.video-container:has(video:picture-in-picture)::before {
content: "Video is now playing in a Picture-in-Picture window";
position: absolute;
right: 36px;
bottom: 36px;
color: #ddd;
}
現状、PIPを利用する機会はあまりないかもしれませんが、Chrome 111ではPIPが任意の要素に対して利用できるdocumentPictureInPictureのサポートが予定されており、 今後は利用する機会が増えるかもしれません。
TypeScript 5.0 Beta
2023年1月にTypeScript 5.0 Betaがリリースされました。正式版は2023年3月が予定されています。 なお、TypeScriptはセマンティックバージョニングに準拠していないため、5.0は重要な変更が含まれていることを意味しません。
実際、allowImportingTsExtensions
やmoduleResolution: "bundler"
、verbatimModuleSyntax
などのフラグや、extends
に複数の設定ファイルを記述できるようになるなど
コンパイラ周りの変更には興味深いものが多くあるものの、tsconfig.json
の変更を滅多にしない多くのライトユーザーにとっては、すぐさま影響のあるバージョンではないと思われます。
以下では、今後誰でも使う機会がありそうな新機能について、いくつか紹介します。
https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/
Decorators
TypeScript 5.0では、TS39のStage3相当のデコレータの実装が予定されています。 今後、デコレータを利用したライブラリが多く登場すると予想されます。
なお、今までコンパイラフラグの--experimentalDecorators
で利用できたデコレータ(Legacy Decoratorsと呼称される)は、古いプロポーザルを元にしたものであり、互換性がないことには注意が必要です。
ただしexperimentalDecorators
が削除される予定はなく、TypeScript 5.0には、experimentalDecorators
を有効にした際の挙動改善も含まれています。
// Legacy Decorators
@register
export class Foo {}
export class C {
constructor(@inject(Foo) private x: any) {}
}
// New decorators proposal
export
@register
class Foo {}
export class C {
// Cannot use decorators as arguments.
constructor(@inject(Foo) private x: any) {}
}
const Type Parameters
今まで、あるオブジェクトのプロパティを取得する関数を実装した際に、具体的な型が欲しければ、呼び出す度に毎回引数へas const
を付ける必要がありました。
TypeScript 5.0では、型パラメータにconst
修飾子を付けることで、引数側でas const
を付ける必要がなくなります。
/*
TypeScript 4.9
*/
type HasNames = { readonly names: string[] };
function getNamesExactly<T extends HasNames>(arg: T): T["names"] {
return arg.names;
}
// Inferred type: string[]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
// Inferred type: readonly ["Alice", "Bob", "Eve"]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] as const });
/*
TypeScript 5.0
*/
type HasNames = { names: readonly string[] };
function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
return arg.names;
}
// Inferred type: readonly ["Alice", "Bob", "Eve"]
// Note: Didn't need to write 'as const' here
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
Next.js v13.2
弊社のいくつかのサービスとこの技術ブログは現在Next.jsで構築されており、特にこの技術ブログでは、未だ実験的な機能であるApp directoryも利用しています。
以下では、2023年2月にリリースされたNext.js v13.2から、お世話になりそうな機能をいくつか紹介します。
https://nextjs.org/blog/next-13-2
Metadata APIによるビルトインSEOサポート
今までのhead.jsが非推奨になり、代わりにMetadata APIが利用できるようになりました。
メタデータが静的な場合は、metadataをlayout.jsやpage.jsからexportし、動的な場合は、 generateMetadataメソッドをexportします。
import type { Metadata } from "next";
// 静的な場合
export const metadata = {
viewport: "width=device-width, initial-scale=1",
title: "My App",
description: "My App Description",
openGraph: {
type: "website",
// ...
},
twitter: {
card: "summary",
//...
},
} satisfies Metadata;
// 動的な場合、記事ページなど
export async function generateMetadata({
params,
searchParams,
}): Promise<Metadata> {
const data = await getData(params.id);
const metadata = {
title: data.title,
// ...
} satisfies Metadata;
return metadata;
}
Metadata APIでは、設定がルートからマージされていきます。head.jsではページ毎に設定する必要がありました。
次の例では、app/page.js
で設定したtwitterの設定が、app/about/page.js
の設定に引き継がれます。
// app/page.js
export const metadata = {
title: "My App",
description: "My App Description",
twitter: {
card: "summary",
site: "@site",
creator: "@creator",
images: "https://example.com/image.png",
},
} satisfies Metadata;
// app/about/page.js
export const metadata = {
title: "About",
description: "About Description",
// twitterの設定が引き継がれる
} satisfies Metadata;
また、どのページでも後ろ側にブランド名を入れたいといったケースに簡単に対応できる機能もあります。
次の例では、これを指定したページ以下の階層でtitle
にAbout
を指定すると、About|My App
のようなタイトルになります。
export const metadata = {
title: {
default: "My App",
template: `%s | My App`,
},
};
https://beta.nextjs.org/docs/api-reference/metadata
Custom Route Handlers
pages/api
のApp directoryバージョンです。
今までApp directoryであっても、APIを実装したい場合はpages/api
を追加する必要がありました。
ですが今後はpage.js
の代わりにroute.js
を追加するだけで、APIを実装できるようになります。
https://beta.nextjs.org/docs/routing/route-handlers
Statically Typed Links (Beta)
Linkコンポーネントのhrefに型が付くようになり、存在しないページへのリンクを防ぐことができます。
この機能はapp
ディレクトリだけでなく、pages
ディレクトリでも利用できます。
ただし、ベータ版であるため、next.config.js
にexperimental.typedRoutes
を追加する必要があります。
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
typedRoutes: true,
},
};
module.exports = nextConfig;
ES2023
ECMAScript 2023は、2023年にリリースされるECMAScriptの仕様です。 なおECMAScriptは、前年にStage 4となった機能が次期ECMAScriptに含まれるという仕組みであり、 Stage 4の条件として2つ以上の実装が必要であるため、次期ECMAScriptがほとんどのブラウザで既に実装されているということがよくあります。
以下では、ECMAScript 2023に追加されたものを紹介します。Symbols as WeakMap keysは紹介しません。
Array find from last
全てのモダンブラウザで実装済みです。
const array = [1, 2, 3, 4];
array.find(item => item % 2 === 1); // 1
array.findLast(item => item % 2 === 1); // 3
array.findIndex(item => item % 2 === 1); // 0
array.findLastIndex(item => item % 2 === 1); // 2
Hashbang Grammar
Node.jsなどと同様に、ファイルの先頭行にある#!
をコメントとして扱います。
これにより、例えばサーバーサイドとクライアントサイドで同じコードを使いまわすことができます。
なお、全てのモダンブラウザで実装済みです。
#!/usr/bin/env node
// in the Script Goal
"use strict";
console.log(1);
Change Array by copy
非破壊的な配列の操作をするメソッド群です。 これらが追加されると、Array.prototype.slice()などを利用したハックをしなくても済みます。 ただし、ほとんどのブラウザで未実装です。
const array = [2, 6, 3];
array.toReversed(); // [3, 6, 2]
array.toSorted(); // [2, 3, 6]
array.with(1, 2); // [2, 2, 3]
Lighthouse 10
2023年2月にLighthouse 10がリリースされました。
https://developer.chrome.com/blog/lighthouse-10-0/
TTIの削除
操作可能になるまでの時間を表す指標であるTime To Interactive(TTI)が削除されました。 TTIが占めていたスコアウェイトは、CLSに移行されました。
削除された理由としては、ランキングで利用されるCore Web Vitalsの指標ではない点、Core Web Vitalsの指標であるインタラクティブ性(FID)を測定するにはTTIよりもTotal Blocking Time(TBT)の方がより良い指標である点などが考えられます。 FIDやTTI、TBTの関係性については次の図を参考にしてください。
なお、よく誤解されますがLighthouseとCore Web Vitalsには直接的な関連はありません。つまり、この変更はSEOに影響がありません。
Squoosh
Squoosh CLI及びlibsquooshが非推奨に
Squooshは、Googleが開発した画像圧縮ウェブアプリです。また、Squoosh CLIは、Squooshの圧縮機能をコマンドラインから利用できるようにしたものです。
ですが、2023年1月に経済状況などから取り組む時間や人がいないというコメントと共に、SquooshのCLI及びlibsquooshのコードがリポジトリから削除されました。
なお、数日後にGoogleでは1万2000人(全従業員の6%)の解雇を発表しています。