もやもやエンジニア

IT系のネタで思ったことや技術系のネタを備忘録的に綴っていきます。フロント率高め。

Gatsby.jsでTypeScriptで書かれたReact/ReduxなSPAを配信する

個人で公開しているSPAが素朴なReactで作られていてSSRも何もしておらず、もうちょっとGoogle先生Botに優しく作ってあげようということで、前段階の準備としていろいろ試してみたメモです。対象のサイトはそんなに大きくないので、SSRを頑張るのではなくて事前にレンダリング済のページを静的なHTMLに出力して配信という形式にします。

作ったもの

いきなり作り変えるには知見がないのでReduxのチュートリアルでよくやるTodoアプリケーションをGatsby.jsを使って実装して、できたものをNetlifyにデプロイしました。言語は一部のGatsbyの設定ファイル以外はTypeScriptで書いています。

で、作ってみたのがこちら gatsby-ts-todo.netlify.com

オリジナルのチュートリアルには無いページですがTodoの詳細のページを追加していて 、pathはtodos/:id/ になります。Gatsby.jsで動的に静的なページを生成するための検証として、Todoを2つ初期状態で登録済にしておいて、これらのTodoの詳細ページは静的なページとして配信して、URLを直接叩いて見えるようになっています。

これは直接見える https://gatsby-ts-todo.netlify.com/todos/1/

これは直接見えない。 https://gatsby-ts-todo.netlify.com/todos/3/

はまったところ

ページを動的に生成するためにはgatsby-node.jsに生えているAPIを実装するのですが、ここもTSで書きたいなあというとこで、ちょっと試行錯誤しました。結果としては以下のようになりました。

gatsby-node-config.tsにTSで実装を書いてます。createPagesではStoreの初期データからcreatePageしてるのがわかると思います。

gatsby-node-config.ts

import { resolve } from 'path';
import { GatsbyCreatePages, GatsbyOnCreatePage } from './types';
import { initialData } from '@src/state/todos';
import { Todo } from '@src/types';

export const createPages: GatsbyCreatePages<{ todo: Todo }> = async ({ actions }) => {
  const { createPage } = actions;
  initialData.forEach(todo => {
    createPage({
      path: `/todos/${todo.id}`,
      component: resolve(`./src/templates/todos.tsx`),
      context: {
        todo,
      },
    });
  });
};

export const onCreatePage: GatsbyOnCreatePage<{ todo: Todo }> = async ({ page, actions }) => {
  const { createPage } = actions;

  // page.matchPath is a special key that's used for matching pages
  // only on the client.
  if (page.path.match(/^\/todos/)) {
    page.matchPath = '/todos/:id';
    createPage(page);
  }
};

で、本来実装する gatsby-node.js では gatsby-node-config からrequireしてexportしてスルーパスしています。configのビルドはts-nodeを使うことでうまいこと通りました。

gatsby-node.js

'use strict'

require('source-map-support').install()

require("tsconfig-paths").register({
  baseUrl: './',
  paths: {
    '@src/*': [ 'src/*' ],
    '@test/*': [ 'test/*' ]
  }
});

require('ts-node').register({
  compilerOptions: {
    module: 'commonjs',
    target: 'es2017',
    noImplicitAny: false
  }
})

const { resolve } = require('path');

const config = require('./gatsby-node-config');

exports.createPages = config.createPages;

exports.onCreatePage = config.onCreatePage;

exports.onCreateWebpackConfig = ({ actions }) => {
  actions.setWebpackConfig({
    resolve: {
      alias: {
        '@src': resolve(__dirname, 'src/'),
        '@test': resolve(__dirname, 'test/')
      }
    }
  })
}

おしまい

Gatsbyチュートリアルが充実してたので、Reactを触ったことがあれば基本的な機能は割とすぐ使えるかなという印象です。GraphQLまわりのところがちょっとまだ完全には把握できてないので、そのあたりをもうちょっとドキュメントを読みつつ試してみようかなと思います。

全体のコードはこちら

GitHub - rei-m/gatsby-todo: Todo Application created by Gatsby with TypeScript.