ทำลายบิลด์จำนวนมากด้วย Netlify และ Next.js

เผยแพร่แล้ว: 2022-03-10
สรุปโดยย่อ ↬ Static Generation นั้นยอดเยี่ยมสำหรับประสิทธิภาพ — จนกว่าแอพจะใหญ่เกินไปและเวลาในการสร้างจะค่อยๆ หายไป วันนี้เราจะมาดูกันว่าตัวสร้างแบบออนดีมานด์ใหม่ของ Netlify สามารถแก้ไขปัญหานั้นได้อย่างไร นอกจากนี้ เรายังจับคู่กับ Incremental Static Regeneration ที่เพิ่มขึ้นของ Next.js เพื่อประสบการณ์ผู้ใช้และนักพัฒนาที่ดีที่สุด และแน่นอน เปรียบเทียบผลลัพธ์เหล่านั้น!

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

ตัวอย่างเช่น หากแอปของคุณมีหลายหน้า (การดู เส้นทาง) เมื่อสร้างสิ่งประดิษฐ์การปรับใช้ แต่ละเส้นทางเหล่านั้นจะกลายเป็นไฟล์ จากนั้นเมื่อคุณมีถึงจำนวนนับพัน คุณเริ่มสงสัยว่าเมื่อใดที่คุณสามารถปรับใช้โดยไม่จำเป็นต้องวางแผนล่วงหน้า สถานการณ์นี้เป็นเรื่องปกติในแพลตฟอร์มอีคอมเมิร์ซหรือบล็อก ซึ่งเป็นส่วนใหญ่ของเว็บอยู่แล้ว แต่ไม่ใช่ทั้งหมด แม้ว่าเส้นทางจะไม่ใช่ปัญหาคอขวดเพียงอย่างเดียว

ในที่สุดแอพที่ใช้ทรัพยากรมากก็จะถึงจุดเปลี่ยนนี้ในที่สุด เครื่องกำเนิดไฟฟ้าแบบคงที่จำนวนมากดำเนินการเพิ่มประสิทธิภาพสินทรัพย์เพื่อให้มั่นใจว่าผู้ใช้จะได้รับประสบการณ์ที่ดีที่สุด หากไม่มีการปรับให้เหมาะสมของบิลด์ (บิลด์ที่เพิ่มขึ้น การแคช เราจะเข้าถึงได้ในไม่ช้า) ในที่สุดก็จะไม่สามารถจัดการได้เช่นกัน ลองนึกถึงภาพทั้งหมดในเว็บไซต์: ปรับขนาด ลบ และ/หรือสร้างไฟล์ใหม่ซ้ำแล้วซ้ำอีก และเมื่อทำเสร็จแล้ว อย่าลืมว่า Jamstack ให้บริการแอปของเราจากขอบของ Content Delivery Network ดังนั้นเราจึงยังต้องย้ายสิ่งต่าง ๆ จากเซิร์ฟเวอร์ที่คอมไพล์ไปที่ขอบของเครือข่าย

สถาปัตยกรรมบริการทั่วไปของ Jamstack
สถาปัตยกรรมบริการทั่วไปของ Jamstack (ตัวอย่างขนาดใหญ่)

เหนือสิ่งอื่นใด ยังมีข้อเท็จจริงอีกประการหนึ่งคือ ข้อมูลมักจะเป็นไดนามิก หมายความว่าเมื่อเราสร้างแอปและปรับใช้ อาจใช้เวลาไม่กี่วินาที สองสามนาที หรือแม้แต่หนึ่งชั่วโมง ในขณะเดียวกัน โลกยังคงหมุนไป และหากเราดึงข้อมูลจากที่อื่น แอพของเราก็คงจะล้าสมัย รับไม่ได้! สร้างใหม่เพื่ออัปเดต!

สร้างครั้งเดียว อัปเดตเมื่อจำเป็น

การแก้ปัญหา Bulky Builds เป็นเรื่องสำคัญที่สุดสำหรับทุกๆ แพลตฟอร์ม Jamstack เฟรมเวิร์ก หรือบริการมาระยะหนึ่งแล้ว โซลูชันจำนวนมากเกี่ยวข้องกับบิลด์ที่เพิ่มขึ้นเรื่อยๆ ในทางปฏิบัติ นี่หมายความว่าบิลด์จะมีขนาดใหญ่พอ ๆ กับความแตกต่างที่เกิดขึ้นกับการปรับใช้ในปัจจุบัน

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

Next.js มาพร้อมกับ Incremental Static Regeneration ( ISR ) โดยพื้นฐานแล้ว มันคือวิธีการประกาศสำหรับแต่ละเส้นทางว่าเราต้องการให้สร้างใหม่บ่อยเพียงใด ภายใต้ประทุน มันช่วยลดความยุ่งยากในการทำงานมากมายให้กับฝั่งเซิร์ฟเวอร์ เนื่องจากทุกเส้นทาง (ไดนามิกหรือไม่) จะสร้างตัวเองใหม่ตามกรอบเวลาที่กำหนด และมันเข้ากันได้อย่างสมบูรณ์แบบในสัจพจน์ Jamstack ของการทำให้แคชใช้ไม่ได้ในทุกบิลด์ คิดว่าเป็นส่วนหัว max-age แต่สำหรับเส้นทางในแอป Next.js ของคุณ

ในการเริ่มต้นแอปพลิเคชันของคุณ ISR เพียงแค่คุณสมบัติการกำหนดค่าเท่านั้น ในองค์ประกอบเส้นทางของคุณ (ภายในไดเร็กทอรี /pages ) ให้ไปที่เมธอด getStaticProps และเพิ่มคีย์ revalidate ให้กับออบเจกต์ส่งคืน:

 export async function getStaticProps() { const { limit, count, pokemons } = await fetchPokemonList() return { props: { limit, count, pokemons, }, revalidate: 3600 // seconds } }

ตัวอย่างด้านบนจะทำให้แน่ใจว่าหน้าของฉันสร้างใหม่ทุก ๆ ชั่วโมงและดึงโปเกมอนออกมาแสดงมากขึ้น

เรายังคงได้รับบิลด์จำนวนมากเป็นระยะๆ (เมื่อออกการปรับใช้ใหม่) แต่สิ่งนี้ทำให้เราสามารถแยกเนื้อหาออกจากโค้ด โดยการย้ายเนื้อหาไปยัง ระบบจัดการเนื้อหา (CMS) เราสามารถอัปเดตข้อมูลได้ภายในไม่กี่วินาที ไม่ว่าแอปพลิเคชันของเราจะใหญ่แค่ไหน ลาก่อนเว็บฮุคสำหรับการอัปเดตการสะกดผิด!

ตัวสร้างแบบออนดีมานด์

Netlify เพิ่งเปิดตัว On-Demand Builders ซึ่งเป็นแนวทางในการสนับสนุน ISR สำหรับ Next.js แต่ยังทำงานข้ามเฟรมเวิร์กรวมถึง Eleventy และ Nuxt ในเซสชันที่แล้ว เราพบว่า ISR เป็นขั้นตอนที่ดีในการสร้างเวลาให้สั้นลง และกล่าวถึงส่วนสำคัญของกรณีการใช้งาน อย่างไรก็ตาม คำเตือนอยู่ที่นั่น:

  1. บิลด์แบบเต็มเมื่อมีการปรับใช้อย่างต่อเนื่อง
    ขั้นตอนที่เพิ่มขึ้นจะเกิดขึ้น หลังจาก การปรับใช้และสำหรับข้อมูลเท่านั้น ไม่สามารถจัดส่งรหัสเพิ่มได้
  2. การสร้างที่เพิ่มขึ้นเป็นผลจากเวลา
    แคชจะใช้งานไม่ได้ตามเวลา ดังนั้น การสร้างที่ไม่จำเป็นอาจเกิดขึ้น หรือการอัปเดตที่จำเป็นอาจใช้เวลานานขึ้น ทั้งนี้ขึ้นอยู่กับระยะเวลาการตรวจสอบความถูกต้องที่กำหนดในโค้ด

โครงสร้างพื้นฐานการปรับใช้ใหม่ของ Netlify ช่วยให้นักพัฒนาสามารถสร้างตรรกะเพื่อกำหนดว่าส่วนใดของแอปจะสร้างขึ้นจากการปรับใช้และส่วนใดจะถูกเลื่อนออกไป (และจะเลื่อนออกไป อย่างไร )

  • วิกฤต
    ไม่จำเป็นต้องดำเนินการใดๆ ทุกสิ่งที่คุณปรับใช้จะถูกสร้างขึ้นจากการ กด
  • รอการตัดบัญชี
    ส่วนหนึ่งของแอปจะไม่ถูกสร้างขึ้นเมื่อมีการปรับใช้ แต่จะถูกเลื่อนออกไปเพื่อสร้างตามความต้องการเมื่อใดก็ตามที่มีคำขอครั้งแรกเกิดขึ้น จากนั้นจะถูกแคชเป็นทรัพยากรอื่นในประเภทเดียวกัน

การสร้างตัวสร้างแบบออนดีมานด์

ก่อนอื่น เพิ่มแพ็คเกจ netlify/functions เป็น devDependency ในโครงการของคุณ:

 yarn add -D @netlify/functions

เมื่อเสร็จแล้ว จะเหมือนกับการสร้างฟังก์ชัน Netlify ใหม่ หากคุณไม่ได้ตั้งค่าไดเร็กทอรีเฉพาะสำหรับพวกเขา ให้ไปที่ netlify/functions/ และสร้างไฟล์ชื่อใดๆ ให้กับตัวสร้างของคุณ

 import type { Handler } from '@netlify/functions' import { builder } from '@netlify/functions' const myHandler: Handler = async (event, context) => { return { statusCode: 200, body: JSON.stringify({ message: 'Built on-demand! ' }), } } export const handler = builder(myHandler)

ดังที่คุณเห็นจากข้อมูลโค้ดด้านบน ตัวสร้างแบบออนดีมานด์แยกจากฟังก์ชัน Netlify ปกติ เพราะมันรวมตัวจัดการไว้ในเมธอด builder() วิธีนี้เชื่อมต่อฟังก์ชันของเรากับงานสร้าง และนั่นคือทั้งหมดที่คุณต้องมีส่วนหนึ่งของใบสมัครของคุณที่รอการตัดบัญชีสำหรับการสร้างเมื่อจำเป็นเท่านั้น บิลด์ที่เพิ่มขึ้นทีละน้อยตั้งแต่เริ่มต้น!

Next.js บน Netlify

ในการสร้างแอป Next.js บน Netlify มีปลั๊กอินสำคัญ 2 ตัวที่ควรเพิ่มเข้าไปเพื่อให้ได้รับประสบการณ์ที่ดีขึ้นโดยทั่วไป ได้แก่ Netlify Plugin Cache Next.js และ Essential Next-on-Netlify อดีตแคช NextJS ของคุณมีประสิทธิภาพมากขึ้น และคุณต้องเพิ่มมันด้วยตัวเอง ในขณะที่ส่วนหลังทำการปรับเปลี่ยนเล็กน้อยเกี่ยวกับวิธีการสร้างสถาปัตยกรรม Next.js เพื่อให้เหมาะกับ Netlify มากขึ้น และพร้อมใช้งานตามค่าเริ่มต้นสำหรับทุกโครงการใหม่ที่ Netlify สามารถระบุได้คือ ใช้ Next.js

ตัวสร้างแบบออนดีมานด์ด้วย Next.js

สร้างประสิทธิภาพ ปรับใช้ประสิทธิภาพ แคช ประสบการณ์นักพัฒนา ทั้งหมดนี้เป็นหัวข้อที่สำคัญมาก แต่มีจำนวนมาก — และต้องใช้เวลาในการตั้งค่าอย่างเหมาะสม จากนั้นเราก็เข้าสู่การสนทนาแบบเก่าเกี่ยวกับการเน้นที่ประสบการณ์ของนักพัฒนาแทนที่จะเป็นประสบการณ์ผู้ใช้ ซึ่งเป็นเวลาที่สิ่งที่ซ่อนอยู่ใน Backlog ที่ถูกลืม ไม่เชิง.

Netlify ช่วยคุณได้ เพียงไม่กี่ขั้นตอน เราสามารถใช้ประโยชน์จาก Jamstack ได้อย่างเต็มที่ในแอป Next.js ของเรา ถึงเวลาพับแขนเสื้อของเราแล้วประกอบเข้าด้วยกัน

การกำหนดเส้นทางที่แสดงผลล่วงหน้า

หากคุณเคยทำงานกับการสร้างสแตติกใน Next.js มาก่อน คุณอาจเคยได้ยินเมธอด getStaticPaths วิธีนี้มีไว้สำหรับเส้นทางแบบไดนามิก (เทมเพลตหน้าที่จะแสดงผลหน้าที่หลากหลาย) โดยไม่ต้องคำนึงถึงความซับซ้อนของวิธีนี้มากเกินไป สิ่งสำคัญคือต้องสังเกตว่าประเภทการส่งคืนเป็นวัตถุที่มี 2 ปุ่ม เช่นเดียวกับใน Proof-of-Concept ของเรา นี่จะเป็นไฟล์เส้นทางแบบไดนามิก [Pokemon]:

 export async function getStaticPaths() { return { paths: [], fallback: 'blocking', } }
  • paths คือ array ดำเนินการ ทุก เส้นทางที่ตรงกับเส้นทางนี้ ซึ่งจะถูกแสดงผลล่วงหน้า
  • ทาง fallback มี 3 ค่าที่เป็นไปได้: การบล็อก, true หรือ false

ในกรณีของเรา getStaticPaths ของเรากำลังกำหนด:

  1. จะไม่มีการแสดงเส้นทางล่วงหน้า
  2. เมื่อใดก็ตามที่มีการเรียกเส้นทางนี้ เราจะไม่ให้บริการเทมเพลตสำรอง เราจะแสดงหน้าเว็บ ตามต้องการ และให้ผู้ใช้รอ บล็อก แอปไม่ให้ทำอย่างอื่น

เมื่อใช้ตัวสร้างแบบออนดีมานด์ ตรวจสอบให้แน่ใจว่ากลยุทธ์ทางเลือกของคุณบรรลุเป้าหมายของแอป เอกสาร Next.js อย่างเป็นทางการ: เอกสารทางเลือกมีประโยชน์มาก

ก่อนหน้าตัวสร้างแบบออนดีมานด์ getStaticPaths ของเราแตกต่างออกไปเล็กน้อย:

 export async function getStaticPaths() { const { pokemons } = await fetchPkmList() return { paths: pokemons.map(({ name }) => ({ params: { pokemon: name } })), fallback: false, } }

เรากำลังรวบรวมรายชื่อหน้าโปเกมอนทั้งหมดที่เราตั้งใจจะมี แมปวัตถุ pokemon ทั้งหมดให้เป็น string ที่มีชื่อโปเกมอน และส่งต่อวัตถุ { params } ที่ส่งไปยัง getStaticProps ทางเลือกของเราถูกตั้งค่าเป็น " false " เพราะหากเส้นทางไม่ตรงกัน เราต้องการให้ fallback โยนหน้า 404: Not Found

คุณสามารถตรวจสอบทั้งสองเวอร์ชันที่ปรับใช้กับ Netlify:

  • ด้วยตัวสร้างตามความต้องการ: รหัส, ถ่ายทอดสด
  • สร้างสแตติกอย่างสมบูรณ์: รหัส live

โค้ดนี้ยังเป็นโอเพ่นซอร์สบน Github และคุณสามารถปรับใช้โค้ดได้ด้วยตัวเองเพื่อตรวจสอบเวลาสร้าง และด้วยคิวนี้ เราจะเลื่อนไปที่หัวข้อถัดไป

สร้างไทม์

ดังที่ได้กล่าวไว้ข้างต้น การสาธิตก่อนหน้านี้เป็น Proof-of-Concept จริงๆ ไม่มีอะไรดีหรือไม่ดีถ้าเราไม่สามารถวัดได้ สำหรับการศึกษาเล็กๆ น้อยๆ ของเรา ฉันไปที่ PokeAPI และตัดสินใจจับโปเกมอนทั้งหมด

เพื่อวัตถุประสงค์ในการทำซ้ำ ฉันได้จำกัดคำขอของเรา (ถึง 1000 ) สิ่งเหล่านี้ไม่ใช่ ทั้งหมด ภายใน API แต่บังคับใช้จำนวนหน้าจะเท่ากันสำหรับบิลด์ทั้งหมดไม่ว่าสิ่งต่าง ๆ จะได้รับการอัปเดตในเวลาใดก็ตาม

 export const fetchPkmList = async () => { const resp = await fetch(`${API}pokemon?limit=${LIMIT}`) const { count, results, }: { count: number results: { name: string url: string }[] } = await resp.json() return { count, pokemons: results, limit: LIMIT, } }

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

กลยุทธ์ เลขหน้า จำนวนทรัพย์สิน เวลาสร้าง เวลาในการปรับใช้ทั้งหมด
สร้างแบบคงที่เต็มที่ 1002 1005 2 นาที 32 วินาที 4 นาที 15 วินาที
ตัวสร้างแบบออนดีมานด์ 2 0 52 วินาที 52 วินาที

หน้าต่างๆ ในแอพ PokeDex ของเรานั้นค่อนข้างเล็ก เนื้อหารูปภาพนั้นน้อยมาก แต่เวลาในการปรับใช้ที่เพิ่มขึ้นนั้นมีความสำคัญมาก หากแอปมีเส้นทางขนาดกลางถึงจำนวนมาก ก็ควรพิจารณากลยุทธ์ ODB อย่างแน่นอน

ทำให้การปรับใช้ของคุณเร็วขึ้นและเชื่อถือได้มากขึ้น การเข้าถึงประสิทธิภาพจะเกิดขึ้นในคำขอแรกสุดเท่านั้น จากคำขอที่ตามมาและต่อไปหน้าที่แสดงผลจะถูกแคชไว้บน Edge ทำให้ประสิทธิภาพเหมือนกับการสร้างแบบคงที่ทั้งหมดทุกประการ

อนาคต: การแสดงผลแบบต่อเนื่องแบบกระจาย

ในวันเดียวกันนั้นเอง ตัวสร้างแบบออนดีมานด์ได้รับการประกาศและเปิดให้เข้าใช้ก่อนกำหนด Netlify ยังเผยแพร่คำขอความคิดเห็นเกี่ยวกับการแสดงผลแบบต่อเนื่องแบบกระจาย (DPR)

DPR เป็นขั้นตอนต่อไปสำหรับตัวสร้างตามความต้องการ มันใช้ประโยชน์จากบิลด์ที่เร็วขึ้นโดยใช้ขั้นตอนการสร้างแบบอะซิงโครนัส จากนั้นแคชสินทรัพย์จนกว่าจะมีการอัปเดตจริง ไม่มีการสร้างแบบเต็มอีกต่อไปสำหรับเว็บไซต์ 10k หน้า DPR ช่วยให้นักพัฒนาสามารถควบคุมการสร้างและปรับใช้ระบบได้อย่างเต็มที่ผ่านการแคชที่แน่นหนาและการใช้ตัวสร้างแบบออนดีมานด์

ลองนึกภาพสถานการณ์นี้: เว็บไซต์อีคอมเมิร์ซมีหน้าผลิตภัณฑ์ 10,000 หน้า ซึ่งหมายความว่าจะใช้เวลาประมาณ 2 ชั่วโมงในการสร้างแอปพลิเคชันทั้งหมดสำหรับการปรับใช้ เราไม่จำเป็นต้องเถียงว่าเจ็บปวดแค่ไหน

ด้วย DPR เราสามารถตั้งค่าหน้า 500 อันดับแรกเพื่อสร้างในการปรับใช้ทุกครั้ง หน้าการเข้าชมที่หนักที่สุดของเราพร้อม เสมอ สำหรับผู้ใช้ของเรา แต่เราเป็นร้านค้า นั่นคือทุกวินาทีมีค่า ดังนั้นสำหรับหน้าอื่นๆ อีก 9500 หน้า เราสามารถตั้งค่าเบ็ดหลังการสร้างเพื่อทริกเกอร์ตัวสร้างของพวกเขา — ปรับใช้หน้าที่เหลือของเราแบบอะซิงโครนัสและแคชทันที ไม่มีผู้ใช้ได้รับบาดเจ็บ เว็บไซต์ของเราได้รับการอัปเดตด้วยการสร้างที่เร็วที่สุด และทุกอย่างอื่นที่ไม่มีอยู่ในแคชจะถูกเก็บไว้

บทสรุป

แม้ว่าประเด็นการสนทนาหลายประเด็นในบทความนี้จะเป็นแนวความคิดและต้องมีการกำหนดการใช้งานจริง แต่ฉันก็รู้สึกตื่นเต้นกับอนาคตของ Jamstack ความก้าวหน้าที่เราทำในฐานะชุมชนนั้นหมุนรอบประสบการณ์ของผู้ใช้ปลายทาง

คุณคิดอย่างไรกับการแสดงผลต่อเนื่องแบบกระจาย? คุณได้ลองใช้ On-Demand Builders ในแอปพลิเคชันของคุณหรือไม่? แจ้งให้เราทราบเพิ่มเติมในความคิดเห็นหรือโทรหาฉันทาง Twitter ฉันอยากรู้จริงๆ!

อ้างอิง

  • “คู่มือฉบับสมบูรณ์สำหรับการฟื้นฟูแบบคงที่ที่เพิ่มขึ้น (ISR) ด้วย Next.js” ลี โรบินสัน
  • “สร้างได้เร็วขึ้นสำหรับไซต์ขนาดใหญ่บน Netlify ด้วยตัวสร้างแบบออนดีมานด์” Asavari Tayal, Netlify Blog
  • “การแสดงผลต่อเนื่องแบบกระจาย: วิธีการ Jamstack ใหม่สำหรับการสร้างที่เร็วขึ้น” Matt Biilmann, บล็อก Netlify
  • “การแสดงผลถาวรแบบกระจาย (DPR)” Cassidy Williams, GitHub