静的サイトジェネレーター「Gatsby.js」について知りたかったのでUdemyのコースで学び、その備忘録となります。
以下の内容となっています。
- 「Gatsby JS: Build static sites with React Wordpress & GraphQL」を参考
- PluginはGatsby.jsの「公式サイト」を参考に設定
- Headless CMS(WordPress)で投稿したdataをGraghQLで取得してGatsby(React)でサイト表示
- 投稿、固定ページ、カスタム投稿ページ、カスタムフィールドに対応
- Contact Formについは未実装
- GraphQLのqueryについては別の記事でまとめています。「Gatsby.jsでWordPressのAPIをGraphQLで取得しるときによく使うquery」
- 静的Webホスティングサービスの「Netlify」でサイト公開する手順は別の記事でまとめています。HeadlessCMS(WordPress)とGatsby.jsで制作したサイトをNetlifyで公開する手順
Set Up
環境
- MAMPでlocal環境構築
- localのphpmyadminでDB接続
API
WordPressが提供しているREST APIからdataを取得するのではなくGraphQLでdata取得
REST API例
http://localhost:8888/myawesomeportfolio.io/wp-json/wp/v2/pages
gatsbyをinstall
$ npm install -g gatsby-cli
WordPressのREST APIをGatsbyにデータをpullさせるプラグインを設定
- npm「gatsby-source-wordpress」をinstall
-
gatsby-config.js
にplugin設定。公式サイトを参考 -
baseUrl
でMAMPで設定したWordpressのURLを指定
WordPressのdataをNode.jsで読み込みRouting処理
-
CreatePage
を設定- component(適用したいcomponentのpathを指定)
- context(componentのpropsで受け取るvalue)
- path(Routing処理)
gatsby-node.js
const _ = require(`lodash`)
const Promise = require(`bluebird`)
const path = require(`path`)
const slash = require(`slash`)
exports.createPages = ({ graphql, actions }) => {
const { createPage, createRedirect } = actions;
return new Promise((resolve, reject) => {
// ==== 固定ページ ====
graphql(
`
{
allWordpressPage {
edges {
node {
id
slug
status
template
title
content
template
}
}
}
}
`
)
.then(result => {
if (result.errors) {
console.log(result.errors)
reject(result.errors)
}
// Create Page pages.
const pageTemplate = path.resolve("./src/templates/page.js")
_.each(result.data.allWordpressPage.edges, edge => {
createPage({
path: `/${edge.node.slug}/`,
component: slash(pageTemplate),
context: edge.node,
})
})
})
// ==== END 固定ページ ====
// ==== 投稿ページ ====
.then(() => {
graphql(
`
{
allWordpressPost {
edges{
node{
id
title
slug
excerpt
content
}
}
}
}
`
).then(result => {
if (result.errors) {
console.log(result.errors)
reject(result.errors)
}
const postTemplate = path.resolve("./src/templates/post.js")
_.each(result.data.allWordpressPost.edges, edge => {
createPage({
path: `/post/${edge.node.slug}/`,
component: slash(postTemplate),
context: edge.node,
})
})
resolve()
})
})
// ==== END 投稿ページ ====
})
}
-
context: edge.node
と指定するとpropsにdataが入る
src/templates/page.js
import React from 'react';
export default ({ pageContext }) => (
<div>
<h1>
{pageContext.title}
</h1>
</div>
)
- pageでredirect処理
- root pathで
/home
にredirect
const { createRedirect } = actions;
createRedirect({ fromPath: '/', toPath: '/home', redirectInBrowser: true, isPermanent: true })
制作
ツール
- ダミーテキスト生成用のfakerを使用
- メニューのAPIを生成のWordPressプラグイン「WP REST API Menus」をでinstall
GraphQLで取得したdataにLinkを設定
- GatsbyのLink componentを使用
import { graphql, StaticQuery, Link } from 'gatsby';
<StaticQuery query={graphql`
{
allWordpressWpApiMenusMenusItems{
edges{
node{
items{
title
object_slug
}
}
}
}
}
`} render={props => (
<div>
{props.allWordpressWpApiMenusMenusItems.edges[0].node.items.map(item => (
<Link to={`/${item.object_slug}`} key={item.title}>
{item.title}
</Link>
))}
</div>
)} />
styled-componentsを使用
$ yarn add gatsby-plugin-styled-components styled-components babel-plugin-styled-components
-
gatsby-config.js
のpluginに設定
gatsby-config.js
plugins: [
`gatsby-plugin-styled-components`,
],
- globalにstyleを設定する場合は
createGlobalStyle
を使用
import styled, { createGlobalStyle } from 'styled-components';
const GlobalStyles = createGlobalStyle`
@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i&display=swap');
body, html {
font-family: 'Open Sans', san-serif;
margin: 0 !important;
padding: 0 !important;
}
`;
const Layout = ({ children }) => (
<div>
<GlobalStyles />
{children}
</div>
);
- Linkにstyled-componentsを使用する場合は
styled(Link)
を指定
import { graphql, StaticQuery, Link } from 'gatsby';
import styled from 'styled-components';
const ManuItem = styled(Link)`
color: white;
display: block;
padding: 8px 16px;
`;
const MainMenu = () => (
<StaticQuery query={graphql`
{
allWordpressWpApiMenusMenusItems(filter: {
name: {
eq: "Main menu"
}
}){
edges{
node{
items{
title
object_slug
}
}
}
}
}
`} render={props => (
<MainMenuWrapper>
{props.allWordpressWpApiMenusMenusItems.edges[0].node.items.map(item => (
<ManuItem to={`/${item.object_slug}`} key={item.title}>
{item.title}
</ManuItem>
))}
</MainMenuWrapper>
)} />
);
カスタム投稿タイプ「portfolio」を追加
-
gatsby-config.js
のgatsby-source-wordpress
のoptionsに追加 -
gatby-node.js
に「portfolio」のtemplateを追加
gatsby-config.js
includedRoutes: [
"**/categories",
"**/posts",
"**/pages",
"**/media",
"**/tags",
"**/taxonomies",
"**/users",
"**/menus",
"**/portfolio",
]
gatby-node.js
// ==== カスタム投稿タイプ(portfolio) ====
.then(() => {
graphql(
`
{
allWordpressWpPortfolio{
edges{
node{
id
title
slug
excerpt
content
featured_media{
source_url
}
}
}
}
}
`
).then(result => {
if (result.errors) {
console.log(result.errors)
reject(result.errors)
}
const portfolioTemplate = path.resolve("./src/templates/portfolio.js")
_.each(result.data.allWordpressWpPortfolio.edges, edge => {
createPage({
path: `/portfolio/${edge.node.slug}/`,
component: slash(portfolioTemplate),
context: edge.node,
})
})
resolve()
})
})
// ==== カスタム投稿タイプ(portfolio) ====
templateの選択によってレイアウトを変える場合
- WordPressの固定ページにTemplate選択機能を追加、投稿ページに適用
-
gatsby-node.js
にtemplateの種類を追加、その分岐処理
wp-content/themes/wp-gatsby-js-theme-starter-master/portfolio_under_content.php
<?php /* Template name: Portfolio items below content */ ?>
gatsby-node.js
const pageTemplate = path.resolve("./src/templates/page.js")
const portfolioUnderContentTemplate = path.resolve("./src/templates/portfolioUnderContent.js")
_.each(result.data.allWordpressPage.edges, edge => {
createPage({
path: `/${edge.node.slug}/`,
component: slash(edge.node.template === 'portfolio_under_content.php' ? portfolioUnderContentTemplate : pageTemplate),
context: edge.node,
})
})
「Advanced Custom Fields」を扱う
- WordPressの「Advanced Custom Fields」のAPIを取得プラグインで「ACF to REST API」をinstall
-
gatsby-node.js
でGraphQLで取得するdataにacf
を追加
gatsby-node.js
graphql(
`
{
allWordpressWpPortfolio{
edges{
node{
id
title
slug
excerpt
content
featured_media{
source_url
}
acf{
portfolio_url
}
}
}
}
}
`
)
投稿一覧画面にページネーション実装
-
gatsby-node.js
でGraphQLで展開したdataを元にCreatePage
を設定
gatsby-node.js
const posts = result.data.allWordpressPost.edges;
const postsPerPage = 2;
const numberOfPages = Math.ceil(posts.length / postsPerPage);
const blogPostListTemplate = path.resolve('./src/templates/blogPostList.js');
Array.from({ length: numberOfPages }).forEach((page, index) => {
createPage({
component: slash(blogPostListTemplate),
path: index === 0 ? '/blog' : `/blog/${index + 1}`,
context: {
posts: posts.slice(index * postsPerPage, (index * postsPerPage) + postsPerPage),
numberOfPages,
currentPage: index + 1
},
});
});
-
CreatePage
のdataをtemplatesで画面制作 -
pageContext.posts
はcontextで設定したposts
で、GraphQLから取得したdata -
Array.from({ length: pageContext.numberOfPages})
でページャの番号を生成
src/templates/blogPostList.js
const blogPostList = ({ pageContext }) => (
<Layout>
{
pageContext.posts.map(post => (
<div key={post.node.wordpress_id}>
<h3 dangerouslySetInnerHTML={{ __html: post.node.title }} />
<p dangerouslySetInnerHTML={{ __html: post.node.content }} />
</div>
))
}
{
Array.from({ length: pageContext.numberOfPages}).map((page, index) => (
<div key={index}>
<Link to={index === 0 ? '/blog' : `/blog/${index + 1}`}>
{index + 1}
</Link>
</div>
))
}
</Layout>
);
export default blogPostList;
一覧画面のページネーションでCurrentPageかを判定
- style-componentsでpropsを受け取って判定
-
pageContext.currentPage
はgatsby-node.js
のcontextで設定した値
import styled from 'styled-components';
const PageNumberWrapper = styled.div`
border: 1px solid #eee;
background-color: ${props => props.isCurrentPage ? '#eee' : 'white'};
`;
<PageNumberWrapper key={index} isCurrentPage={index + 1 === pageContext.currentPage}>
GraphQLで日付フォーマットを指定
-
formatString
で指定
{
allWordpressPost{
edges{
node{
excerpt
date(formatString: "YYYY/MM/DD hh:mm")
}
}
}
}
その他
WordPressにカスタム投稿タイプ「portfolio」を追加
- WordPressのthemeで管理しているfunctions.phpに記述
functions.php
<?php
add_theme_support( 'custom-logo' );
add_theme_support( 'menus' );
add_theme_support('post-thumbnails');
function create_custom_portfolio_post_type() {
register_post_type('portfolio',
array(
'labels' => array(
'name' => __('portfolio'),
'singular_name' => __('portfolio')
),
'public' => true,
'show_in_admin_bar' => true,
'show_in_rest' => true,
));
add_post_type_support('portfolio', array('thumbnail', 'excerpt'));
}
add_action('init', 'create_custom_portfolio_post_type');