はじめに
こんにちは。 odmishienです。この記事はDeNA 21 新卒 Advent Calendar 2020の12日目の記事です。実は4日目も担当していました。
4日目に書いた記事はどちらかというと技術的な話というより勉強法や精神論的なポエムだったので、今回は技術ネタを持ってきました。
私はアルバイトの業務では主にバックエンドを担当していて、特にPerl, Python, Go などを書くことが多いです。Webフロントエンドに関しては知らないことばかり...という感じで、Vue.jsを趣味のプロダクトで使ってみたことがあり、かろうじてコンポーネントを理解している…というようなレベルです。
でも....カッコよくて便利なポートフォリオが欲しい.....!!!!
そんな気持ちが、ポートフォリオになりました❤︎
今回はWebフロントエンドをバリバリに書いたことのない私がGatsby.jsを使ってポートフォリオサイトを作るにあたってやってみたことをまとめてみようかと思います。
これまでのポートフォリオの問題点
なんとも言えないパフォーマンス
以前作っていたポートフォリオはVue.jsで作成してGitHubPagesでホストしていました。Lighthouse Report Viewerのスコアがこんな感じ。
そこまで重たいコンテンツを置いているわけではないのですが、スコアが微妙。
コンテンツをHTMLにベタ書きして更新しなければならない
ポートフォリオなので作ったものを掲載したりしていたのですが、これを手動でやっていました。具体的には以下のようなコンポーネントを作成してそこに props
を渡したものを毎回書いていました。
<template>
<div class="card">
<img class="card-img-top" width="100%" height="50%":src="src" />
<div class="card-body">
<h4 class="card-title">{{ title }}</h4>
<p class="card-text">{{ text }}</p>
<div class="row my-2">
<a
v-if="appLink"
class="btn btn-primary col-4 offset-2"
target="_blank"
:href="appLink"
>Check App</a>
<a v-if="githubLink" class="col-4" target="_blank" :href="githubLink">
<LogoGithubIcon w="40px" h="40px"></LogoGithubIcon>
</a>
</div>
<div class="tags">
<p v-for="tag in tags" class="card-text text-muted">#{{ tag }}</p>
</div>
</div>
</div>
</template>
<script>
import LogoGithubIcon from "vue-ionicons/dist/logo-github.vue";
export default {
name: "card",
components: {
LogoGithubIcon
},
props: {
imgPath: {
type: String,
required: true
},
title: {
type: String,
required: true
},
text: {
type: String,
required: true
},
appLink: {
type: String,
default: null
},
githubLink: {
type: String,
default: null
},
tags: {
type: Array,
required: true
}
},
data() {
return {
src: require(`../assets/${this.imgPath}`)
};
}
};
</script>
新しいものを作成するたびにポートフォリオの方も修正して、buildして、pushして....みたいなことをしていて、結構面倒でした。
手動でビルドしないといけない
GitHubPagesを使っていたので、決まったブランチの決まったディレクトリにpushさえすればデプロイはできたのですが、手元でwebpackを使ってbuildしてあげる必要がありました。よくコマンドを忘れてhistoryから探したり、「あれ?ビルドしたっけ?」となることがあったりして、少し不便でした。
そもそもGatsby.jsとは何か
Gatsby is a React-based open source framework for creating websites and apps. Build anything you can imagine with over 2000 plugins and performance, scalability, and security built-in by default.
- 早いらしい
- プラグインで色々簡単に拡張できるらしい
- コマンドポチポチしたらサイトができてるらしい
とにかく導入が簡単!コマンドを打つだけで複雑な設定は隠蔽してくれます!!というインターネットの声をよく目にしました。こういうなんでもやってくれる系のフレームワークはお節介が過ぎると敬遠してしまう場面もありますが、Webフロントエンドに精通していない私にとっては手取り足取りして欲しい!ということでGatsbyを導入してみることにしました。
プロジェクトの作成方法など、Gatsbyのコマンドに関する話はここでは割愛させていただきます。(インターネットにたくさん良記事が転がっていた記憶があります)
構成
- フロントエンド
- TypeScript+React(Gatsby.js)
- ホスティング
- GitHub Pages
- デプロイ
- GitHub Actions
ある程度長くメンテナンスするだろうなということとお勉強のためにTypeScriptを選びました。Reactを使うのはGatsby.jsの場合避けられません。
ホスティングはこれまでも使っていて使い慣れているGitHub Pagesを採用しました。また、デプロイも親和性の高いGitHub Actionsで行うことにしました。
GitHubのリポジトリ情報にGraphQL経由でアクセスする
ldd/gatsby-source-github-api を使います。
READMEの通りに設定していきます。まずは gatsby-config.js
にプラグインを追加します。
{
resolve: "gatsby-source-github-api",
options: {
token: process.env.GITHUB_API_TOKEN,
graphQLQuery: `
query ($nFirst: Int, $q: String!) {
search(query: $q, type: REPOSITORY, first: $nFirst) {
edges {
node {
... on Repository {
id
name
description
url
}
}
}
}
}`,
variables: {
q: "topic:portfolio user:odmishien",
nFirst: 10,
},
},
}
今回は portfolio
というtopicが指定された 私のリポジトリを10件取ってくる設定になっています。これで人様に見せてもいいと思うリポジトリには portfolio
というtopicを付けておくだけでOKです。
そしてindexページのtsxファイルの中でGraphQLでデータにアクセスします。
import { IndexQuery } from "../../types/graphql-types"
interface IndexProps {
data: IndexQuery
}
const IndexPage: React.FC<IndexProps> = ({ data }) => {
const repos = data.githubData?.data?.search?.edges
... // 省略
}
export const query = graphql`
query Index {
githubData {
data {
search {
edges {
node {
id
name
description
url
}
}
}
}
}
}
`
ひとまずこんな感じでリポジトリとそのdescriptionを表示させることに成功。
GraphQLのクエリの構築に結構苦戦しましたが、GitHub GraphQL API Explorer で型の補完を効かせながら試行錯誤しました。
また同じようなことを自分の技術ブログ(はてなブログ)のRSSフィードから生成するみたいなことをmottox2/gatsby-source-rss-feedを使って実現しています。
GraphQLのスキーマをTypeScriptの型に落とし込む
GraphQLを使ってAPIなどのレスポンスを使えるのは便利なのですが、その構造を型に落とし込むのを手動でやっていくのはちょっと骨が折れます。先ほど触れなかったのですが
import { IndexQuery } from "../../types/graphql-types"`
としている部分があったことにお気付きでしょうか。これは私が定義したのではなく、 gatsby-plugin-graphql-codegen を使って、型定義ファイルを自動で生成しています。
またも、gatsby-config.js
の中にプラグインを書いていきます。
{
resolve: "gatsby-plugin-graphql-codegen",
options: {
fileName: `types/graphql-types.d.ts`,
},
}
これで types/graphql-types.d.ts
というファイルに gatsby build
されるたびに発行しているqueryに応じて、型定義が追加されていきます。
例えば私のポートフォリオの場合、index.ts
の末尾に以下のようなIndex
という名前のクエリを発行しています。
export const query = graphql`
query Index {
...省略
}
`
ビルドしてみると、この名前に Query
というsuffixが付いた IndexQuery
という型が定義されているのが確認できます。便利!
GitHub Actionsを使って決まった時間に自動デプロイ
GitHubやRSSから欲しい情報を取ってくることはできますが、コンテンツの更新はビルドし直さないとできません。そこでmasterにpushがあった時と毎日夜0:00にビルドしてGitHub PagesにデプロイするというActionを仕込みます。これはkentaromさんの記事がとても参考になりました。
name: Deploy
on:
push:
branches:
- master
schedule:
- cron: "0 0 * * *"
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: setup node 14.x
uses: actions/setup-node@v1
with:
node-version: "14.x"
- name: install
run: npm install
- name: build
run: npm run build
env:
GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
cname: your.domain.com # 独自ドメインを利用したい場合
secrets.GITHUB_TOKEN
の方はActionsのworkflow内で使える自動で生成されたトークンなのでsecretsに登録する必要がありません1。便利!
peaceiris/actions-gh-pages は github_token
を指定しておくだけで、デフォルトで gh-pages
ブランチにpublic
ディレクトリの内容をデプロイしてくれます。カスタマイズしたかったらこれらの値をyamlに書いてあげてください。
また、独自ドメインを利用したい場合は cname
フィールドに利用したいドメインを指定しておきます。これをしないとデプロイの度にCNAME
ファイルが作られないので、毎回デフォルトのURL(https://username.github.io
)が指定されてしまいます。
まとめ
無事読み込みも速くなって、オールグリーン!ビルドやデプロイのことはGitHub Actionsが全て担ってくれるので、私がやることはなくなりました。サイコー!Webフロントエンドのことをあまりよく分かっていない私でもある程度のセッティングができたので便利ですね、Gatsby!!
改善点としては
-
npm install
を毎回やってしまっているので、キャッシュする - GraphQLについてかなりフィーリングで触っているので勉強して無駄のないクエリを書く
- 掲載するコンテンツを増やす
などでしょうか。随時手を入れていこうと思います。
最後までお読みいただきありがとうございました。
参考になった記事
宣伝
この記事を読んで「面白かった」「学びがあった」と思っていただけた方、よろしければ Twitter や facebook、はてなブックマークにてコメントをお願いします。
また DeNA 公式 Twitter アカウント @DeNAxTech では、 Blog記事だけでなく色々な勉強会での登壇資料も発信してます。ぜひフォローしてみて下さい。