※トリスタinsideに投稿された記事の再掲載です。
この記事はドワンゴ Advent Calendar 2018の10日目の記事です。
こんにちは。
ニコニコ静画でフロントエンド開発を行っている@nagisioです。
今年は冬コミに無事当選し、原稿に追われています :innocent:
去年の記事は「少人数でのフロントエンド開発をクイックに進めるために」でした。
今年一年間のフロントエンド環境ですが、React + MobXでの構成は変わらず採用しており安定感があります。
ただし、Flowに関しては少し取り回しのしづらさがあると感じる点があり1、現在はTypeScriptへの移行も検討しています。
周りの環境も少しづつ動いており、今年は特にVue.jsが目立った年になりましたね!
PWA(Progressive Web Apps)やGraphQL(Apollo)といった新しい技術にも注目です。
さて、お気付きのとおり本記事は「トリスタinside」というサイトで公開しています2。 本記事ではこのトリスタinsideの開発過程について、技術的な話を交えて紹介します。
技術選定
開発に当たっての技術選定ですが、まずは以下の要件を基にいくつかフレームワークを選定しました。
- JavaScript製のフレームワークであること
- 記事の投稿やメンテナンスが容易なこと
- (できれば)Reactが使えること
ここで挙がったフレームワークとしてはHexo, Next.js, Gatsby.jsなどがありました。
Next.jsに関しては既に開発経験がありましたが、ブログとしての用途を考えると(マークダウンの記事を変換したり、記事のページを自動生成するなど)採用しづらい側面がありました。
一方で、Hexoはデザインテンプレートも豊富にあり採用しやすい雰囲気はありましたが、テンプレートを使うとどうしてもスタイルの拡張がしづらく、こちらも見送りとなりました。
最後にGatsby.jsはReactでコンポーネントが書けること、内部でGraphQLが利用されていることなどから、新しい技術への挑戦も含めてGatsby.jsを採用しました。
モックサイトの作成
Gatsby.jsにもテンプレートはいくつか用意されていますが、せっかく作るならデザインから作りたいという気持ちがありました。
ということで、デザイナーにデザインの依頼をしつつ、まずは簡単なモック(ブログとして動くところまで)を作成していきます。
Gatsby.jsのチュートリアルを読みながら開発を進めれば、ステップバイステップでGatsby.jsの仕組みや開発方法について学ぶことができます。
とはいえ、初めて開発サーバを起動した画面は本当に何もないため、絶望感すらありましたが…
また、GraphQLは初めて触る技術であったため、まずはGraphQLは何たるかを知る必要がありました。
幸いにしてGatsby.jsは開発時にGraphiQLという、実際にクエリを実行できるサーバを別途立ち上げてくれるため、ドキュメントを片手にクエリを叩けば何となく理解はできるでしょう3。
Gatsby.jsは上図のように、データ(マークダウンファイル)からGraphQLクエリを発行し、このクエリを実行することでコンポーネントやページを作成していきます。
例えば全記事を取得するクエリは次のような関数で実現できます。
const fetchBlogPosts = graphql => graphql(` { allMarkdownRemark { edges { node { frontmatter { title author category tags } } } } } `);
このクエリを実行すると、次のようなObjectが返ってきます。
{ "data": { "allMarkdownRemark": { "edges": [ { "node": { "frontmatter": { "title": "トリスタinsideが生まれるまで", "author": "nagisio", "category": "engineering", "tags": [ "JavaScript", "Gatsby.js", "GraphQL" ] } } }, ... ] } } }
クエリ結果を利用することで、例えば各カテゴリーのページを作成できます。
const createCategoryPages = (posts, createPage) => { const categoryTemplate = path.resolve('./src/templates/categories.js'); let categories = []; posts.forEach(({ node }) => { const postCategory = node.frontmatter.category; if (postCategory != null) { categories.push(postCategory); } }); categories = _.uniq(categories); categories.forEach(category => { createPage({ path: `/category/${_.kebabCase(category)}`, component: categoryTemplate, context: { category } }); }); };
チュートリアルではこうしたGatsby.jsとGraphQLによる、ブログサイト作成の基礎について学ぶことができます。
ただし、ブログとしては最低限の機能だけを備えているといった状態で、このままではもちろん公開できません。
この後のさらなる開発を控えて、まずは開発環境をより良くすることを目指していきます。
より開発しやすい環境を作る
より開発しやすく、保守しやすい環境を作成するために、ライブラリやモジュールなどを導入していきます。
JavaScript
主にFlowの導入とLinterの導入です。LinterはESLintとPrettierでの構成としています4。
Gatsby.jsではgatsby-plugin-flowプラグインが用意されているので、こちらを利用しています。
CSS
CSSはStyledComponentsを採用しました。
StyledComponentsの特徴はPropsを経由してCSSの出し分けができることで、また、SPA(Single Page Application)であるGatsby.jsとの相性も良いでしょう(もちろんプラグインも用意されています)。
また、LinterとしてStyleLintを導入しています。
Test
ユニットテストに関しては、残念ながら導入はできていません。 そのため、テストの導入は今後の課題としています。
ただし、プルリクエスト時にCIを経由してサイトのビルド及びデプロイを行っています。
これより、事前にサイトの確認ができるようにしています。
新規機能の開発
より見栄えがよく、使い勝手の良いサイトを目指すために、新規機能の開発は必須です。
例えばこのページトップにある著者アイコンや各種シェアボタン、右下にあるトップへ戻るボタンなどが新規機能にあたります。
新規機能の開発では、AtomicDesignを基にして小さなコンポーネントを組み合わせて作っていきます。
例えば著者アイコン画像を表示する<Avatar>
コンポーネントは以下のようなJSXファイルです。
// @flow import React from 'react'; import { graphql } from 'gatsby'; import Img from 'gatsby-image'; import styled from 'styled-components'; type Props = {| avatar: Object, alt?: string, size?: number |}; const StyledAvatar = styled(Img)` margin: 0; border-radius: 50%; ... `; const Avatar = (props: Props) => ( <StyledAvatar fixed={props.avatar.childImageSharp.fixed} alt={props.alt} size={props.size} /> ); Avatar.defaultProps = { alt: '', size: 50 }; export default Avatar; export const query = graphql` fragment AvatarFragment on File { childImageSharp { fixed(width: 128) { ...GatsbyImageSharpFixed_noBase64 } } } `;
ReactとGraphQLの相性が良いため、クエリを利用しながら小さなコンポーネントを簡単に作成できます。
また、上記のコンポーネントでは画像の展開にgatsby-imageプラグインを利用しており、ビルド時に画像の自動圧縮やlazyloadの組み込み、ブラウザサイズによる画像の出し分けなどを自動で行ってくれます。
<picture> <source srcset="/static/nagisio-7bd1c.jpeg 1x, /static/nagisio-47a41.jpeg 1.5x"> <img alt="nagisio" width="128" height="128" src="/static/nagisio-7bd1c.jpeg"> </picture>
実際に出力されるHTML - <picture>タグが使われている
Gatsby.jsはこうしたかゆいところに手が届く便利なプラグインが豊富に用意されており、大抵の面倒なことはプラグインに任せることができます5。
例えばgatsby-plugin-google-analyticsプラグインは、設定を書くだけでビルド時にGoogleAnalyticsを仕込んでくれます。
プラグインを活用することで、機能の開発に集中できるのはとても良いですね :smile:
記事の執筆
さて、ここまでの開発でブログはほぼ完成しましたが、肝心の記事がまだありません。 記事はマークダウン形式で書くことができ6、また、開発サーバを立ち上げればリアルタイムでページが更新されるため、快適に執筆できます。
トリスタinsideでは更に記事の品質を向上させるために、TextLintを導入しています7。
TextLintは主に日本語向け文章のLintツールで、これを用いて文章校正を行うことができます。
73:25 error 文末が"。"で終わっていません。末尾に不要なスペースがあります。 133:34 error 一文に二回以上利用されている助詞 "が" がみつかりました。
現在執筆中の文章で既に出ているエラー例 :innocent:
TextLintによってブログ全体の記事に対して、文章の統一と読みやすさの向上が期待できます。 実際に、技術書典5における執筆ではTextLintが大いに活用されました8。
今後の展望
トリスタinsideの今後の展望としては以下のような課題の解決が挙げられます。
- サイトパフォーマンスの向上
- PWA対応
- 記事の充実化
Gatsby.jsはSPAで動いており、さらにCode Splittingやprefetchなどを活用して最適化されているため、遷移などはとても速く実現できています。
しかしながら、TOPページのレンダリングスピードについてはそれほど速くはありません。
Chrome Lighthouseの計測では、上のようにPerformanceの点数がやや低めとなっています。 これらのパフォーマンスはまだまだ改善の余地があるため、調整をしていく必要がありそうです。
同じく点数が低いPWAへの対応も課題です。 PWAを実現することでオフライン対応やアプリ化といったことが可能となります。 今後挑戦していきたい技術の1つです。
最後に、記事の充実化と継続的な投稿を目指し、社内におけるアウトプット文化を広めていきます。
これは私一人の力では実現できないので、トリスタの全員で協力していきます :muscle:
まとめ
本記事ではトリスタinsideがどのように生まれたか、また、どのように動いているかについて解説をしました。 Gatsby.jsは静的サイトジェネレータではありますが、ReactやGraphQLといった最新の技術を学べる良いフレームワークだと感じています。
今後はパフォーマンスの向上や記事の充実を行い、トリスタinsideがもっとより良いサイトになるよう目指していきます。