Closed14

Eleventy + Web Components のサイトを Next.js でリプレイスするメモ

準備

プロジェクト作成

npx create-next-app プロジェクト名

GitHub

nex create-next-app が Git の設定までしくれるので push しておくだけ

git remote add origin リポジトリURL
git push -u origin main

TypeScript

npm install --save-dev typescript @types/react @types/node
touch tsconfig.json
npm run dev

インストール後に一度開発サーバーを立ち上げることで、tsconfig.json の中身が自動的に記述され、next-env.d.ts が生成されます

ESLint

npm install -D eslint

前に導入したときから選択肢が変わっていたが、雰囲気で答えていきました。

npx eslint --init
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · airbnb
✔ What format do you want your config file to be in? · JavaScript
✔ Would you like to install them now with npm? · No / Yes

導入後 package.jsonlint 系を追加し、npm run lint:fix でエラーを一気に消しました

  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint . --ext .ts,.tsx",
    "lint:fix": "eslint . --ext .ts,.tsx --fix"
  },

npm run lint:fix でも消えないエラーは一旦無視させています

  rules: {
    'react/react-in-jsx-scope': 'off',
    'react/jsx-filename-extension': 'off',
    'react/jsx-props-no-spreading': 'off',
  },

Vercel にリリース

Vercel で Repository を選択肢、ビルドのオプションなどは何も変更せずポチポチしてリリース

特に困ることもなくできた

TypeScript

ReturnType<typeof 関数名>
import styles from './button.module.css';

type Props = {
  children: React.ReactNode;
  onClick: () => void;
};

export default function Button(props: Props) {
  const { children, onClick } = props;
  return (
    <button type="button" onClick={onClick} className={styles.button}>{children}</button>
  );
}

2 画面を書き直した感想

  • コアなロジック的なものは TypeScipt で書いてあったのでコピペでいけそう
  • データを画面に描画するところが React, Next.js, TypeScript で書き心地が良い
    • web components では素の DOM API を叩いてた

next.config.js をラップする

const withPWA = require('next-pwa');

module.exports = withPWA({
  images: {
    domains: [
      'pbs.twimg.com',
    ],
  },
  i18n: {
    locales: ['ja', 'en'],
    defaultLocale: 'ja',
  },
});

manifest.jsonpublic 配下に設置

{
  "name": "Enjoy SFV More",
  "short_name": "Enjoy SFV More",
  "icons": [
    {
      "src": "/src/img/icon/logo-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/src/img/icon/maskable_icon.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/src/img/icon/logo-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/",
  "display": "standalone",
  "background_color": "#3E4EB8",
  "theme_color": "#2F3BA2"
}

_document.tsxHeadmanifest.json の読み込みや他の設定を追加

<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#2F3BA2" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="white" />
<meta name="apple-mobile-web-app-title" content="Enjoy SFV More" />
<link rel="apple-touch-icon" sizes="120x120" href="/src/img/icon/logo-512x512.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/src/img/icon/logo-512x512.png" />

いろいろ足したら next build を実行する

が、今作っているサイトが next build に 30分かかる・・・

GA が AMP でエラーになる(カスタムスクリプトが存在すると言われる

https://zenn.dev/tiwu_dev/books/a5a3332c0b5aa2/viewer/6bee80#%2Fpages%2Fdocument.tsx

dangerouslySetInnerHTML で仕込んでいるところがエラーになる

 <script
            dangerouslySetInnerHTML={{
              __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${GA_TRACKING_ID}', {
              page_path: window.location.pathname,
            });
          `,
            }}
          />

とりあえず分けて実行してみた(なんとか動いていそう)

function MyApp({ Component, pageProps }) {
  const router = useRouter();
  useEffect(() => {
    const handleRouteChange = (url) => {
      // @ts-ignore
      gtag('js', new Date());
      // @ts-ignore
      gtag('config', GA_TRACKING_ID, {
        page_path: window.location.pathname,
      });
      pageview(url);
    };
    router.events.on('routeChangeComplete', handleRouteChange);
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange);
    };
  }, [router.events]);

  return <Component {...pageProps} />;
}

export const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID;

// eslint-disable-next-line prefer-rest-params
export function gtag() { window.dataLayer.push(arguments); }

export const pageview = (url) => {
  if (!GA_TRACKING_ID) {
    return;
  }
  // @ts-ignore
  gtag('config', GA_TRACKING_ID, {
    page_path: url,
  });
};

export const event = ({
  action, category, label, value,
}) => {
  if (!GA_TRACKING_ID) {
    return;
  }
  // @ts-ignore
  gtag('event', action, {
    event_category: category,
    event_label: label,
    value,
  });
};
このスクラップは2021/05/04にクローズされました
ログインするとコメントできます