---
# System prepended metadata

title: 用 Gatsby.js 透過 GraphQL 讀取 Markdown 架設靜態網站
tags: [Gatsby]

---

# 用 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"/>
```