とろろこんぶろぐ

かけだしR&Dフロントエンジニアの小言

Next.js + AMP + typescript + styled-components ( + storybook)でやったことと気をつけること

Next.js + AMP + typescript + styled-components ( + storybook)でやったことと気をつけること

  • Next.jsでAMP化するときは、 pagesのコンポーネントに対して、nextAmpでwithAmpをimportし、withAmpで囲う。
import { withAmp, useAmp } from "next/amp";
  • withLayoutも使いたいときはそれも囲う
withAmp(withLayout(Index), { hybrid: true });
  • hybrid: trueをつけているときは、URLに ?amp=1 をつけているときだけAMP化されたHTMLが返る

  • getInitialProps はデータをfetchしてSSRしてくれるのでAMPで問題なく動く。

  • amp-customのscriptソースは、_document.tsxで内に記述する。 これは、理想的にはそれを使うコンポーネント内で指定したいが、複数呼び出すと複数head内にscriptが記述されてしまうのでよくない。

  • amp-customのstyleは、_documentのgetInitialPropsでstyled-componentsのServerStyleSheetからstylesheetを文字列に直し、inlineCssとしてSSR時にhtml内に吐き出すように書く。

  static getInitialProps({ renderPage }: { renderPage: any }) {
    const sheet = new ServerStyleSheet();
    const page = renderPage((App: any) => (props: any) =>
      sheet.collectStyles(<App {...props} />),
    );
    const stylesheets = [...sheet.getStyleElement()];
    const inlineCss = stylesheets.reduce(
      (inlineStyles, currentStylesheet) =>
        currentStylesheet
          ? `${inlineStyles}${
              (currentStylesheet.props as any).dangerouslySetInnerHTML.__html
            }`
          : inlineStyles,
      "",
    );
    return { ...page, inlineCss };
  }
          <style amp-custom="">{(this.props as any).inlineCss}</style>
  • ampのカスタムエレメント向けに型を用意する ampのカスタムエレメントやon属性はreact-domが型として未定義の情報なので、jsx.d.tsというような形で型情報を用意する。
declare namespace JSX {
  interface AmpHtml {
    children: React.ReactNode;
    amp?: any;
  }
  interface IntrinsicElements {
    html: AmpHtml;
  }
}

必要な型情報は追加していく。

  • ampでcssの擬似要素に content: "" は許容されてない。 iconなどはicon用の画像をコンポーネント別に用意する必要がある。

  • storybookで見る preview-head.html で headタグは足せるので、amp向けのscriptを追加は可能。ただし動作は安定しない。

  • yarn dev の developビルドだと__NEXT_DATAのようなnext.js向けのjavascriptがhtml内にinsertされるので注意。出力したHTMLをamp-validatorとかにかけてもエラーになる。yarn buildして閲覧したhtmlなら大丈夫。

  • amp-stateは_document.tsxで定義する。<script type="application/json"> はreactだと内部に記述するjsonjsonとして読み込まないので書くときには注意が必要。dangerouslySetInnerHtml を使うか、文字列で記載するようにする。 srcで指定するようにして、外部からjsonを取得するのが迷わないのでオススメ。

  • もしstyled-componentsを使っている場合は、on属性でイベントトリガーを記述する要素にstyleをあててはいけない。on要素がstyled-componentsのレンダリング時に(なぜか)破棄されてしまうので、直接div要素で指定する。

  • ampのstateを利用する際に [class] といった形式では記述できないので、 data-amp-bind-class といった data-amp-bind-xxx の形式を用いる。

  • amp-stateは、デバッグコンソールで AMP.printState() を呼び出すと確認できる。ただし、URLで #development=1 を指定しておかないと見れないので注意。