สรุปเรื่อง Web Performance Optimization จาก BKK.js #8
ได้มีโอกาสไปฟังบรรยายในงาน BKK.js #8 จัดที่ WISESIGHT ซึ่งมี Speakers ทั้งหมด 4 ท่านซึ่งแต่ละคนก็พูดหัวข้อดังนี้
- มาในหัวข้อของ “การ Shared Code ระหว่าง Project และ JavaScript Framework”
- Teerasak Kroputaponchai มาในเรื่องของ Micro-frontend กับหัวข้อ “What I Talk About When I Talk About Mirco-Frontends”
- Henry Lim (English Session) มาในเรื่องดาร์กๆ กับ Dark Theme “EVERYTHING You Need To Know About Dark Theme”
- Jecelyn Yeen (English Session) กับประสบการณ์การทำเว็บไซต์ NG-MY 2019 กับหัวข้อ “How We Build NG-MY Website Performance, SEO, CICD”
โดยหัวข้อที่น่าสนใจที่สุดสำหรับผมคือหัวข้อ “How We Build NG-MY Website Performance, SEO, CICD”
ซึ่ง Jecelyn Yeen จะมาเล่าเกี่ยวกับการสร้าง NG-My Website และเทคนิคการ Optimize เว็บของเค้าเองได้น่าสนใจมากและสามารถนำไปใช้ได้จริงและใกล้ตัวผมมากที่สุด ผมเลยสรุปมาดังนี้
แอบบอกว่าจริง ๆ แล้วกะจะเขียนสรุปตั้งแต่ กรกฎาคม แต่ว่าดองไว้จนเกือบถึงปลายปีเลย (ตอนที่เขียนอยู่นี้ก็ปลายเดือนพฤศจิกายน 2562 ละ นานมากกก) (ตอนนี้ก็เดือนมกรา 2563 เข้าให้แล้ว … เอาล่ะไปดูเนื้อหากันเลย)
Section 1: Images
แทบทุกเว็บจะต้องมี Image แทบทั้งสิ้น ทีนี้ image จะต้องมีปัจจัยหลัก ๆ 4 ข้อดังนี้
- Appropriate format
- Appropriate compression
- Appropriate display & size & density
- Load only when necessary (lazy-loaded)
1.1 ไม่ควรใช้ .gif
แต่ควรใช้ .mp4
แทน
ส่วนวิธีการ convert .gif
ไปเป็น .mp4
สามารถใช้ ffmpeg
ได้เช่น
$ ffmpeg -i input.gif output.mp4
ซึ่งผลลัพธ์ออกมาเป็นที่น่าตกใจทีเดียวเพราะลดขนาดภาพจาก
6.8M (.gif)→ 420K (.mp4)
ทีนี้หลายคนอาจจะสงสัยว่าแล้วเว็บเช่น Facebook, Twitter ก็มี .gif
เหมือนกัน
จริง ๆ แล้วผมพึ่งสังเกตจาก talk นี้เลยครับว่าเบื้อหลังมันคือ .mp4
ดี ๆ นี่เอง
1.2 ไฟล์ภาพควรใช้ webp แทน JPEG, PNG
WebP images นั้นมีขนาดเล็กกว่าไฟล์ JPEG, PNG อยู่ 25–35%
โดยส่วนมาก Browser Support เจ้าดัง ๆ ก็ Support กันหมดแล้ว
ซึ่งวิธีการใช้งานจะใช้ tag <picture>
ครอบและ ภาพไว้ด้านในดังด้านล่าง
ถ้า .webp
ไม่ support ใน browser ก็จะโหลดภาพ .jpg
ปกติแทน
อันนี้ขอเสริมนิดหนึ่งว่าเว็บไซต์เช่นพวก ebay ก็นำ webp
ไปใช้แล้วเหมือนกันนะ (จริง ๆ เห็นนานแล้วแหละแต่ตอนนั้นยังไม่รู้ว่าคืออะไร)
1.2 Image Compression
มาอีกหัวข้อหนึ่งที่คิดว่าแทบทุกเว็บจะได้ใช้แน่ ๆ คือหัวข้อนี้ครับ เพราะแทบทุกเว็บไซต์ในปัจจุบันมีรูปภาพอยู่ไม่มากก็น้อย
การ Compression นั้นมีด้วยกัน 2 รูปแบบคือ
- Lossless Compression คือการ compress โดยที่ไม่ลดคุณภาพของไฟล์ลงแต่การ compress ชนิดนี้จะไม่ช่วยลดขนาดของไฟล์ลงมากเท่าไรนัก
- Lossy Compression คือการ compress โดยที่ลดคุณภาพของไฟล์ลงอย่างมากเพื่อแลกกับขนาดที่เล็กลงอย่างมากเช่นกัน
หากใครเคยดาวน์โหลดเพลงมาฟังครับ มันจะมีไฟล์เพลงอยู่สองประเภทก็คือ Lossless กับ Lossy นี่แหละครับ ถ้า Lossless ก็จะเป็นพวกไฟล์นามสกุล .flac
ซึ่งไฟล์จะมีขนาดใหญ่มาก (จากที่เคยดาวน์โหลดประมาณ ~10 MB ต่อเพลง) ซึ่งต่างกับ Lossy ที่จะเป็นพวกนามสกุล .mp3
ซึ่งขนาดไฟล์จะประมาณ (~1–2 MB ต่อเพลง)
พอจะเห็นความต่างของขนาดไหมครับ ถ้าเราเก็บ lossless ไฟล์เดียวอาจจะเก็บ lossy ได้ 5 ไฟล์ ดังนั้นถ้าไม่ซีเรียสเรื่องคุณภาพอะไรมากก็เก็บเป็น lossless จะดีกว่าครับ ประหยัดพื้นที่เครื่องด้วย
การบีบอัดแบบ Lossy นั้นจะช่วยลดขนาดของไฟล์รูปภาพเยอะกว่าก็จริงแต่นั่นก็หมายความว่าเราต้องแลกด้วยคุณภาพที่เสียไปด้วยครับ ตัวอย่างเช่น ภาพดอกไม้ด้านล่าง
สรุปว่า Lossy Compression ถ้าเราตั้งค่า quality ไว้สัก 80–85 เปอร์เซ็นต์จะส่งผลกระทบต่อรูปภาพน้อยที่สุดและจะช่วยลดขนาดของรูปภาพลงไปถึง 30–40%
ซึ่งบางภาพแล้วถ้าเราเอาเป็นพื้นหลังเราก็ไม่จำเป็นต้องใช้ภาพระดับ Full HD หรือโครตจะคมชัดให้เสียดาย bandwidth หรือ data plan ของเรา เมื่อภาพมีขนาดน้อยลงเว็บเราก็จะโหลดไวขึ้นด้วย
เครื่องมือที่จะช่วย compress จะมีรายชื่อดังนี้
Update 12 Jan 2019 พอดีเห็นในไลฟ์ทำเว็บ(นาทีที่ 49.54) JavaScript Bangkok 1.0.0 ของพี่ Thai Pangsakulyanont ได้เครื่องมือมาใหม่ชื่อ ImageOptim และอีกตัวหนึ่งจำไม่ได้มันเป็น command line
โดยเค้านำภาพธรรมดาที่เป็น .png
ที่มีภาพความละเอียดสูงมาย่อให้เป็น .jpg
และใช้ ImageOptim ทำ Lossy Compression ให้มีขนาดเล็กลงไปอีกซึ่งแต่เดิมไฟล์ภาพ 1.3 MB ย่อให้เหลือ 119 KB ซึ่งลดลงเกือบ 11 เท่าเลยทีเดียว!
1.3 Responsive Image
รูปภาพที่เราดาวน์โหลดมานั้นล้วนมี cost ทั้งนั้น ถ้าเราดาวน์โหลดรูปภาพของ Desktop มาแต่เราใช้บน Mobile มันก็สิ้นเปลืองโดยใช่เหตุ โดยรูปภาพบน Desktop อาจใหญ่กว่าบนมือถือประมาณ 2–3 เท่า
รูปภาพที่มีขนาดมากเกินยังทำให้ cost ต่าง ๆ ไม่ว่าจะเป็น
- Transmission cost
- Decoding cost
- Resizing cost
ทางที่จะ Optimize ในส่วนนี้ได้ก็คือ เราต้องมีรูปภาพขนาดต่างกัน 3–5 ขนาดเพื่อแสดงผลได้ดีที่สุดในแต่ละหน้าจอ
Squoosh (resize, compress, format)
เค้าแนะนำเว็บตัวหนึ่งที่มีชื่อว่า Squoosh! โดยเป็น Open Source จาก GoogleChromeLabs เอาไว้ compress พวกภาพโดยสามารถเลือก option ต่าง ๆ ได้
ซึ่งความสามารถมันสามารถทำได้ทั้ง
- Resize
- Compress
- Format
ภาพแมวด้านล่างคือตัวอย่างครับ ผมลากภาพแมวเข้าไปและเค้าจะมี options ให้เลือกด้านข้างเช่นพวก compression methods, width, height และสามารถเลือก
หรือเราสามารถทำ automate ด้วย npm packages เหล่านี้ได้
- imagemin
- sharp
- jimp
สรุปเรื่อง Performant Images จะมี key 4 ตัวด้วยกัน คือ
- Appropriate format
- Appropriate compression
- Appropriate display & size & density
- Load only when necessary (lazy-loaded)
1.4 Lazy-Loading Images
นี่ก็เป็นอีกเทคนิคหนึ่งที่สามารถทำได้เลยเช่นกัน พูดง่าย ๆ คือแทนที่เราจะดาวน์โหลด image มาทั้งหน้า เราก็แค่ดาวน์โหลดภาพแค่บนหน้าจอที่ user เห็นเท่านั้นเอง ลองดูเดโมได้ที่นี่ https://mathiasbynens.be/demo/img-loading-lazy
ทีนี้การทำ lazy-loading ถ้าใน chrome เวอร์ชัน 76 ขึ้นไปจะมี native lazy-loading มาให้เราเพียงแค่เราใส่ loading
<img src="image.png" loading="lazy" alt="…" width="200" height="200">t
<iframe src="https://example.com" loading="lazy"></iframe>
การ lazy-loading สามารถทำได้ทั้ง image, iframe ครับ ทั้งนี้หาก browser ไม่ support native lazy-loading เราสามารถใส่สคริปต์ด้านล่างลงไปแทนได้
1.4 ใช้ Image CDN
CDN (Content Delivery Network) คือกลุ่มของเซิฟเวอร์ที่ตั้งอยู่ที่ต่าง ๆ ที่เอาไว้ให้บริการพวกไฟล์ HTML, javascript files, stylesheets, images และ videos ต่าง ๆ แทนที่ client จะ request ไปหาเครื่อง server ของเราโดยตรง(สีเหลือง) ถ้าเจ้าพวก server ที่อยู่ตามประเทศต่าง ๆ มีไฟล์ที่ client ร้องขออยู่ก็จะตอบกลับไปเลย
จินตนาการว่าเราอยู่ประเทศไทยถ้าเราต้องการดู NAKED DIRECTOR ใน Netflix แทนที่เราจะวิ่งไปเอาไฟล์ video ที่อยู่ที่อเมริกาเราก็วิ่งไปเอาจากเซิฟเวอร์ที่อยู่ใกล้ที่สุดแทนเช่นสิงค์โปรเป็นต้น รูปภาพและพวก static files ก็ทำเช่นเดียวกัน
ในหัวข้อนี้ไม่มีอะไรมากครับ เอาภาพของเราขึ้น CDN แล้วเรียกเอาครับ เช่น เราต้องการภาพ cat.jpg
ที่มีความกว้าง 300 ความยาว 200 และ quality 50
ซึ่งพวก Popular Image CDN ได้แก่ Akami Image Manager, Cloudinary, Imgix, Thumbor (self-hosted)
ส่วนตัวผมใช้ Cloudflare อยู่ครับไม่ต้องติดตั้งอะไรมากให้ตัว DNS ชี้ไปที่ IP ของเค้าและเรา manage พวก Settings ต่าง ๆ ผ่าน web interface เค้าอีกทีหนึ่ง แต่ด้วยความง่ายนี้เองเราต้องเป็นเจ้าของ domain หรือได้รับสิทธิ์จากเจ้าของให้ทำนะครับ
Section 2: Web Fonts
Flash of Invisible Text (FOIT)
อาการนี้จะเกิดก็ต่อเมื่อเราใช้ Font ที่จำเป็นต้องดาวน์โหลดยังไม่เสร็จและตัวอักษรพวกนั้นจะไม่แสดงผลขึ้นมาจนกว่าจะดาวน์โหลด Font เสร็จ
browser จะไม่แสดตัวอักษรจนกว่าจะถึงระยะเวลาที่กำหนด มันเลยทำให้ User Experience ไม่ค่อยดีนัก
โดย browser แต่ละตัวจะมีระยะเวลาดังนี้
- Edge 3 seconds
- Chrome 3 seconds
- Firefox 3 seconds
- Safari ∞ seconds …
อาการนี้ส่งผลกระทบต่อ 2 / 5 mobile sites
ตัวอย่างอาการ FOIT
Flash of Unstyled Text (FOUT)
FOUT คือการที่ browser จะแสดงผลหน้าเว็บเพจด้วย default styles ก่อนที่จะดาวน์โหลด styles ของเว็บเราและ re-render หน้านั้นด้วย styles ที่ดาวน์โหลดมา
ตัวอย่างเช่น
https://upload.wikimedia.org/wikipedia/commons/9/93/Fouc-example.gif
เราสามารถใช้วิธีนี้แก้ปัญหา FOIT ได้ (เพราะว่า FOIT จะไม่แสดง font ยังดาวน์โหลดไม่เสร็จ แต่วิธีนี้จะแก้ปัญหาโดยการให้แสดงผลด้วย default font ของ system ไปก่อนและเมื่อดาวน์โหลดเสร็จก็ค่อยเปลี่ยน font เป็นตัวที่เสร็จ)
สามารถทำได้ดังนี้คือ
@font-face {
.
.
.
font-display: swap;
}
ซึ่งช่วยให้
- 0.5s improvement in “Visually Complete” on 3G
- 20% Improvement in mobile page load times (this & other techniques)
ข่าวดีสำหรับคนทีใช้ Google Fonts เราสามารถเติม &display=swap
ได้เลยเช่น
fonts.googleapis.com/css?family=Kanit&display=swap
Optimizing your font request
ถ้าเราใช้ font ที่มีตัวอักษรแค่ 10 เราก็ไม่ต้องการ font ของทุกตัวอักษร
ถ้าเป็น Google Fonts สามารถทำได้โดยการใส่
fonts.googleapis.com/css?family=Poppins:600&display=swap&text=RemotefrSlids
อ่านต่อได้ที่
Font import
import โดยใช้ @import
จะช่วยลดเรื่องของ render-blocking
Icon Fonts
ถ้าเราใช้แค่ 10 icons เราจะ load อีก 100 icons มาทำไม?
เราสามารถแก้ปัญหานี้โดยใช้ SVG หรือ inline แทนก็ได้
ลองไปดูพวก icomoon, icomoon-cli, fonticons ครับ
Section 3: JavaScript
To be continued.
References
Jeremy Wagner, ‘Replace Animated GIFs with Video’, Web Fundamentals [web blog], https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/replace-animated-gifs-with-video, (accessed 15 Nov 2019).
Image Compression
What Is Image Compression?, KeyCDN [website], 21 November 2018, https://www.keycdn.com/support/what-is-image-compression, (accessed 27 November 2019).
Lossless compression, Wikipedia [website], 13 November 2019, https://en.wikipedia.org/wiki/Lossless_compression, (accessed 27 November 2019).
Lossy compression, Wikipedia [website], 13 November 2019, https://en.wikipedia.org/wiki/Lossless_compression, (accessed 27 November 2019).
Responsive Images
What Img Srcset Does In HTML5: A Quick & Simple Guide Read more [website], https://html.com/attributes/img-srcset/, (accessed 19 Jan 2019).
Lazy-loading Image
Automatically lazy-loading offscreen images & iframes for Lite mode users, Chromium Blog [website], 24 October 2019, https://blog.chromium.org/2019/10/automatically-lazy-loading-offscreen.html, (accessed 8 December 2019).
Native lazy-loading for the web, web.dev [website], 6 August 2019, https://web.dev/native-lazy-loading/, (accessed 8 December 2019).
CDN
What Is a CDN? How Does a CDN work?, Cloudflare [website], https://www.cloudflare.com/learning/cdn/what-is-a-cdn/, (accessed 14 Jan 2019).
Font
How to Fix Flash of Invisible Text (FOIT) in WordPress [website], https://wpspeedmatters.com/fix-foit-font-in-wordpress/, (accessed 19 Jan 2019).
Get Started with the Google Fonts API [website], https://developers.google.com/fonts/docs/getting_started#optimizing_your_font_requests, (accessed 19 Jan 2019)
font-display [website], https://css-tricks.com/almanac/properties/f/font-display/, (accessed 19 Jan 2019)
Responsive Images 101, Part 3: Srcset Display Density [website], https://cloudfour.com/thinks/responsive-images-101-part-3-srcset-display-density/, (accessed 19 Jan 2019)
Flash of unstyled content [website], https://en.wikipedia.org/wiki/Flash_of_unstyled_content, (accessed 24 Feb 2019)
font-display [website], https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display, (accessed 24 Feb 2019)
Slides