# 用 Gatsby.js 透過 GraphQL 讀取 Markdown 架設靜態網站
###### tags: `Gatsby`
#### Gatsby 主要的技術為 React Stack、PWA、GraphQL,是一個靜態網頁產生器,可以類比為 WordPress,但資源度來說還落差很大。但 Gatsby 優點為使用的技術較新,透過 PWA 可以實作離線瀏覽,以及讓網頁速度非常快,並因為是靜態網站,可以直接在開源網站 (GITHUB) 架站。
### 1.安裝 Gatsby.js cli
```shell=
npm install --global gatsby-cli
```
##### 下載版型,這邊使用預設版型,要更多可參考官網[連結](https://www.gatsbyjs.com/starters/?v=2)
```shell=
gatsby new testing https://github.com/gatsbyjs/gatsby-starter-default
```
##### 啟動方式
```shell=
gatsby develop
```
### 2.安裝讀取 Markdown 套件
```shell=
npm install --save gatsby-source-filesystem
npm install --save gatsby-transformer-remark
```
##### 將安裝的套件加入 gatsby-config.js,此檔案專門用來放 plugins 與一些站台的 metaData
```javascript=
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `@gatsbyjs`,
},
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
},
},
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/src/pages`,
name: 'pages',
}
},
`gatsby-transformer-remark`
],
}
```
### 3.建立 Markdown 與模板
#### 在 src>pages 內建立 markdown 資料夾,在此篇建立兩個不同 markdown 頁面,以及在 src>pages 內建立 templates 資料夾,以及兩個 js 檔案。在兩者的關係中,模板可以把它比擬成一個 html 檔案,markdown 則是一個資料庫。
* ##### markdown > index.md
```javascript=
---
path: '/markdown' //連結位置
template: 'src/templates/index.js' //讀取的模板位置
title: 'My page'
---
```
* ##### markdown > index2.md
```javascript=
---
path: '/markdown/index2'
template: 'src/templates/index2.js'
title: 'My test page2'
date: '2020/12/7'
---
```
* ##### templates > index.js
```javascript=
import React from 'react';
import { graphql } from "gatsby"
import Helmet from 'react-helmet';
import SEO from "../components/seo"
//html 部分寫在這
export default function Template({data}) {
const {markdownRemark: page} = data;
return (
<>
<SEO title={page.frontmatter.title} />
<div>
<h1>{page.frontmatter.title}</h1>
</div>
</>
)
}
export const pageQuery = graphql`
//依據我的路徑,去拿取不同 markdown 的資料
query($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
path
title
}
}
}
`
```
* ##### templates > index2.js
```javascript=
import React from 'react';
import { graphql } from "gatsby"
import Helmet from 'react-helmet';
import SEO from "../components/seo"
export default function Template({data}) {
const {markdownRemark: page} = data;
return (
<>
<SEO title={page.frontmatter.title} />
<div>
<h1>{page.frontmatter.title}</h1>
<h1>{page.frontmatter.date}</h1>
</div>
</>
)
}
export const pageQuery = graphql`
query($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
path
title
date
}
}
}
`
```
### 4.自動在首頁生成 Markdown 連結
#### 尋找 gatsby-node.js 這隻檔案,若沒有可以自行創建,但名稱必須相同,這隻用來撰寫創建頁面,將各頁面的初始化都先創建。
* ##### gatsby-node.js
```javascript=
const path = require('path');
exports.createPages = ({boundActionCreators, graphql}) => {
const {createPage} = boundActionCreators;
//這邊只需取得路徑與模板做初始化動作
return graphql(`{
allMarkdownRemark {
edges {
node {
html
id
frontmatter {
path
template
}
}
}
}
}`)
.then(res => {
if (res.errors) {
return Promise.reject(res.errors);
}
//將所有Markdown頁面都創建
res.data.allMarkdownRemark.edges.forEach(({node}) => {
createPage({
path: node.frontmatter.path,
component: path.resolve(node.frontmatter.template)
})
})
})
}
```
#### 到首頁 (src>pages>index.js) 撰寫入口連結
```javascript=
import React from "react"
import { Link } from "gatsby"
import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"
const IndexPage = ({data}) => (
<Layout>
<SEO title="Home" />
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<h2>Index</h2>
{data.allMarkdownRemark.edges.map(page => (
<Link
key={page.node.id}
to={page.node.frontmatter.path}>
{page.node.frontmatter.title}
</Link>
))}
</Layout>
)
export const pageQuery = graphql`
query IndexQuery {
allMarkdownRemark(limit: 10) {
edges {
node {
id
frontmatter {
title
path
}
}
}
}
}
`
export default IndexPage
```
### 5.優化:將專案內原只能讀取單一圖片改成動態讀取圖片
#### 調整 components > image.js 的 graphQL 寫法,將所有圖片都載出,再做filter。
* ##### image.js
```javascript=
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"
const Image = ({fileName}) => {
const data = useStaticQuery(graphql`
query {
//sourceInstanceName是資料夾名稱
allFile(filter: {extension: {regex: "/(jpg)|(jpeg)|(png)/"}, sourceInstanceName: {eq: "images"}}) {
edges {
node {
childImageSharp {
fluid(maxWidth: 300, quality: 50) {
originalName
...GatsbyImageSharpFluid
}
}
}
}
}
}
`)
let image=data.allFile.edges.filter(x=>x.node.childImageSharp.fluid.originalName===fileName).map(x=>x.node.childImageSharp.fluid)
if (!image?.[0]) {
return <div>Picture not found</div>
}
return <Img fluid={image?.[0]} />
}
export default Image
```
#### 讀取圖片範例
```javascript=
<Image fileName="gatsby-astronaut.png"/>
```