EY-Office ブログ

EY-OfficeサイトもJamstackブームに乗ってみようと思う(2)

EY-OfficeサイトもJamstackブームに乗ってみようと思う(1) を開発や教育業務のあいまに進めています。まだNext.jsへの移行は完成していませんが、問題点もわかってきたので中間報告を書きます。

NEXT.js

Jekyll版EY-Officeホームページの構造

まず、現状のJekyll版EY-Officeホームページですが、以下の要素に分かれます。

  • コンテンツ
    • HTML(+Liquid) : トップページ(/)やブログ・トップページ(/blog)など複雑なレイアウトのページはHTMLとLiquidテンプレートを使って書いています
    • Markdown : 業務案内(/education)や教育コース(/education_react)のページは、Markdwonで書いています
  • ブログ記事
    • ブログ記事は、_postディレクトリーに 2020-07-09-what-is-docker.md のような日付+Slugのファイル名のMarkdwonファイルです。これをJekyllが /2020/07/09/what-is-docker/index.html のようなパスに変換します
  • レイアウトや共通HTML片
    • レイアウトやヘッダー、メニュー等のHTMLとLiquidテンプレートを使って書かれています
  • Jekyllプラグイン
    • Liquidテンプレートで出来ない事はRubyでプラグインを書いています

ツール

今回つかったJamstack(Static Site Generators)ツールを簡単に説明します。

Next.js

Next.jsは2016年にserver-renderedを含むReact用フレームワークとして生まれました。

・Server Side Rendering(SSR)

通常Reactはブラウザー上で動くJavaScriptが画面を構築するので、当時のJavaScriptを解釈しないGoogleクローラーではGoogleに正しいページ情報が掲載されず、SEO上の問題がありました。また最初の画面がブラウザーに表示されるまで時間がかかるなどの問題もありました。

この問題を解決する方法として、サーバー側でトップページ等のHTMLを作成し、ブラウザーやクローラーに渡すServer Side Rendering(SSR)技術が、SEOや初期表示を高速化する技術として注目されました。

・Static Site Generators(SSG)

その後のJamstackブームに乗り、SSRだけでなく、サーバーサイドでHTMLを作ってしまうStatic Site Generators(SSG)としての機能もアピールするようになりました。

・Zero Configuration(超便利React開発環境)

create-react-app登場前は、Reactアプリの開発環境を作るにはいくつものソフトをインストールし複数の設定ファイルを書く必要がありました。
しかしNext.jsでは、 npm install next react react-dom を実行するだけで、直ぐにReactアプリの開発が始められます。TypeScript対応も空のtsconfig.jsonファイルを作るだけで完了です。またシンプルなルーティング機能も入っていてSSR/SSGを使わなくても使う人がいるほど良いツールです

MDX.js

Jekyll版EY-OfficeホームページもMarkdownでコンテンツを書いています。HTMLコンテンツは少し変更するだけで、JSX(Reactコンポーネント)になりますが、Markdownはどうしましょうか?

検索していたら、MDXを発見しました。これはReactのJSXの替わりにMarkdownで書けるライブラリーです。サンプルコードは最後に取り上げます、これを使う事でMarkdownコンテンツも少しの変更でReactコンポーネントとして使えるようになります。

また、MDXはNext.js用プラグインも用意されていてNext.jsで直ぐに使えます。その他Gatsby用のプラグインなども用意されています。

ホームページ・コンテンツ

ディレクトリー構造

Next.jsの作る標準の環境を使っています。pages/の下のコンテンツのReactコンポーネントが置き、components/の下にはレイアウトや共通のReactコンポーネントを置きました。

lib/の下には情報やユーティリティ的なTypeScriptファイル、public/の下にはcssや画像が置かれています(node_modules/やpackage.json等は省略しました)。

.
├── components                レイアウトや共通コンポーネント
│   ├── default_layout.tsx
│   ├── foonter.tsx
│   ├── home_layout.tsx
│   ├── html_header.tsx
│   └── navigation.tsx
├── lib
│   ├── blog_posts.ts
│   └── site.ts
├── next.config.js
├── pages                     コンテンツのReactコンポーネント
│   ├── education.mdx
│   ├── education_react.mdx
│   └── index.tsx
├── public
│   ├── css
│   └── images
└── tsconfig.json
index.tsx

トップページのReactコンポーネントのコードの一部です、普通のReactのコンポーネントですよね。<HomeLayout> はホームページ用のレイアウト・コンポーネントです、コードは下に置きました。
他は元のHTMLを少し変更しただけです、ただしLiquidテンプレートの部分はReactに置き換えました、Blogを扱うライブラリーを作ったので以前て比べシンプルになりました。

最下部のgetStaticPropsはNext.js用の関数です。SSR(SSG)の際にこの関数が動作しブログ情報を取得し、最新のブログ情報のみHomePageコンポーネントに渡しています。

import React from 'react'
import HomeLayout from '../components/home_layout'
import { getBlogEntries, BlogEntry } from '../lib/blog_posts'

type HomePageProps = {
  firstPost: BlogEntry
}

const HomePage: React.FC<HomePageProps>  = ({firstPost}) => {
  return (
  <HomeLayout>
    <section className="sect-wrap news" id="news">
      <h2 className="section__title section--blue">お知らせ</h2>
      <div className="news__box">
        <div className="news__topic">
          <div className="news__date">{firstPost.date}</div>
          <div className="news__category">ブログ</div>
          <a href={firstPost.url} className="news__heading">{firstPost.title}</a>
        </div>
        <div className="news__topic">
          <div className="news__date">2020-05-21</div>
          <div className="news__category">教育</div>
          <a href="/education_remote" className="news__heading">リモート教育を開始いたしました!</a>
        </div>

   ・・・

      </div>
    </section>
  </HomeLayout>
  )
}

export default HomePage

export const getStaticProps = async () => {
  const posts = await getBlogEntries()
  return {
    props: {
      firstPost: posts[0]
    }
  }
}

home_layout.tsx

<HomeLayout>のコードです、普通のReactのコンポーネントですよね。

import React from 'react'
import HtmlHeader from './html_header'
import Footer from './foonter'
import Navigation from './navigation'

const HomeLayout: React.FC  = ({children}) => {
  return (
    <>
      <HtmlHeader/>
      <header>
        <div className="head__logo"><a href="/"><img src="/images/logo.svg" alt="logo"/></a></div>
        <Navigation />
        <div className="head__bg">
          <h1 className="head__discription">
            ビジネスを始める方むけのサービス・アプリ開発と<br/>
            成長するためのソフトウェア開発者向け教育
          </h1>
        </div>
        <div className="nav__item mb-foot"><a href="/contact" className="nav__item--hexagon">
          <i className="far fa-envelope"></i>お問合わせ</a></div>
      </header>
      <main>{children}</main>
      <Footer/>
    </>
  )
}

export default HomeLayout
education.mdx

Markdownで書かれたコンテンツのページです。MDX内にはJSXが書けるので、レイアウト用のReactコンポーネントをDefaultLayoutを作り使っています。

import DefaultLayout from '../components/default_layout'

<DefaultLayout
  title="教育サービス"
  description="EY-Officeの教育は画一的な集合教育ではなく御社の開発プランや開発者のスキルに合わせた教育・・・"
>

<section className="content__top">

#### EY-Officeの教育は画一的な集合教育ではなく
#### 御社の開発プランや開発者のスキルに合わせた教育を**リモート**、または御社に出向き実施します。

![EY-Officeの教育](/images/2ndTraining-discription.png)

 現在のソフトウェアはかつてない早さの技術進歩、システムの複雑化、短い開発期間を要求されています。
 <br/>この要求に応えるには技術者のスキルアップが必須です。 EY-Officeでは、生産性を本当に上げられる技術、
 開発者のモチベーションを高められる開発手法等の教育を提供し、経験豊かな開発者がお客様の問題解決をサポートします。

</section>

#### EY-Officeの教育の特徴

* Zoom, Slack, GitHubを組み合わせたリモート教育を行います [EY-Officeのリモート教育](/education_remote)
* または、御社に出向き教育を行います
* 御社の業務、開発者のスキルに合わせた教育プランをご提案
* 教育後のOJT(オプション)により、プロジェクトを成功に導きます
* 実際にReact.js, Ruby on Rails, iPhone(iOS)開発を行っている開発者が教えます
* 適切な価格

   ・・・

</DefaultLayout>

Jekyll版では以下の様にtitleやdescriptionはYAML形式で指定していましたが、今回はこれを辞めてレイアウト用コンポーネントのパラメーターで渡す事にしました。

---
layout: default
title: "教育サービス"
description: "EY-Officeの教育は画一的な集合教育ではなく御社の開発プランや開発者のスキルに合わせた教育・・・"
---

問題点

説明リストが表現されない

JekyllのMarkdownでは、説明リスト(dl, dt, dd)をサポートしているのですが、MDXではサポートしていませんでした。しかしMDXで使っているMarkdown処理系 remark 用の説明リスト用プラグインremark-deflist を見つけインストールしてみましたが、どうもパーサーの解釈が違うようで今のところ上手く行っていません。

ブログ記事が上手くいかない

さて、ブログ記事はJekyll同様に_post/デレクリーから読み、HTMLコンテンツを作る処理をNext.jsの特徴であるSSR処理の getStaticProps() , getStaticPaths() で行おうと試みたのですが上手く行きませんでした。Next.jsは純粋なSSGではなくSSR用ツールなのだと言うことを理解しました・・・・

続きは、次回(?)・・・

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ