บันทึกการทำบล็อคด้วย Gatsby บนเว็บ Devahoy

บันทึกการทำบล็อคด้วย Gatsby บนเว็บ Devahoy


หลังจากบทความก่อนหน้านี้ 3 เดือนที่แล้ว ผมเขียนบอกไว้ว่า Migrate จาก Middleman มา Gatsby แล้ว ซึ่งต่อจากนั้น ก็ไม่ได้มีเวลาอัพเดทเลย แถมปล่อยให้ link ที่เราเปลี่ยน permalink เน่าตาย ลืมเปลี่ยนในบทความด้วย สุดท้าย google index หายเลย เพิ่งจะเริ่มมีเวลากลับมาแก้ มาเปลี่ยน ก็ช่วง 2-3 สัปดาห์ที่ผ่านมา ก็เลยได้ redesign ต่อหลังจาก migrate แล้วก็ปรับโค๊ด ปรับ Performance

บทความนี้จะเน้นไปที่ภาพรวมนะครับ ส่วนเชิงลึกเช่น Gatsby, GraphQL หรืออะไรพวกนี้ เดี๋ยวค่อยเขียนบทความอีกทีละกัน

วันนี้ก็เลยถือโอกาส เอาบันทึกการทำบล็อค Gatsby มาเขียนเป็นบทความซะเลย หวังว่าจะเป็น Use case หรือเป็นตัวอย่าง เป็นแนวทางให้ใครหลายๆคนนะครับ หน้า Showcase เว็บที่ใช้ Gatsby ครับ

Language และ Tools ที่ผมใช้ในเว็บบล็อคก็คือ

  • Gatsby : แน่นอนแหละ เป็น core ทำให้เกิดบล็อคแห่งนี้ได้ หลักๆคือเอาไว้ development แล้วจากนั้นก็ build เป็น static file.
  • React : แน่นอน Gatsby นั้นมันก็คือ React นั่นเอง
  • GraphQL : การใช้ Gatsby นั้นก็จำเป็นต้องรู้ GraphQL แต่ไม่ต้องกังวล ไม่เคยเขียน GraphQL มาก่อน ก็สามารถเขียน Gatsby ได้ ดูตัวอย่าง หรือลอง debug ลอง query ผ่าน explorer ก็จะเข้าใจครับ
  • Github Pages : ผมโอสไว้ที่ Github Pages ครับ (จริงๆ ส่วนใหญ่จะใช้ Netlify เป็น Hosting ซึ่งก็สะดวกดีครับ)
  • Cloudflare : เป็น CDN เอาไว้ช่วย caching เอาไว้กันเว็บโดนยิง เว็บล่มอะไรพวกนี้
  • Semaphore CI : เป็น CI/CD ที่ผมเลือกใช้ครับ เนื่องจากว่าผมติดใจเรื่องความไวในการรัน การเทส ไวกว่าทุกเจ้า ก็เลยใช้มาเรื่อยๆ แล้วก็ตอนนี้มีฟรี $20 ทุกเดือน ก็เลยใช้ 😂

ซึ่งจะเห็นว่าผมใช้ Github Pages + Cloudflare โดยปกติจะเห็นหลายๆคนเลือกใช้ Gatsby + Netlify ด้วยความง่าย จริงๆเว็บลูกค้า หรือเว็บทำเล่นๆผมก็ใช้ Netlify ครับ โดยที่ Netlify มี DNS, HTTPS และเป็น CDN ได้ในตัว เรียกได้ว่าใช้ Netlify ก็ไม่จำเป็นต้องใช้ Cloudflare ก็ได้ แต่ผมลอง Netlify กับบล็อคนี้แล้วไม่ไหวครับ ตอน build แล้ว timeout เนื่องจาก image กับ query เยอะไปหน่อย

Getting stared with Gatsby

เริ่มแรกก็ไม่รอช้า ผมใช้ Gatsby Starter ที่มีให้แล้ว และก็เว็บผมเป็นบล็อค ผมก็เลยเลือกใช้ gatsby starter blog ครับ ติดตั้ง Gatsby CLI แบบ global

npm install -g gatsby-cli

ทีนี้ก็สร้างโปรเจ็คจาก gatsby starter blog template

gatsby new devahoy-blog https://github.com/gatsbyjs/gatsby-starter-blog

cd devahoy-blog
npm start
# yarn start

ทีนี้ข้อดีของ Starter blog คืออะไร?

  • มีโครงสร้าง project มาให้เรียบร้อยแล้ว สามารถทำเว็บต่อยอดได้เลย จะเห็นว่า เว็บของ Dan ก็ fork มาเหมือนกัน
  • ไฟล์ src/seo.js ทำโครงไว้ให้แล้ว โดยใช้ react-helmet ข้อดีคือเราสามารถ custom meta tag ของเว็บได้ ช่วยเพิ่ม SEO รวมถึง Open graph facebook, twitter ได้
  • มี Plugin ลงไว้ให้ครบ เช่น plugin ที่เอาไว้ tranform ไฟล์ markdown ที่สามารถ query ผ่าน GraphQL ได้เลย
  • มีตัวอย่างการ Query หน้า pages และใช้ StaticQuery ใครไม่คุ้นชิน GraphQL ก็ดูตัวอย่างประกอบ หรือเรียนรู้ไม่ยากมากครับ
  • ตัว starter จริงๆมี typography เป็น library ที่ดีมาก ปรับ spacing, theming typo ของเว็บเราได้ แต่ว่าพอดีผมใช้ css โครงเก่า ก็เลยเอาออกไป

ต่อมา ก็ได้เวลา Develop

  • ผมแยกไฟล์ stylesheet และ compile css ด้วย gatsby-plugin-scss โดยใช้ skeleton base แบบเดิม
  • หน้า static page ทั่วๆไป จะเป็น .js ธรรมดา อยู่ที่ src/pages/**
  • ส่วน blog บทความ จะถูกแยกไว้ที่โฟลเดอร์ content ตรงนี้ตั้งค่าได้ที่ไฟล์ gatsby-config.js โดยใช้ plugin gatsby-source-filesystem
  • และ blog เขียนด้วย Markdown ทั้งหมด โดยใช้ plugin gatsby-transformer-remark รวมถึงรูปภาพใน markdown จะถูก optimize ให้แล้ว ด้วย plugin gatsby-remark-images

ทีนี้เรื่องรูปจะมีความยุ่งยากนิดนึง (ถ้าใน Markdown ไม่มีปัญหา เพราะมี plugin ช่วย) แต่ถ้าเป็น component หรือ page การโหลด Image อาจจะยุ่งยากนิดนึง คือ

  1. ต้อง Query รูปก่อน เช่น
export const myQuery = graphql`
  query {
    avatar: file(absolutePath: { regex: "/chai.png/" }) {
      childImageSharp {
        fluid(maxWidth: 500) {
          ...GatsbyImageSharpFluid
        }
      }
    }
  }
}
  1. ใช้ gatsby-image ในการแสดงรูป เพราะว่าทำเผื่อ optimize มัน generate srcset ให้อัตโนมัติ เป็นอีกหนึ่งข้อที่ชื่นชอบใน Gatsby ecosystem
import Image from 'gatsby-image'

const Page = ({ data }) => {
  return (
    <div>
      <Image fluid={data.avatar.childImageSharp.flud} alt="" />
    </div>
  )
}
  • ปกติ หน้า page ในโฟลเดอร์ src/pages/** จะ built-in query ไว้ให้แล้ว คือจะ match กับ GraphQL query ใน component ที่เราสร้างไว้ แล้วก็สามารถเรียกใช้งานผ่าน props.data ได้เลย คล้ายๆกับ Redux connect()(App) ที่สามารถเรียกใช้ dispatch() ได้นั่นเอง
  • แต่ในขณะเดียวกัน ถ้าไม่ใช่ pages เป็น component อื่นๆ เราจำเป็นต้องใช้ StaticQuery มาครอบอีกที
import { StaticQuery } from 'gatsby'

const WithQuery = () => (
  <StaticQuery
    query={myQuery}
    render={data => {
      <MyComponent />
    }}
  />
)

const myQuery = ``
  • ไม่ลืมที่จะใส่ Disqus comment ในเว็บ ทีแรกจะใช้ facebook comment plugin แต่ว่ามันโหลดนานมาก เลยเอาทิ้งไป ถ้าเทียบกันแล้ว Disqus ก็ช้า แต่ยังเร็วกว่า Facebook มาก ข้อดีคือ ใช้ disqus-react ที่มีใน npm อยู่แล้วก็ใช้ได้เลย
  • ไหนๆก็เขียนบล็อค เราก็อยากมี tags มี category ได้บ้าง ก็ได้ gatsby-plugin-categories และ gatsby-plugin-tags ช่วยชีวิตไว้ ก็แค่ระบุ template ใน gatsby-config.js ว่าจะให้ใช้ template ไหน ทีเหลือ plugin จัดการให้ สะดวกเลย

ตั้งค่า Semaphore CI

ผมใช้ Semaphore CI ในการให้มันรัน test, build และ deploy โปรเจ็คของผม เริ่มแรกผมก็ต้องสร้างไฟล์ .semaphore/semaphore.yml ขึ้นมา ข้างในก็มี script บอกไว้ว่า ตอน test จะรันอะไร test เสร็จ สั่งลอง build ถ้า build ผ่าน ก็จะให้ deploy โดยไปเรียกอีกไฟล์ .semaphore/deploy.yml ที่ตั้งค่าไว้ให้มัน deploy ไป github pages โดยใช้ library gh-pages

ซึ่ง Semaphore CI ไม่สามารถ commit ไป git repo แบบ private ได้ ถ้าไม่มี token ฉะนั้นผมจึงเพิ่ม private variable ใน Semaphore ผ่าน sem (Semaphore CLI) และก็ไปกำหนด Notification เพื่อ webhook ไปบอกใน Slack ว่า status เป็นไง เริ่ม build, deploy เสร็จ เป็นต้น

Semaphore Dashbaord

Workflow การเขียนบล็อค

ต่อมาผมพูดถึง Workflow ในการเขียนบล็อคดีกว่า หลังจากช่วง develop เสร็จสิ้นไปแล้ว ทีนี้เราจะอัพเดท เพิ่มบทความ แก้ไขหน้าเว็บยังไง ใช้ Hosting อะไร? Deploy ยังไง? โอเค มาเริ่มทีละ step กันเลย

ผมวางโครงสร้างไว้ดังนี้

1. Checkout new branch from master

ทุกครั้งที่จะ implement หรือเพิ่มบทความ ผมจะ checkout branch ใหม่จาก master

git checkout -b feature/add-new-blog-post-about-gatsby

2. commit, push and open PR

เมื่อเขียนบทความ หรือแก้ไขอะไรเสร็จก็แล้วแต่่ ก็ commit code, push ไป origin (github) จากนั้นก็เปิด Pull Request

git add .
git commit -m '[#11] Closed finish article'
git push origin feature/add-new-blog-post-about-gatsby

3. Review and approve

ไป review ใน Github อีกรอบ เผื่อบางทีอาจจะตาลาย หลงๆลืมๆไป (จริงๆเวลาทำงานเป็นทีม ก็จะมีเพื่อนในทีมช่วยกัน review แต่นี้ก็ต้องมา review เอง approve เองเลย 😂)

Github PR

ถึงแม้ repo นี้ผมจะทำคนเดียว ผมก็ยังต้องไป protect branch ไม่ให้ push ตรงๆมาที่ master ได้ ผ่าน Pull request เท่านั้นถึง merge ได้

4. merge to master

หลังจาก CI ผ่านแล้ว ก็กด rebase and merge เพื่ออัพเดทโค๊ดจาก feature/xx ไปที่ master ทีนี้พอโค๊ดไปที master แล้ว ตัว Semaphore CI ก็จะไป trigger ตามที่เราเขียน script ไว้ เมื่อไม่มีอะไรผิดพลาด มันก็จะไป deploy ที่ branch gh-pages ที่ผมตั้งไว้ เป็น branch ของ Github Pages ใช้เวลาในการ build deploy ประมาณ 3-4 นาที

เป็นอันเรียบร้อย สำหรับ Workflow ในการเขียนบล็อคของผม

ปัญหาที่เจอ

หลังจาก Deploy เว็บไปด้วย Gatsby ก็เริ่มมีปัญหาที่เจออยู่บ้าง ก็พยายามเขียนเล่าไปละกัน บางเรื่องก็ลืมๆครับ ไม่ได้เจอพร้อมกัน เจอแล้วก็แก้

เจอปัญหาเว็บ cache

ปกติเรามักจะมีปัญหาเว็บโหลดช้าเพราะไม่ยอม caching แต่ตัว Gatsby ตั้งค่า plugin มี gatsby-plugin-offline มาช่วยเป็น Service Worker มี PWA มาในตัว ด้วย gatsby-plugin-manifest ทำให้แทบไม่ต้องทำอะไรมาก

แต่ปัญหาคือ ผมดันไปตั้งค่า Cloudflare ในส่วน Page Rules ให้มัน cache everything ทำให้มันไป cache ไฟล์ sw.js ด้วย ซึ่งไฟล์นี้ไม่ควรจะ cached ครับ ฉะนั้น ก็เลยต้องไปกำหนดให้

  • /sw.js นั้น Cache level เป็น By Pass ซะ คือไม่ cache

ทีนี้บล็อคของผม ก็สามารถเห็นบทความใหม่แล้ว ทีแรกไปเผลอ cache ไว้ มันก็จะมองว่าเป็น sw.js ตัวเก่า ก็จะโหลด content เดิมที่ cache ไว้ ไม่ยอมอัพเดท

Google Index

ปัญหาต่อมาคือ ช่วง 3เดือนก่อน ที่รีบย้ายเว็บ และเปลี่ยน Permalink แล้วดันลืมนึกถึงผลกระทบ คือ link พวกนั้นมัน index google อยู่ ฉะนั้นเวลาที่คนค้นหา แล้ว reach เข้าผ่าน google จะเป็น url เก่าๆ ซึ่งพอเข้าเว็บมา มันจะไม่เจอ เพราะเราเปลี่ยน URL แล้ว ทำให้ได้หน้า 404 กลับไปทุกครั้ง

พอหลายๆวันเข้า เว็บผมก็เริ่มร่วง index เริ่มหายไป

Google search console

หน้า Report จาก Google Search Console (Google Webmaster เก่า)

สิ่งที่นึกออกคือ ผมลืมทำไฟล์ sitemap.xml เนื่องจากว่าที่เคย submit ไว้ พอเปลี่ยนเว็บ ลืมทำใหม่ โชคดี gatsby มี plugin ผมใช้ gatsby-plugin-sitemap และ gatsby-plugin-advanced-sitemap ในการทำ sitemap เพื่อไป submit google index ใน Webmaster tool อีกที

จากนั้นเว็บผมก็กลับมา มีคน reach ผ่าน search อีกครั้ง ใช้เวลา 1สัปดาห์มั้ง ถึงจะเริ่ม index url ใหม่ๆ ไปแทนที่ url เดิม (แต่ index เก่าๆ ก็ยังเป็น URL ที่เข้ามาจะเจอ 404 อยู่ดี ตรงนี้ก็คงต้องขึ้นอยู่กับระยะเวลาแล้ว ไม่สามารถทำอะไรได้)

Audit ด้วย Lighthouse

แน่นอน Gatsby ขึ้นชื่อเรื่อง Performance อยู่แล้ว ไม่ว่าจะเป็น PWA, Caching, Service Worker หรือเรื่อง Image Optimization ซึ่งปกติเว็บส่วนใหญ่จะไม่ได้ทำรองรับ แต่ว่า Gatsby มี Plugin ทำให้หมด เรียกได้ว่า ตอนเทส ครั้งแรก 80+ ทุกตัวแต่มีเรื่อง a11y (Accessibility) นั่นแหละที่ตำกว่าเพื่อน

a11y

เลยไปหาอ่านบทความเรื่อง a11y ประกอบ พบว่า เว็บเราห่างเรื่องนี้เยอะเลย สุดท้าย ก็เลยใช้ ESLint ช่วย ให้มัน error มาให้หน่อย ว่าต้องแก้ยังไงบ้าง ด้วย eslint-plugin-jsx-a11y เรียกได้ว่า ช่วยชีวิตไปได้เยอะ ซึ่งส่วนที่จะเจอกันเยอะเป็นพิเศษคือ <img src="" /> ไม่ใส่ alt รวมถึง id ไม่ unique เป็นต้น

Performance

ส่วน Performance มีแนะนำให้ใช้ prefetch, preconnect สำหรับ google font ซึ่งทีแรกเราใช้วิธีการเรียก google font ผ่าน <link rel=""> จากนั้นเลยเปลี่ยนไปใช้ plugin gatsby-plugin-prefetch-google-fonts และก็อีกเรื่องคือ Google Analytics ใน Audit แนะนำให้ใช้ preconnect แต่เนื่องจากไปดูใน Gatsby แล้วรู้สึกว่าใส่มาแล้วนะ ก็เลยลองใส่เองที่ไฟล์ gatsby-ssr.js

import React from 'react'

export const onRenderBody = ({ setHeadComponents }) => {
  setHeadComponents([
    <link
      rel="preconnect dns-prefetch"
      key="preconnect-google-analytics"
      href="https://www.google-analytics.com"
    />
  ])
}

ส่วนนี้งงว่ามันไม่เหมือนกับในตัว core ของ Gatsby ตรงไหน (จนถึงตอนนี้ก็ยังงงๆ เอาเป็นว่า ลองใช้ไปก่อน)

Best Practice

ข้อนี้เห็นส่วนใหญ่จะเป็นเรื่อง Links must have discernible text คือไม่ได้ใส่พวก aria-label ให้กับพวก a href

SEO

อันนี้ข้ามไป เพราะมี react-helmet และ set ค่าไว้แล้ว ทำให้ตรง meta ไม่ต้องกังวล แต่มีบางเรื่องเช่น font เล็ก ขนาด button ไม่พอกดเวลาจอมือถือเป็นต้น

PWA

ข้อนี้ Gatsby ทำมาให้แล้ว ไม่ต้องแก้อะไรเลย 90+ ตั้งแต่แรก

สุดท้าย เทส Audit performance ด้วย Lighthouse ก็ได้ผลลัพธ์ตามรูป (แต่ละครั้งอาจจะได้ผลลัพธ์ไม่เหมือนกัน แต่ตัวเลขไม่ห่างกันมาก)

Audit devahoy

Conclusion

สรุปบทความนี้ก็เป็นการบันทึกการทำบล็อคด้วย Gatsby อาจจะไม่ได้ลงลึกรายละเอียดเท่าไหร่นัก ให้เห็นภาพรวม มุมมองต่างๆ และก็หวังว่าบทความนี้จะเป็นประโยชน์สำหรับใครที่กำลังมองว่าจะใช้ Gatsby ดีหรือเปล่าหนอ บอกได้เลยว่าไม่ผิดหวังครับ

ตอนนี้เรียกได้ว่าผมเป็น fanboy Gatsby ไปแล้วครับ แม้ว่าจะเพิ่งลองใช้ไม่นานก็เถอะ (เสียดายมากได้ยินมานานแล้ว แต่ทำไมไม่รีบใช้ไม่รู้)

และนอกเหนือจาก Workflow หรือ Stack ในบทความผม จริงๆ แล้ว Gatsby + Netlify หรือ Gatsby + Firebase ก็เป็นอีกหนึ่งตัวเลือกที่ไม่เลวทีเดียวนะครับ สุดท้ายก็ไม่มี Process หรือ Tool หรือ Stack อะไรที่มันดีที่สุด อยู่ที่ว่าถนัด ชื่นชอบ หรือตอบโจทย์ เข้ากับโปรเจ็คของเรามั้ย ก็เท่านั้นครับ

Happy Coding

Chai Phonbopit

Chai Phonbopit: Software Engineer แห่งหนึ่ง • ผู้ชายธรรมดาๆ ที่ชื่นชอบ Node.js, JavaScript, React และ Open Source มีงานอดิเรกเป็น Acoustic Guitar และ Football นอกจากเขียนบล็อคที่เว็บนี้แล้ว ก็มีเขียนที่ https://medium.com/@Phonbopit ครับ