บันทึกการการอัพเดท Blog v3 เปลี่ยนไปใช้ Gatsby + MDX และ Theme UI
Ahoy! สวัสดีครับ
วันนี้ผมจะมาเขียนบล็อกแชร์ประสบการณ์ และบันทึกการอัพเดท Blog ของผมกันนะครับ (หลายๆคนคงมีคำถามในใจ เปลี่ยนอีกแล้วหรอ?)
จริงๆ ไม่ได้ปรับแก้อะไรมากมาย ยังคงใช้ Gatsby เหมือนเดิม แต่ก็แอบอยากให้ Next.js เหมือนกัน (เดี๋ยววันหลังมาเขียนเปรียบเทียบ จากตัวผมเองดีกว่า ว่าข้อดีข้อเสียมีอะไรบ้างระหว่าง Gatsby vs Next.js เฉพาะทำ personal blog นะ ไม่รวมไปทำเว็บอื่นๆ )
สิ่งที่ผมต้องการจะทำคือ อยากเปลี่ยนจาก Markdown ธรรมดา เป็นใช้ MDX เพื่ออยากแทรกโค๊ด หรือ Component ลงไปใน Markdown ง่ายๆ ปัจจุบัน มีบาง Component ที่ใช้วิธี ก็อปปี้วาง แต่ละไฟล์ ซึ่งเริ่มไม่ค่อยสะดวก แก้ทีลำบากเลย หลัก เช่นๆ กล่อง Info หรือ Button
พอคิดว่าจะเปลี่ยน MDX แล้ว ไปๆ มาก็ไหนๆ ก็ไหนๆ Refactoring code เก่าๆ ด้วยเลย รื้อ css หมดเลยละกัน (เคยคิดไว้นานแล้ว แต่ทุกครั้งไม่กล้าแตะ เพราะมันเยอะมาก และใช้เวลา)
ก็ได้ความว่า ไหนๆ ใช้ MDX แล้ว ใช้พวก Emotion หรือ Styled Component เลยดีกว่า สรุป ก็เลยมาจบที่ Theme UI ครับ
Getting Started
เริ่มต้น ผมก็วางแผนแล้วว่าจะใช้อะไรบ้าง หลังจากนั่ง Research นั่งดูตัวอย่าง นั่งดู Doc สรุปมาได้แบบนี้
- Gatsby - Static Site Generator ไม่เปลี่ยน (แม้จะอยากลองใช้ Next.js ก็ตาม ไม่อยากรอ build ตอน process images )
- MDX - แน่นอน จุดประสงค์หลักเลย และโชคดี Gatsby ก็มี
gatsby-plugin-mdx
ให้ใช้ - Theme UI - เอามาแทน css เดิม ข้อดีคือเอามาทำ Design System ตัวเองได้ และก็มี
gatsby-plugin-theme-ui
ให้ใช้ - Digital Ocean (Referral Link)- ผมเลือกมาทำ self host เองดีกว่า (เผื่อหัด config server เองด้วย)
- NGINX - เมื่อ host เองแล้ว ตัว web server ก็เลือก NGINX ครับ serve static files ไป แล้วก็ปรับพวก config อะไรเอาเอง
- Cloudflare - เอาไว้เป็น DNS + Proxy และ CDN เพื่อ cache ด้วย ก่อนหน้านี้ใช้ Netlify มี CDN/DNS ในตัว เลยไม่ได้ใช้ Cloudflare
เพราะเนื่องจากว่าปัจจุบันใช้ Gatsby Cloud + Netlify ข้อดีคือสะดวกสบาย แต่ข้อเสียคือ deploy นาน เพราะรอ Gatsby Cloud build เสร็จ แล้ว upload ไฟล์ build มา Netlify ตอนอัพโหลดรอครึ่งชัว่โมง และอีกข้อสำคัญเลย คือ Netlify ให้ bandwidth มาแค่ 100GB ต่อเดือน ซึ่งมันไม่พอ เพราะแชร์หลาย app เอาเป็นว่าไว้ทำแอพเล็กๆ ทำเว็บเล่นๆ ค่อยใช้ Netlify (ซึ่งพวก Vercel / Firebase Hosting / Github Pages / Render / Surge พวกนี้ก็มี Free plan ที่จำกัด bandwidth 100GB ต่อเดือนเหมือนกัน) แต่ Netlify มี CDN และ Caching ผมก็นึกว่า cache ไม่โดย bandwidth แต่นั่งไล่ดู analytics มันโดนหมดเลย ทั้ง request ปกติ ทั้ง bot มีบางเดือน bandwidth เกิน ก็โดนไปฟรีๆ $20 ก็เลยคิดว่า Host เองดีกว่า
เริ่ม Refactoring
ก่อนหน้านี้ ผมใช้ Gatsby และบทความแต่ละบทความ เขียนด้วย Markdown ไม่มี Database
ทีนี้ตัว Gatsby จะมี gatsby-remark
กับ gatsby-remark-transform
เพื่อแปลง Markdown เป็นรูปแบบ GraphQL เมื่อเราอยากได้ข้อมูลอะไร ก็ใช้วิธี Query Graphql มาแสดงนั่นเอง
ส่วน CSS ผมก็ใช้ SCSS คอมไฟล์จาก node-sass
ตัว scss ก็ค่อนข้างเก่ามาก มีทั้ง แบบเวอร์ชั่นเก่าๆ เมื่อก่อนใช้ mixin พวก transition กับ responsive ส่วน base css ผมใช้ skeleton
สรุปก่อน Refactor
ก่อนการ Refactor ผมใช้แบบนี้ครับ
- Gatsby - ทำ Static Site Generator
- Markdown - เขียนบทความ
- scss และ bourbon - ตัว preprocessor css และ mixins เก่ามากๆ ใช้ตั้งแต่ก่อนหน้าที่เว็บผม เขียนด้วย Jekyll ไล่มา Middleman และมา Gatsby ล่าสุด
- skeleton - เป็น css หลักที่ใช้ เรียกว่าเป็น base grid system ที่ใช้ในเว็บเลย แล้วก็ค่อนมา custom scss + bourbon / neat อีกที
Gatsby MDX
พระเอกของเรื่องเลยครับ ทำให้ผมสามารถแทรก React Compnonent ลงไปในไฟล์ Markdown ได้แบบนี้
ให้อารมณ์เหมือนเขียน React แหละครับ แต่เป็น Markdown based แทน
- ซึ่งการเรียก Component แบบ Global โดยไม่ต้อง import ไฟล์ใน mdx ก็ทำได้ โดยใช้ shortcodes ครับ แบบนี้เลย เช่นแก้ไขในไฟล์
Layout.js
Reference : Gatsby MDX Shortcodes
- ทีนี้สิ่งที่เปลี่ยนไปเวลาใช้ MDX คือ
gatsby-plugin-remark
กับgatsby-transform-remark
ไปใช้gatsby-plugin-mdx
แทน ส่วนไฟล์ extension.md
หรือ.markdown
ก็ไม่ต้องเปลี่ยน แค่กำหนด extensions ในgatsby-plugin-mdx
ว่าใช้ extensions อะไรบ้างก็จบแล้ว
Reference : Getting Started with MDX
- อยากให้มัน generate page แบบ auto ด้วย mdx ก็ทำได้ ตามนี้ครับ https://www.gatsbyjs.org/docs/mdx/programmatically-creating-pages/ ยังคงต้องใช้
gatsby-source-system
เพื่อโหลด file system เหมือนเดิม
- query จากปกติใช้
allMarkdownRemark
ก็เปลี่ยนเป็นallMdx
ส่วนบล็อกโพสจากmarkdownRemark
ก็เป็นmdx
- และในส่วน Blog post template ที่เอาไว้ render markdown ก็ใช้ component
MDXRenderer
แทน และ graphql data ก็เปลี่ยนจากhtml
เป็นbody
Reference : Migrating Remark to MDX
Theme UI
Theme UI เรียกว่าเป็น Design Framework ครับ ทำให้เราสามารถทำ UI ให้มีแนวคิดเดียวกัน สร้าง Design System ได้ มองว่ามันคือ ทั้ง Concept และ Framework ในตัว (มีพวก built-in Component มาให้)
ซึ่งจริงๆ แล้ว Theme UI มันก็ยืม API จาก Emotion มา หากใครเขียน Emotion มา ก็เขียน Theme UI ได้ไม่ยาก และตัว Concept หรือ Design Spec ก็จะตาม System UI
สรุปสั้นๆ Theme UI เป็นอะไรดีนะ เรียกว่าเป็น Tool ที่ช่วยให้เราออกแบบและปรับแต่ง UI ผ่าน props รวมถึงการ scale ต่างๆ และ theme color ละกัน
วิธีการใช้งาน ก็ง่ายเลย เพราะมี gatsby plugins ให้ใช้ ไม่ต้องกำหนด <ThemeProvder>
และ custom อะไรมากมาย
จากนั้น ก็แค่เพิ่มลงไปในไฟล์ gatsby-config.js
เมื่อใช้ Theme UI แล้ว ผมก็จะไม่ใช้ remark-prism
ที่เป็นตัว syntax highlighter ใน markdown แต่ใน Theme UI จะมี @theme-ui/prism
มาให้ใช้งานเลย
Reference : gatsby-plugin-theme-ui
ส่วนพวก Theme config ต่างๆ Color ของสีในเว็บ และ UI theme ต่างๆ ผมก็ใช้ Gatsby Shadowing เพื่อ override default ของ theme ui ครับ โดยสร้างโฟลเดอร์ชื่อเดียวกัน ไว้ที่ไฟล์ src/gatsby-plugin-theme-ui
ในนี้ผมก็กำหนด index.js
กำหนด component.js
ได้ครับ
ไฟล์ component.js
ผมก็ใช้ @theme-ui/prism
แบบนี้
ส่วน index.js
ก็เป็น Theme spec เลย ตัวอย่าง Theme Specification
Reference : Shadowing in Gatsby Themes
ไหนๆ พูดถึง Theme UI แล้ว ก็ขอยกตัวอย่าง ให้ดูคร่าวๆ ละกันครับ ในเมื่อมันเป็น Design System ฉะนั้นเราสามารถปรับ UI หรือกำหนด style ให้มันง่ายๆ ผ่าน props ได้เลย เช่น
ตัว h1
มันก็จะได้ css ตามที่เรากำหนดไว้ใน theme เช่น
หรือกำหนด เป็น color ก็ได้ เหมาะสำหรับการกำหนด พวก primary
, text
, secondary
color เช่น
อีกตัวอย่างเช่น เรื่อง spacing หรือ fontSize หากเรากำหนด theme ไว้แบบนี้
เราสามารถกำหนด props ได้แบบนี้เลย เพื่อทำ Responsive
ด้านบน my
(หรือ margin-top
และ margin-bottom
) ถูกกำหนดเป็น array หมายความว่า ตัวแรก คือตอน breakpoint = 40em
หรือบน mobile นั่นเอง ตัวสองคือ breakpoint 52em
ไล่ไปเรื่อยๆ
หากใครสนใจ Theme UI หรือแนวๆ คล้ายๆ กันลองดู links พวกนี้เพิ่มเติมครับ
Optimized Build time
ข้อนี้ก็เป็นปัญหาใหญ่อยู่เหมือนกัน เนื่องจากว่า Gatsby ใช้เวลา build นานมากๆ สาเหตุหลักก็คือ Generate Images ตอน build นั่นเอง ซึ่ง ก่อนหน้านี้ เวลา build ตอนไม่มี .cache
ผมใช้เวลา 25-30 นาที นะ ซึ่งนานมากๆ (Generate รูปประมาณ 18000 รูป)
แต่ถ้ามี .cache
แล้ว มันก็ไม่นานแล้ว เฉลี่ย 1-5 นาที
โดยเงื่อนไข มันจะไม่ build หรือ generate ไฟล์ใหม่ ถ้าเราไม่ไปแก้ไฟล์
gatsby-config.js
- เวลาเพิ่ม ลบ plugins ยังไงก็ต้องแก้Query ต่างๆ
- เช่น หน้า Home หรือหน้า Post เราเปลี่ยนขนาด Image size มันก็ generate ใหม่
ซึ่งช่วง Development แน่นอน เราต้องแก้ไฟล์พวกนี้บ่อยอยู่แล้ว ทำให้ทุกครั้งมันต้อง generate images ใหม่ตลอด มีบางวัน ผมใช้เวลารอ build นานกว่านั่งโคีด เลยตัดสินใจ เอาพวกรูปเก่าๆ สมัยปี 2014-2016 ซึ่งช่วงนั้นอัพไว้เยอะมากๆ และแทบไม่ได้อัพเดทแล้ว ย้ายไป CDN เลยดีกว่า เพราะเราไม่ได้แก้ไข แต่ทำไมต้องให้มัน build / generate ด้วย
- ทีแรกคิดไว้ว่าจะ host S3 / Cloudfront ไปๆ มาๆ ง่ายๆ อัพขึ้น Github public repo แล้ว link ไปเลย ง่าย จบ ฮ่าๆ
- อีกส่วนนึงคือ
gatsby-remark-images
- พยายามลด Quality และ width size ให้เล็กลงมากที่สุด เพราะยิ่ง quality และ size ใหญ่เท่าไหร่ ตอน generate ยิ่งได้ไฟล์เยอะ และยิ่งนานไปอีก - เรื่องของ Traced SVG ของรูป และสังเกตตอน build มันนานมากๆ สุดท้าย เลิกใช้ ไปใช้แบบ blur effect ปกติครับ
- อีกส่วนคือ
gatsby-node.js
พยายามดูว่า มี function ไหนที่มัน query ช้าๆ หรือไม่ได้ทำ async หรือเปล่า query หลายๆ อันก็จับยัดใส่Promise.all
ซะ - ในแต่ละหน้า จะมี Query ซ้ำซ้อน รวมถึง Query image ที่ size ใหญ่กว่าใช้งานจริงบ้าง ก็พยายามลดให้ size เหมาะกับใช้งานจริง และยิ่ง width น้อย ยิ่ง generate images ได้ไวขึ้น
สุดท้าย ตอนนี้ ไฟล์รูปเวลา generate สุดท้าย จริงๆ เหลือแค่ 5000 แล้ว ตอนนี้ ถ้า build ครั้งแรก จาก 25-30 นาที เหลือประมาณ 10 นาทีเอง และยิ่งตอนออัพเดท หรือมี .cache
แล้ว ก็สบายเลย ดีกว่าเดิมเยอะ (จริงๆ ตอน build production ไม่เท่าไหร่) มีแค่ตอนนั่ง Development นี่แหละ บางทีไปแอบแก้พวก config หรือ images ไง ตอนนี้ก็ไม่ต้องรอนานแล้ว เพราะลดรูป ทั้งจำนวน ขนาด ลงไปเยอะ
Deploy Time
หลังจากตัดสินใจ มาทำ Self host เอง ก็ต้องมาเสียเวลา config server เองด้วย nginx ซึ่งสกิลเราก็ basic มาก อาศัย google ช่วย แล้วก็ลองดูโค๊ดชาวบ้านเค้าประกอบ ค่อยๆ ปรับไป
Workflow ก็ทั่วๆไปครับ คือใช้ Github master branch และเราไม่ต้อง manual แค่เขียนบทความ และ push code ก็พอ
- ทุกๆ feature ทุกๆ commit ก็จะต้องเปิด Pull Request
- ใช้ [Semaphore CI] เป็น CI/CD Service ครับ เพื่อรัน ESLint + Prettier ครับ ในแต่ละ branch
- ถ้า CI ผ่านหมด ก็ merge ไป master ผมใช้วิธี rebase ครับ และชอบ rebase มากกว่า merge :)
- master branch หลักจากรัน ESLint + Prettier เสร็จ มันก็จะไป Trigger Host เราด้วยครับ
- ตัว Host ก็ทำการดึง git ล่าสุดมา build ครับ เป็นอันเรียบร้อย flow คร่าวๆ ก็ไม่ได้มีอะไรพิเศษ
แม้ repo นี้ผมจะทำคนเดียว แต่ผมก็ยังต้องทำ git workflow แบบทีมครับ เพราะตอนเปิด Pull Request ผมก็ยังนั่ง Review ตัวเองเลย ฮ่าๆ หรือถ้าจะให้ดีก็ใส่พวก Code quality ช่วยเช็ค เช่น Codacy หรือฟรีๆ ก็ Sonarqube ก็ช่วยหลีกเลี่ยงบัคได้เยอะเหมือนกัน
สิ่งสำคัญคือเรื่องของการ Caching เพราะต้องทำให้เว็บเรา cache ได้ด้วย เพื่อเพิ่มความเร็วในการโหลด และต้องไม่ไปกระทบกับไฟล์ sw.js
ซึ่งเป็นไฟล์ Service Worker ใน Gatsby ซึ่ง ถ้าเราไป cache ไฟล์นี้ไว้ เวลา content อัพเดท ผู้อ่าน หรือ Client ก็ไม่ได้อัพเดท อ่านโดน cache ตลอด
สิ่งที่ยากคือ ต้อง cache ร่วมกับ Cloudflare จนทำให้ผมได้ เขียนบทความไปก่อนหน้านี้แล้วคือ
สุดท้าย nginx ผมได้หน้าตาประมาณนี้ จากการทำตามบทความเรื่อง Gatsby Caching
ก็ไม่แน่ใจว่า ทำไมพอใช้ Cache-Control "public, max-age=0, must-revalidate"
แล้ว Cloudflare มัน Override set Header expires เพิ่มตลอดเลย ทำให้ไฟล์ sw.js
ถูก cache บน Browser ตลอด ผมก็เลย set ให้เป็น no-cache
, no-store
, expires off
ไปเลย ใส่ทุก options ฮ่าๆ
Reference : Caching Static Sites
สรุป
บทความนี้ก็เป็นบันทึก ที่ผมใช้เวลานั่งปรับบล็อกตัวเอง อยู่นานเหมือนกัน น่าจะเป็นเดือนเลย ใช้เวลาว่างๆ หลังเลิกงาน บางทีก็เสาร์ อาทิตย์ แล้วแต่จะหาเวลาได้ มาปรับแต่งให้มีหน้าตาใหม่
หากใครสนใจ Theme UI มีตัวอย่างใน Github เยอะเลยครับ theme-ui
สิ่งที่ได้คือ ทำให้ผมได้เรียนรู้สิ่งใหม่ๆ วิธีการใหม่ๆ ได้ลองผิด ลองถูก เพราะการที่เราได้ลงมือทำอะไรจริงๆ มันดีกว่าอ่านหนังสือหรือดู Tutorial แน่นอน เพราะงานจริงๆ มันไม่จบแค่ทฤษฎี หรือแค่ทำตามแล้วจะได้ มันมีทั้ง error หรือสิ่งที่ไม่คาดคิด เราก็ต้องนั่งแก้ปัญหาเฉพาะหน้าไป นั่ง debug นั่งไล่โค๊ด ทุกๆ ปัญหา ก็ทำให้เราได้เรียนรู้นั่นเอง
ก็หวังว่าบทความนี้จะเป็นประโยชน์สำหรับใครที่หลงเข้ามาอ่านนะครับ และก็ไม่รู้ว่า มีคนใช้ Gatsby กันอยู่รึเปล่านะ?
Happy Coding ♥️
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust