要約
- 2021年1月現在、Nextjsでstatic exportするとcssがインライン(
<style>
タグにベタ書き)にならない。 - cssを
<link>
タグでロードすると同期的にロードされてしまい、ページのレンダリングが遅くなるのでinlineにしたい(inlineだと非同期ロードなのでレンダリングまでの時間が早い) - そこで参考にしたgithubのissueにあるコードを元に(というかまんまコピー)
_document.tsx
を書いた。 - この記事では一応何をやっているか自分のための備忘録として書いておく
- 使っているNext.jsのバージョン:
10.0.5
import Document, { Head, Main, NextScript } from "next/document"
import fs from "fs"
import path from "path"
declare type DocumentFiles = {
sharedFiles: readonly string[];
pageFiles: readonly string[];
allFiles: readonly string[];
};
class InlineStylesHead extends Head {
getCssLinks({ allFiles }: DocumentFiles) {
const { assetPrefix } = this.context
if (!allFiles || allFiles.length === 0) return null
return allFiles
.filter((file: any) => /\.css$/.test(file))
.map((file: any) => (
<style
key={file}
nonce={this.props.nonce}
data-href={`${assetPrefix}/_next/${file}`}
dangerouslySetInnerHTML={{
__html: fs.readFileSync(path.join(process.cwd(), '.next', file), 'utf-8'),
}}
/>
))
}
}
export default class CustomDocument extends Document {
render() {
return (
<html>
<InlineStylesHead />
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
やっていること
NextJSの Head
コンポーネントを継承した InlineStylesHead
というのを定義している。NextJSの Head
コンポーネントには getCssLinks
という関数があり、ここでnextjsでビルドされたcssを呼び出すための <link>
タグを生成している。 InlineStylesHead
では同じようなことをしているが、 <style>
タグでビルドされたcssを直接書いてしまっている。vercel/next.jsにある_document.tsxのコード をみると
getCssLinks(files: DocumentFiles): JSX.Element[] | null {
//...長いので省略
}
と書いてあるので、getCssLinksという関数には DocumentFiles
という型の files
という引数を渡される。が、 DocumentFiles
という型はexportされていないので自身で定義している。定義自体はここにあるので
declare type DocumentFiles = {
sharedFiles: readonly string[];
pageFiles: readonly string[];
allFiles: readonly string[];
};
を _document.tsx
で定義している。さて、これでビルドされたCSSのファイル一覧を取得できるようになったので、あとは Head
クラス自体が持っているアセットのパスと結合し、ファイルを読み込んで <style>
タグとして出力する。
getCssLinks({ allFiles }: DocumentFiles) {
const { assetPrefix } = this.context
if (!allFiles || allFiles.length === 0) return null
return allFiles
.filter((file: any) => /\.css$/.test(file))
.map((file: any) => (
<style
key={file}
nonce={this.props.nonce}
data-href={`${assetPrefix}/_next/${file}`}
dangerouslySetInnerHTML={{
__html: fs.readFileSync(path.join(process.cwd(), '.next', file), 'utf-8'),
}}
/>
))
}
これを定義したコンポーネントを普段使う <Head>
(これはNextJSのやつ) の代わりに使うとinlineで出力される。
まとめ
- NextJSの
Head
コンポーネントではデフォルトでcssをinlineで出力しない - なので自分でそれっぽいコンポーネントを書く必要がある。
- 参考issueにあったコードを元に作った