社内向けに、フロントエンド関連のニュースや業務で発生したQ&A、利用しているライブラリなどの情報を定期的に書いています。
普段の業務において、質問を受けた際の回答で面白そうなものをまとめました。
指定されていたmineTypeがSafariでサポートされていなかったのが原因でした。 WebKitが、動画の場合はvideo/mp4のみをサポートしていることは実装から明らかです。 一方、Chromiumでは現在video/webmのみをサポートしています。
UserAgentは一般に見たくないと思うので、次のような分岐を追加することを推奨しました。
MediaRecorder.isTypeSupported("video/webm") ? "video/webm" : "video/mp4"
エラーが出ていないため、そもそもpages/api
ディレクトリ以下のファイルが何らかの理由で認識されていない可能性が考えられます。
実際、next.config.js
のpageExtensions
の設定が原因でした。
pageExtensions
という名前は、ページコンポーネントを記述したファイルの拡張子を指定しているように見えますが、実際はpagesディレクトリ以下で特別な役割を持っている_app.js
や_document.js
などに加えて、api
ディレクトリ以下のファイルにも適用されます。
今回のケースでは、page.tsx
などが設定されていたため、api
ディレクトリ以下のファイルが認識されていませんでした。
このことは以下のPRで注意書きが追加されています。
2023年2月にリリースされたiOS 16.4 beta 1及びSafari 16.4 betaについて、いくつか紹介します。
今まではAndroidなどでしかサポートされていなかったWeb Pushが、iOS 16.4 beta 1でサポートされました。 ホーム画面に追加されたWebアプリから、Push API、Notifications API、Service Workerなどを駆使して、ユーザーにプッシュ通知を送ることが可能になります。
またホーム画面に追加されたWebアプリでカウントを表示できるBadging APIなどもサポートされ、今後PWAの利用が広がると期待されます。
Safari 16.4 betaは、非常に巨大なリリースです。Web PushだけでなくECMAScriptの多くの機能が新たにサポートされました。
以下に、サポートされた機能のいくつかを軽く紹介します。
Array.fromがfor相当なのに対して、Array.fromAsyncはfor awaitと見れば理解しやすいと思います。 Async iterableを処理する方法として、Promise.allなどがありますが、Promise.allが並列実行であるのに対して、 Array.fromAsyncはfor await相当であり、順次実行されます。
Array#groupは、配列を指定した関数の戻り値でグルーピングしたオブジェクトを返します。 一方、Array#groupToMapは、配列を指定した関数の戻り値でグルーピングしたMapを返します。
コードを見た方が理解しやすいと思います。
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によって読み込まれるパッケージのURLを指定する手段です。
<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コーデックがSafari 16.4 betaでサポートされました。 しかし、AppleのM1/M2はAV1用のハードウェアアクセラレータを搭載していないため、利用できる環境はほぼありません。
2023年2月にリリースされたChrome 110について、いくつか紹介します。
Chrome 110で、Windows 7/8/8.1などのOSのサポートが終了しました。 なお2023年1月に、MicrosoftはWindows 7/8/8.1のほとんどのバージョンのサポートを終了しています。
また最後のサポートバージョンであるChrome 109へのセキュリティ修正も2023年10月までとなっています。
R.I.P.
PIP(Picture in Picture)とは、再生している動画を画面の隅に別ウィンドウで表示する機能です。
PIPを利用しているvideo要素に対してスタイルを適用できる:picture-in-picture
疑似クラスがChrome 110でサポートされました。
:picture-in-picture
は:
が1つだけであり、::after
や::first-letter
のような擬似要素ではありません。
PIPで表示される別ウィンドウを表すものではなく、PIPを利用しているvideo要素を表す点に注意してください。
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のサポートが予定されており、 今後は利用する機会が増えるかもしれません。
2023年1月にTypeScript 5.0 Betaがリリースされました。正式版は2023年3月が予定されています。 なお、TypeScriptはセマンティックバージョニングに準拠していないため、5.0は重要な変更が含まれていることを意味しません。
実際、allowImportingTsExtensions
やmoduleResolution: "bundler"
、verbatimModuleSyntax
などのフラグや、extends
に複数の設定ファイルを記述できるようになるなど
コンパイラ周りの変更には興味深いものが多くあるものの、tsconfig.json
の変更を滅多にしない多くのライトユーザーにとっては、すぐさま影響のあるバージョンではないと思われます。
以下では、今後誰でも使う機会がありそうな新機能について、いくつか紹介します。
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) {
}
}
今まで、あるオブジェクトのプロパティを取得する関数を実装した際に、具体的な型が欲しければ、呼び出す度に毎回引数へ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で構築されており、特にこの技術ブログでは、未だ実験的な機能であるApp directoryも利用しています。
以下では、2023年2月にリリースされたNext.js v13.2から、お世話になりそうな機能をいくつか紹介します。
今までの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`,
},
}
pages/api
のApp directoryバージョンです。
今までApp directoryであっても、APIを実装したい場合はpages/api
を追加する必要がありました。
ですが今後はpage.js
の代わりにroute.js
を追加するだけで、APIを実装できるようになります。
Linkコンポーネントのhrefに型が付くようになり、存在しないページへのリンクを防ぐことができます。
この機能はapp
ディレクトリだけでなく、pages
ディレクトリでも利用できます。
ただし、ベータ版であるため、next.config.js
にexperimental.typedRoutes
を追加する必要があります。
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
typedRoutes: true,
},
};
module.exports = nextConfig;
ECMAScript 2023は、2023年にリリースされるECMAScriptの仕様です。 なおECMAScriptは、前年にStage 4となった機能が次期ECMAScriptに含まれるという仕組みであり、 Stage 4の条件として2つ以上の実装が必要であるため、次期ECMAScriptがほとんどのブラウザで既に実装されているということがよくあります。
以下では、ECMAScript 2023に追加されたものを紹介します。Symbols as WeakMap keysは紹介しません。
全てのモダンブラウザで実装済みです。
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
Node.jsなどと同様に、ファイルの先頭行にある#!
をコメントとして扱います。
これにより、例えばサーバーサイドとクライアントサイドで同じコードを使いまわすことができます。
なお、全てのモダンブラウザで実装済みです。
#!/usr/bin/env node
// in the Script Goal
'use strict';
console.log(1);
非破壊的な配列の操作をするメソッド群です。 これらが追加されると、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]
2023年2月にLighthouse 10がリリースされました。
操作可能になるまでの時間を表す指標である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は、Googleが開発した画像圧縮ウェブアプリです。また、Squoosh CLIは、Squooshの圧縮機能をコマンドラインから利用できるようにしたものです。
ですが、2023年1月に経済状況などから取り組む時間や人がいないというコメントと共に、SquooshのCLI及びlibsquooshのコードがリポジトリから削除されました。
なお、数日後にGoogleでは1万2000人(全従業員の6%)の解雇を発表しています。