จาก Parcel สู่ Vite: เรื่องราวสั้น ๆ ของการย้ายโค้ด 100K LOC
เราได้ย้ายโปรเจค frontend ทั้งสามของเราจาก Parcel ไปเป็น Vite, และกระบวนการก็... ราบรื่น
เราได้ย้ายโปรเจค frontend ทั้งสามของเราจาก Parcel ไปเป็น Vite, และกระบวนการก็... ราบรื่น
เรามีสามโปรเจค frontend ที่ Logto: ประสบการณ์การลงชื่อเข้าใช้, Console, และ live preview. โปรเจคเหล่านี้ทั้งหมดใช้ TypeScript, React, และ SASS modules; รวมแล้วมีประมาณ 100K บรรทัดของโค้ด
เราเคยชอบ Parcel สำหรับความง่ายและการตั้งค่าแบบ zero-config. ฉันยังจำได้ถึงวันที่ตกใจกับความง่ายในการตั้งค่าโปรเจคใหม่ด้วย Parcel. คุณเพียงแค่รัน parcel index.html แล้วปัง เหมือนมีเวทมนตร์.
ความง่ายของ Parcel เป็นเหตุผลหลักที่เรายึดติดกับมันมานาน แม้ว่าบางครั้งมันจะอารมณ์ไม่ดี เช่น:
ทำไมเราถึงตัดสินใจย้ายไปที่อื่น?
บางคนถึงกับเปิดการสนทนาเพื่อถามว่า Parcel ตายแล้วหรือไม่. คำตอบอย่างเป็นทางการคือไม่, Parcel ยังไม่ตาย แต่กำลังอยู่ในโหมดเรากำลังทำการ refactor ขนาดใหญ่และไม่มีเวลาสำหรับการปล่อยรอง. สำหรับเรา มันเหมือน "ตายเงียบ": เวอร์ชันล่าสุดที่เราใช้ได้คือเมื่อกว่าปีที่แล้วและเราไม่รู้ว่าเวอร์ชันถัดไปจะปล่อยเมื่อไหร่. มันดูเหมือนมันตายแล้ว, มันแสดงบทบาทเหมือนมันตาย, ดังนั้นมันตายสำหรับเรา.

เรารู้จัก Vite จาก Vitest. เมื่อหลายเดือนก่อน, เราเบื่อกับการรองรับ ESM ของ Jest (ใน testing) และต้องการลองสิ่งใหม่. Vitest ชนะใจของเราด้วยการรองรับ ESM แบบเนทีฟและความเข้ากันได้กับ Jest. มันมีประสบการณ์สำหรับนักพัฒนาที่น่าทึ่ง และขับเคลื่อนโดย Vite.
คุณอาจมีการตั้งค่าที่ต่างกันในโปรเจคของคุณ แต่ปกติแล้วคุณจะพบว่า plugin replacements ในขณะที่ระบบนิเวศของ Vite กำลังบานสะพรั่ง นี่คือการตั้งค่าของเราในขณะที่ย้าย:
เราเริ่มการย้ายโดยการสร้างโปรเจค Vite ใหม่และทดลองเล่นกับมันเพื่อดูว่ามันทำงานอย่างไร. กระบวนการราบรื่นและการย้ายจริงใช้เวลาเพียงไม่กี่วัน.
Vite มีการรองรับอัตโนมัติสำหรับ monorepo, ESM, TypeScript, React, และ SASS. เราเพียงแค่ต้องติดตั้ง plugins และการตั้งค่าที่จำเป็น.
Vite มีการรองรับในตัวสำหรับการใช้นามแฝง เช่นใน tsconfig.json ของเรา:
เราจำเป็นต้องเพิ่มการตั้งค่าเดียวกันใน vite.config.ts ของเรา:
การแทนที่ path ควรเป็น path แบบสมบูรณ์ ในขณะที่มันสัมพันธ์กับรากของโปรเจค. หรือคุณสามารถใช้ plugin vite-tsconfig-paths เพื่ออ่านการใช้นามแฝงจาก tsconfig.json.
แม้ว่า Vite จะมีการรองรับในตัวสำหรับ HMR แต่จำเป็นต้องติดตั้ง plugin เพื่อเปิดใช้งาน React Fast Refresh. เราใช้ plugin @vitejs/plugin-react ที่มีการรองรับฟีเจอร์ของ React เช่น Fast Refresh:
เราใช้ plugin vite-plugin-svgr เพื่อแปลง SVGs เป็น React components. มันง่ายเหมือนการเพิ่ม plugin ลงใน config ของ Vite:
อย่างไรก็ตาม เรายังไม่ได้ระบุเงื่อนไขในการแปลง SVGs เป็น React components ดังนั้นการนำเข้าสินค้าทั้งหมดจะถูกแปลง. plugin มีการตั้งค่าที่ดีกว่า: แปลงเฉพาะ SVGs ที่นำเข้าด้วยนามสกุล .svg?react. เราได้อัพเดทการนำเข้าสินค้าตามนั้น.
แม้ว่า Vite จะมีการรองรับในตัวสำหรับ SASS modules แต่มีสิ่งหนึ่งที่เราต้องใส่ใจ: วิธีการจัดรูปแบบชื่อ class. มันอาจเป็นปัญหาสำหรับผู้ใช้และการทดสอบ integration ของเราหากชื่อ class ไม่จัดเรียงแบบเดียวกัน. การตั้งค่าเพียงบรรทัดเดียวใน vite.config.ts สามารถแก้ปัญหาได้:
อย่างไรก็ตาม Parcel และ Vite มีรูปแบบที่ต่างกันในการนำเข้าไฟล์ SASS:
การใช้ไวยากรณ์ * as นั้นทำงานใน Vite แต่จะทำให้เสียชื่อ class แบบ modularized เมื่อคุณใช้คีย์ในการเข้าถึงวัตถุ styles. เช่น:
เนื่องจาก Vite ใช้ Rollup เป็นสิ่งสำคัญ เราสามารถใช้ plugin อย่างเป็นทางการ @mdx-js/rollup เพื่อรองรับ MDX รวมถึง plugins ของมันด้วย
plugin remarkGfm ใช้ในการรองรับ GitHub Flavored Markdown, และ plugin rehypeMdxCodeProps ใช้ในการผ่าน props ไปยัง code blocks ในไฟล์ MDX เหมือนกับ Docusaurus ทำ.
เราต้องการใช้ Mermaid diagrams ในไฟล์ MDX ของเราเช่นเดียวกับภาษาการเขียนโปรแกรมอื่น ๆ การใช้งานควรเป็นเรื่องง่ายเหมือน code blocks อื่นๆ:
ควรถูกแสดงเป็น:
เนื่องจากแอปของเรารองรับ theme แสงและมืด เราเขียนโค้ดเล็กน้อยให้ Mermaid diagrams ทำงานกับ theme มืด. เราสร้าง React component:
useTheme เป็น custom hook เพื่อดึง theme ปัจจุบันจาก context. ไลบราลี่ mermaid ถูกนำเข้าแบบอะซิงค์เพื่อลดโหลดหน้าเริ่มต้น.
สำหรับ code block ในไฟล์ MDX เรามี component แบบรวมที่สุดในการทำงาน:
สุดท้ายเรากำหนด MDX provider โดย:
นี่ไม่ใช่เรื่องเฉพาะของ Vite มันยังคงเป็นสิ่งที่คุ้มค่าที่จะกล่าวถึงเพราะเราอัพเดทหน้าเพจของเราให้ใช้การโหลดแบบ lazy ในระหว่างการย้ายและไม่มีอะไรพังหลังจากนั้น
React มีฟังก์ชัน React.lazy ในตัวเพื่อโหลด components แบบ lazy. อย่างไรก็ตาม มันอาจก่อให้เกิดปัญหาบางอย่างเมื่อคุณกำลังทำงานอย่างรวดเร็ว. เราสร้างไลบราลี่เล็ก ๆ ชื่อ react-safe-lazy เพื่อแก้ปัญหา. มันเป็นตัวแทนโดยตรงสำหรับ React.lazy และคำอธิบายอย่างละเอียดสามารถพบได้ใน โพสต์บล็อกนี้.
มี plugin ที่เรียบง่ายชื่อ vite-plugin-compression เพื่อผลิต assets ที่ถูกบีบอัด. มันรองรับทั้งการบีบอัดแบบ gzip และ brotli. การตั้งค่าง่ายมาก:
ฟีเจอร์ที่ดีของ Vite (หรือ Rollup ที่อยู่ภายใต้) คือ manual chunks. แม้ว่า React.lazy ใช้สำหรับการโหลด components แบบ lazy, เราสามารถควบคุม chunks ได้มากขึ้นโดยการระบุ manual chunks เพื่อตัดสินใจว่า components หรือ modules ไหนควรถูกจับกลุ่มเข้าด้วยกัน.
ตัวอย่างเช่น เราสามารถใช้ vite-bundle-visualizer เพื่อวิเคราะห์ขนาดของ bundle และ dependencies. จากนั้นเราสามารถเขียนฟังก์ชันที่เหมาะสมในการแยก chunks:
แตกต่างจากการสร้าง production, Vite จะไม่จับกลุ่มซอร์สโค้ดของคุณในโหมด dev (รวมถึง dependencies ที่เชื่อมโยงได้ใน monorepo เดียวกัน) และปฏิบัติต่อทุก module ตามไฟล์. สำหรับเรา, เบราว์เซอร์จะโหลด modules เป็นร้อย ๆ ครั้งแรก, ซึ่งดูบ้าแต่จริง ๆ แล้วมันไม่เป็นปัญหาในกรณีส่วนใหญ่. คุณสามารถดูการสนทนาได้ ที่นี่.
หากมันเป็นปัญหาสำหรับคุณ, ตัวเลือกอื่น ๆ แต่ไม่สมบูรณ์คือการระบุ dependencies ที่เชื่อมโยงในตัวเลือก optimizeDeps ของ vite.config.ts:
นี้จะ "pre-bundle" dependencies ที่เชื่อมโยงและทำให้เซิร์ฟเวอร์ dev เร็วขึ้น. จุดบกพร่องคือ HMR อาจไม่ทำงานตามที่ต้องการสำหรับ dependencies ที่เชื่อมโยง.
นอกจากนี้ เราใช้ proxy ที่ให้บริการไฟล์ static ใน production และส่งคำขอไปยังเซิร์ฟเวอร์ Vitest ในการพัฒนา. เรามีการตั้งค่าพอร์ตบางอย่างเพื่อหลีกเลี่ยงความขัดแย้งและมันก็ตั้งค่าได้ง่ายใน vite.config.ts:
แตกต่างจาก Parcel, Vite ใช้แนวทางที่ทันสมัยในการจัดการตัวแปรสภาพแวดล้อมโดยใช้ import.meta.env. มันจะโหลดไฟล์ .env อัตโนมัติและแทนที่ตัวแปรในโค้ด. อย่างไรก็ตาม, มันต้องการให้ตัวแปรสภาพแวดล้อมทั้งหมดมีคำนำหน้า VITE_ (กำหนดได้).
ในขณะที่เราใช้ Parcel, มันแค่แทนที่ตัวแปร process.env โดยไม่ตรวจสอบคำนำหน้า. ดังนั้นเรามีวิธีแก้ไขโดยใช้ฟิลด์ define เพื่อทำให้การย้ายง่ายขึ้น:
นี้ทำให้เราสามารถเพิ่มคำนำหน้าไปที่ตัวแปรสภาพแวดล้อมอย่างค่อยเป็นค่อยไปและลบฟิลด์ define ออก.
แค่นี้ล่ะ! เราได้ย้ายโปรเจค frontend ทั้งสามจาก Parcel มาเป็น Vite สำเร็จแล้วและหวังว่าเรื่องสั้นนี้จะช่วยคุณในการย้ายของคุณ. นี่คือสิ่งที่การตั้งค่าในตอนท้ายดูเหมือน: