Next.js 10 + Tailwind CSS 2.0 でAMP対応(hybrid)を行う
概要
- Next.jsで作成した既存の静的サイトをAMP対応化
- next/ampのhybridモードを使い、通常ページとAMPページを生成
- CSSにはTailwind CSS 2.0を使用
- PostCSSで容量削減を行い、inline cssとして埋め込み
- AMPページではjsが使えないので無効化した
- GoogleAnalyticsについは <amp-analytics> を使う方法があるが、今回は対応していない
参考
- How to use tailwindcss with AMP in a Next.js project - DEV
- next.js の amp モードで tailwind.css を purgecss と合わせて使う - mizdev
- AMP対応するまでに失敗したこと - という話
既存サイトについて
Next.js 10 + Tailwind CSS 2.0で作成した静的サイト。
「Static HTML Export」を用いてビルド時に静的書き出ししたものをfirebaseにデプロイしている。
next build && next exportnext/ampを用いてAMP対応
next/ampを参考に、AMP対応したいページに↓を追加。
export const config = { amp: "hybrid" }AMP化するとカスタムjsが使えなくなるが、既存ページでSNSシェアボタンをはじめカスタムjsをいくつか使用していたため、今回はhybridを指定して通常ページとAMPページをそれぞれ分けることにした。
AMP対応ページのサンプルは↓のようになる。
// pages/some.tsx
import { NextPage } from "next"
import React from "react"
import Share from "~/components/Share"
export const config = { amp: "hybrid" }
const SomePage: NextPage = () => {
return (
<>
<Share url="/some" text="SomePage" />
</>
)
}
export default SomePageローカル環境を立ち上げ http://localhost:3000/some?amp=1 にアクセスするとAMP版のページが表示される。
これだけで基本的なAMP対応ができるのはとても楽。
ただし、この時点ではCSSの設定を行っていないためスタイルは崩れた状態、かつカスタムjsが有効になっているためターミナルにAMP Validationのエラーが色々と表示される。
カスタムjsの無効化
3rd Party jsの読み込みを無効化
pages/_document.tsx では this.props.inAmpMode でAMPモードの区別ができる。
これを用いてAMPモードの場合はカスタムjsを読み込まないように設定した。
import Document, { Head, Html, Main, NextScript } from "next/document"
import React from "react"
class MyDocument extends Document {
render() {
const isAmp = this.props.inAmpMode
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
{!isAmp && (
<>
<script src="/some_third_party1.js"></script>
<script src="/some_third_party2.js"></script>
</>
)}
</body>
</Html>
);
}
}
export default MyDocumentComponentでのjs処理を無効化
react-shareを用いてSNSシェアボタンを付けていたがAMPモードだと動かないので無効化した。
// components/Share.tsx
import React from "react"
import { useAmp } from "next/amp"
import { TwitterIcon, TwitterShareButton } from "react-share"
type Props = {
text: string
url: string
};
const Share: React.FunctionComponent<Props> = ({ text, url }) => {
const isAmp = useAmp()
if (isAmp) {
return <></>
}
return (
<div>
<TwitterShareButton
url={url}
title={text}
className="focus:outline-none"
>
<TwitterIcon size={32} round />
</TwitterShareButton>
</div>
);
};
export default Share同様にしてGoogleAnalyticsのEvent送信もAMPモードの場合は無効化するように設定した。
if (!isAmp) {
sendGaEvent("some", "event")
}これでAMP Validationのカスタムjsに関するエラーが消えた。
Tailwind CSSをinline cssとして埋め込み
AMP対応前のTailwind CSS設定
「Install Tailwind CSS with Next.js」を参考に設定を行った。
tailwind.config.js は↓のように特にカスタマイズしていない状態。
// eslint-disable-next-line no-undef
module.exports = {
purge: ["./components/**/*.tsx", "./pages/**/*.tsx"],
darkMode: false,
theme: {
extend: {},
},
variants: {},
plugins: [],
};postcss.config.js もカスタマイズしていない状態。
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}また、 css/tailwind.css もカスタムcssは追加していない状態。
@import "tailwindcss/base";
/* Start purging... */
@import "tailwindcss/components";
/* Stop purging. */
/* Start purging... */
@import "tailwindcss/utilities";
/* Stop purging. */この状態で、 _app.tsx で tailwind.css を読み込むようにしていた。
// pages/_app.tsx
import "~/css/tailwind.css"
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyAppAMP対応による変更点
CSS容量削減
Post CSSで未使用のCSSを削除するためにライブラリを追加する。
yarn add -D cssnano @fullhuman/postcss-purgecss postcss-clipostcss.config.js を↓に変更。
/* eslint-disable @typescript-eslint/no-var-requires, no-undef */
const purgecssOption = {
content: [
"./pages/**/*.{js,jsx,ts,tsx}",
"./components/**/*.{js,jsx,ts,tsx}",
],
defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
};
module.exports = {
plugins: [
require("tailwindcss"),
require("autoprefixer"),
require("@fullhuman/postcss-purgecss")(purgecssOption),
require("cssnano")({
preset: "default",
}),
],
};PostCSSにpurge設定を追加したので tailwind.config.js からpurgeを削除。
// eslint-disable-next-line no-undef
module.exports = {
darkMode: false,
theme: {
extend: {},
},
variants: {},
plugins: [],
};npm scriptとしてpostcssを実行するように package.json に build-css を追加。
dev と build 時に build-css を実行するように設定。
{
"scripts": {
"dev": "yarn build-css && next dev",
"build": "yarn build-css && next build && next export",
"build-css": "postcss css/tailwind.css --config postcss.config.js -o css/output.css",
"start": "next start",
}
}これで未使用のCSSを削除したものが css/output.css として書き出される。
容量を確認してみると 9KB に削減できていた。
output.cssをinline cssとして埋め込む
raw-loaderを追加。
yarn add -D raw-loadernext.config.js のwebpack設定に raw-loader を追加してcssファイルを文字列としてインポートできるようにする。
/* eslint-disable @typescript-eslint/no-var-requires, no-undef */
module.exports = {
webpack: (config) => {
config.module.rules.push({
test: /\.css$/,
use: "raw-loader",
});
return config;
},
}続いて pages/_document.tsx でoutputcssを読み込み、getInitialPropsでinline cssとして埋め込む。
/* eslint-disable @typescript-eslint/ban-ts-comment */
import Document, { Head, Html, Main, NextScript } from "next/document"
import React from "react"
// @ts-ignore
import outputcss from "!raw-loader!../css/output.css"
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
<style
dangerouslySetInnerHTML={{
__html: outputcss,
}}
/>
</>
),
}
}
render() {
const isAmp = this.props.inAmpMode
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
{!isAmp && (
<>
<script src="/some_third_party1.js"></script>
<script src="/some_third_party2.js"></script>
</>
)}
</body>
</Html>
)
}
}
export default MyDocumentinline css埋め込みを行ったので pages/_app.tsx でimportしていた tailwind.css は削除しておく。
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyAppこれで yarn dev を実行すると ?amp=1 のページにもCSSが適用されていることが確認できる。
build & export
一通りAMP対応ができたので静的書き出しをして最終確認を行う。
yarn build
yarn startpages/some.tsx をhybrid AMP対応にしていた場合、↓でそれぞれのページにアクセスできる。
- 通常ページ:
http://localhost:3000/some/ - AMPページ:
http://localhost:3000/some.amp/
通常ページには AMPページの存在を示す amphtml タグが自動で埋め込まれている。
<link rel="amphtml" href="/some.amp">また、AMPページには canonical タグが自動で埋め込まれる。
<link rel="canonical" href="/some" />なお、独自に canonical タグを設定している場合は、独自設定が優先して埋め込まれていた。
スタイル崩れ等が起きていないことが確認できたらデプロイを行う。
AMP テスト
「AMP テスト - Google Search Console」にデプロイ後のAMPページURLを入力してAMPページに問題がないことを確認する。
AMPのindex登録について
AMP対応ページ(/some.amp/)は sitemap.xml に追加する必要はないとのこと。
rel=amphtml が記載されていればAMPページも参照してくれる模様。
@Kfowler325 No need for sitemaps for AMP pages -- the rel=amphtml link is enough for us.
— 🍌 John 🍌 (@JohnMu) October 13, 2016
Search Console側は AMP ステータス レポート から確認できるが、Search Consoleに反映されるには数日かかりそうなので、しばらく待ってから再確認しよう。
ひとまずこれでNext.js 10 + Tailwind CSS 2.0環境でのAMP対応ができた。
