การเพิ่มประสิทธิภาพแอปพลิเคชัน Next.js ด้วย Nx
เผยแพร่แล้ว: 2022-03-10ในบทความนี้ เราจะพูดถึงวิธีการเพิ่มประสิทธิภาพและสร้างแอปพลิเคชัน Next.js ที่มีประสิทธิภาพสูงโดยใช้ Nx และคุณลักษณะที่หลากหลาย เราจะพูดถึงวิธีการตั้งค่าเซิร์ฟเวอร์ Nx วิธีเพิ่มปลั๊กอินไปยังเซิร์ฟเวอร์ที่มีอยู่ และแนวคิดของ monorepo ด้วยการแสดงภาพจริง
หากคุณเป็นนักพัฒนาที่ต้องการเพิ่มประสิทธิภาพแอปพลิเคชันและสร้างส่วนประกอบที่นำมาใช้ซ้ำได้ในทุกแอปพลิเคชันอย่างมีประสิทธิภาพ บทความนี้จะแสดงวิธีปรับขนาดแอปพลิเคชันของคุณอย่างรวดเร็ว และวิธีการทำงานกับ Nx ในการปฏิบัติตาม คุณจะต้องมีความรู้พื้นฐานเกี่ยวกับเฟรมเวิร์ก Next.js และ TypeScript
Nx คืออะไร?
Nx เป็นเฟรมเวิร์กการสร้างแบบโอเพนซอร์สที่ช่วยให้คุณออกแบบ ทดสอบ และสร้างในทุกขนาด โดยผสานรวมกับเทคโนโลยีและไลบรารีที่ทันสมัยอย่างราบรื่น ในขณะที่มอบอินเทอร์เฟซบรรทัดคำสั่ง (CLI) ที่มีประสิทธิภาพ การแคช และการจัดการการพึ่งพา Nx นำเสนอเครื่องมือและปลั๊กอิน CLI ขั้นสูงสำหรับกรอบงาน การทดสอบ และเครื่องมือที่ทันสมัย
สำหรับบทความนี้ เราจะเน้นว่า Nx ทำงานร่วมกับแอปพลิเคชัน Next.js อย่างไร Nx มีเครื่องมือมาตรฐานสำหรับการทดสอบและการจัดรูปแบบในแอปพลิเคชัน Next.js ของคุณ เช่น Cypress, Storybook และส่วนประกอบที่มีสไตล์ Nx อำนวยความสะดวก monorepo สำหรับแอปพลิเคชันของคุณ สร้างพื้นที่ทำงานที่สามารถเก็บซอร์สโค้ดและไลบรารีของแอปพลิเคชันต่างๆ ได้ ช่วยให้คุณสามารถแบ่งปันทรัพยากรระหว่างแอปพลิเคชันต่างๆ
ทำไมต้องใช้ Nx?
Nx มอบฟังก์ชันการใช้งานที่เหมาะสมแก่นักพัฒนาตั้งแต่เริ่มต้น ซึ่งรวมถึงต้นแบบสำหรับการทดสอบแอปพลิเคชันของคุณแบบ end-to-end (E2E) ไลบรารีการจัดสไตล์ และโมโนเรโป
ข้อดีหลายประการมาพร้อมกับการใช้ Nx และเราจะพูดถึงบางส่วนในส่วนนี้
- การดำเนินงานตามกราฟ
Nx ใช้การดำเนินการงานตามกราฟแบบกระจายและการแคชการคำนวณเพื่อเพิ่มความเร็วให้กับงาน ระบบจะกำหนดเวลางานและคำสั่งโดยใช้ระบบกราฟเพื่อกำหนดว่าโหนดใด (เช่น แอปพลิเคชัน) ควรดำเนินการแต่ละงาน สิ่งนี้จะจัดการการทำงานของแอปพลิเคชันและปรับเวลาดำเนินการให้เหมาะสมอย่างมีประสิทธิภาพ - การทดสอบ
Nx มีเครื่องมือทดสอบที่กำหนดค่าไว้ล่วงหน้าสำหรับการทดสอบหน่วยและการทดสอบ E2E - เก็บเอาไว้
Nx ยังเก็บกราฟโครงการที่แคชไว้ด้วย ซึ่งช่วยให้สามารถวิเคราะห์เฉพาะไฟล์ที่อัปเดตใหม่เท่านั้น Nx ติดตามการเปลี่ยนแปลงของไฟล์ตั้งแต่การคอมมิตครั้งล่าสุด และให้คุณทดสอบ สร้าง และดำเนินการกับไฟล์เหล่านั้นเท่านั้น ซึ่งช่วยให้สามารถเพิ่มประสิทธิภาพได้อย่างเหมาะสมเมื่อคุณทำงานกับฐานโค้ดขนาดใหญ่ - กราฟการพึ่งพา
กราฟการพึ่งพาแบบภาพช่วยให้คุณตรวจสอบว่าส่วนประกอบโต้ตอบกันอย่างไร - การจัดเก็บเมฆ
Nx ยังมีที่เก็บข้อมูลบนคลาวด์และการรวม GitHub เพื่อให้คุณสามารถแชร์ลิงก์กับสมาชิกในทีมเพื่อตรวจสอบบันทึกของโครงการ - การแชร์รหัส
การสร้างไลบรารีที่ใช้ร่วมกันใหม่สำหรับทุกโครงการอาจต้องเสียภาษีมาก Nx ขจัดความยุ่งยากนี้ ช่วยให้คุณมีสมาธิกับฟังก์ชันหลักของแอปได้ ด้วย Nx คุณสามารถแชร์ไลบรารีและส่วนประกอบข้ามแอปพลิเคชันได้ คุณยังสามารถแชร์รหัสที่ใช้ซ้ำได้ระหว่างแอปพลิเคชันส่วนหน้าและส่วนหลังของคุณ - รองรับ monorepos
Nx มีพื้นที่ทำงานเดียวสำหรับหลายแอปพลิเคชัน ด้วยการตั้งค่านี้ ที่เก็บ GitHub หนึ่งแห่งสามารถจัดเก็บซอร์สโค้ดสำหรับแอปพลิเคชันต่างๆ ภายใต้พื้นที่ทำงานของคุณ
Nx สำหรับห้องสมุดที่เผยแพร่ได้
Nx ช่วยให้คุณสร้างไลบรารีที่เผยแพร่ได้ นี่เป็นสิ่งสำคัญเมื่อคุณมีไลบรารี่ที่คุณจะใช้ภายนอก monorepo ในกรณีที่คุณกำลังพัฒนาส่วนประกอบ UI ขององค์กรด้วยการรวม Nx Storybook ไว้ Nx จะสร้างส่วนประกอบที่เผยแพร่ได้ควบคู่ไปกับเรื่องราวของคุณ ส่วนประกอบที่เผยแพร่ได้สามารถคอมไพล์ส่วนประกอบเหล่านี้เพื่อสร้างบันเดิลไลบรารีที่คุณสามารถปรับใช้กับรีจิสตรีภายนอกได้ คุณจะใช้ตัวเลือก --publishable
เมื่อสร้างไลบรารี ซึ่งแตกต่างจาก --buildable
ซึ่งใช้ในการสร้างไลบรารีที่ใช้ใน monorepo เท่านั้น Nx ไม่ได้ปรับใช้ไลบรารีที่เผยแพร่ได้โดยอัตโนมัติ คุณสามารถเรียกใช้งานบิลด์ผ่านคำสั่งเช่น nx build mylib
(โดยที่ mylib
คือชื่อของไลบรารี) ซึ่งจะสร้างบันเดิลที่ปรับให้เหมาะสมที่สุดในโฟลเดอร์ dist
/ mylib
ที่สามารถนำไปใช้กับรีจิสตรีภายนอกได้
Nx ให้ตัวเลือกแก่คุณในการสร้างพื้นที่ทำงานใหม่ด้วย Next.js เป็นค่าที่ตั้งไว้ล่วงหน้า หรือจะเพิ่ม Next.js ลงในพื้นที่ทำงานที่มีอยู่
ในการสร้างพื้นที่ทำงานใหม่ด้วย Next.js เป็นค่าที่ตั้งล่วงหน้า คุณสามารถใช้คำสั่งต่อไปนี้:
npx create-nx-workspace happynrwl \ --preset=next \ --style=styled-components \ --appName=todo
คำสั่งนี้จะสร้างเวิร์กสเปซ Nx ใหม่ด้วยแอป Next.js ชื่อ "todo" และมี styled-components
ที่มีสไตล์เป็นไลบรารีการจัดรูปแบบ
จากนั้น เราสามารถเพิ่มแอปพลิเคชัน Next.js ลงในเวิร์กสเปซ Nx ที่มีอยู่ด้วยคำสั่งต่อไปนี้:
npx nx g @nrwl/next:app
การสร้างแอปพลิเคชัน Next.js และ Nx
ปลั๊กอิน Nx สำหรับ Next.js มีเครื่องมือและตัวดำเนินการสำหรับการเรียกใช้และเพิ่มประสิทธิภาพแอปพลิเคชัน Next.js ในการเริ่มต้น เราต้องสร้างเวิร์กสเปซ Nx ใหม่โดยมี next
เป็นค่าที่ตั้งไว้ล่วงหน้า:
npx create-nx-workspace happynrwl \ --preset=next \ --style=styled-components \ --appName=todo
บล็อกโค้ดด้านบนจะสร้างพื้นที่ทำงาน Nx ใหม่และแอปพลิเคชัน Next.js เราจะได้รับข้อความแจ้งให้ใช้ Nx Cloud สำหรับบทช่วยสอนนี้ เราจะเลือก “ไม่” จากนั้นรอให้การพึ่งพาของเราติดตั้ง เมื่อเสร็จแล้วเราควรมีแผนผังไฟล์ที่คล้ายกับนี้:
happynrwl ┣ apps ┃ ┣ todo ┃ ┣ todo-e2e ┃ ┗ .gitkeep ┣ libs ┣ node_modules ┣ tools ┣ .editorconfig ┣ .eslintrc.json ┣ .gitignore ┣ .prettierignore ┣ .prettierrc ┣ README.md ┣ babel.config.json ┣ jest.config.js ┣ jest.preset.js ┣ nx.json ┣ package-lock.json ┣ package.json ┣ tsconfig.base.json ┗ workspace.json
ในโฟลเดอร์ apps
เราจะมีแอปพลิเคชัน Next.js “todo” พร้อมการทดสอบ E2E ที่กำหนดค่าไว้ล่วงหน้าสำหรับแอปที่ต้องทำ ทั้งหมดนี้สร้างขึ้นโดยอัตโนมัติด้วยเครื่องมือ Nx CLI อันทรงพลัง
ในการรันแอพของเรา ให้ใช้คำสั่ง npx nx serve todo
เมื่อคุณให้บริการแอปเสร็จแล้ว คุณจะเห็นหน้าจอด้านล่าง:
การสร้าง API
ณ จุดนี้ เราได้ตั้งค่าพื้นที่ทำงาน ต่อไปคือการสร้าง CRUD API ที่เราจะใช้ในแอปพลิเคชัน Next.js ในการดำเนินการนี้ เราจะใช้ Express เพื่อแสดงการสนับสนุน monorepo เราจะสร้างเซิร์ฟเวอร์ของเราเป็นแอปพลิเคชันในพื้นที่ทำงาน ขั้นแรก เราต้องติดตั้ง Express plugin สำหรับ Nx โดยรันคำสั่งนี้:
npm install --save-dev @nrwl/express
เมื่อเสร็จแล้ว เราก็พร้อมที่จะตั้งค่าแอป Express ของเราในพื้นที่ทำงานที่ให้ไว้ ในการสร้างแอป Express ให้รันคำสั่งด้านล่าง:
npx nx g @nrwl/express:application --name=todo-api --frontendProject=todo
คำสั่ง nx g @nrwl/express:application
จะสร้างแอปพลิเคชัน Express ซึ่งเราสามารถส่งผ่านพารามิเตอร์ข้อกำหนดเพิ่มเติม เพื่อระบุชื่อแอปพลิเคชัน ใช้แฟ --name
; เพื่อระบุแอปพลิเคชันส่วนหน้าที่จะใช้แอป Express ให้ส่งชื่อแอปในพื้นที่ทำงานของเราไปที่ --frontendProject
มีตัวเลือกอื่นๆ สองสามตัวสำหรับแอป Express เมื่อเสร็จแล้ว เราจะมีโครงสร้างไฟล์ที่อัปเดตในโฟลเดอร์ apps
โดยเพิ่มโฟลเดอร์ todo-api
เข้าไป
happynrwl ┣ apps ┃ ┣ todo ┃ ┣ todo-api ┃ ┣ todo-e2e ┃ ┗ .gitkeep …
โฟลเดอร์ todo-api
เป็นไฟล์สำเร็จรูปแบบด่วนที่มีไฟล์รายการ main.ts
/** * This is not a production server yet! * This is only minimal back end to get started. */ import * as express from 'express'; import {v4 as uuidV4} from 'uuid'; const app = express(); app.use(express.json()); // used instead of body-parser app.get('/api', (req, res) => { res.send({ message: 'Welcome to todo-api!' }); }); const port = process.env.port || 3333; const server = app.listen(port, () => { console.log(`Listening at http://localhost:${port}/api`); }); server.on('error', console.error);
เราจะสร้างเส้นทางของเราภายในแอพนี้ ในการเริ่มต้น เราจะเริ่มต้นอาร์เรย์ของอ็อบเจ็กต์ที่มีคู่คีย์-ค่าสองคู่ item
และ id
ภายใต้การประกาศแอป
/** * This is not a production server yet! * This is only minimal back end to get started. */ import * as express from 'express'; import {v4 as uuidV4} from 'uuid'; const app = express(); app.use(express.json()); // used instead of body-parser let todoArray: Array<{ item: string; id: string }> = [ { item: 'default todo', id: uuidV4() }, ]; …
ต่อไป เราจะตั้งค่าเส้นทางเพื่อดึงรายการสิ่งที่ต้องทำทั้งหมดภายใต้ app.get()
:
… app.get('/api', (req, res) => { res.status(200).json({ data: todoArray, }); }); …
บล็อกโค้ดด้านบนจะคืนค่าปัจจุบันของ todoArray
ต่อจากนั้น เราจะมีเส้นทางสำหรับการสร้าง อัปเดต และลบรายการสิ่งที่ต้องทำออกจากอาร์เรย์
… app.post('/api', (req, res) => { const item: string = req.body.item; // Increment ID of item based on the ID of the last item in the array. let id: string = uuidV4(); // Add the new object to the array todoArray.push({ item, id }); res.status(200).json({ message: 'item added successfully', }); }); app.patch('/api', (req, res) => { // Value of the updated item const updatedItem: string = req.body.updatedItem; // ID of the position to update const id: string = req.body.id; // Find index of the ID const arrayIndex = todoArray.findIndex((obj) => obj.id === id); // Update item that matches the index todoArray[arrayIndex].item = updatedItem res.status(200).json({ message: 'item updated successfully', }); }); app.delete('/api', (req, res) => { // ID of the position to remove const id: string = req.body.id; // Update array and remove the object that matches the ID todoArray = todoArray.filter((val) => val.id !== id); res.status(200).json({ message: 'item removed successfully', }); }); …
ในการสร้างรายการสิ่งที่ต้องทำใหม่ สิ่งที่เราต้องมีคือค่าของรายการใหม่เป็นสตริง เราจะสร้าง ID โดยการเพิ่ม ID ขององค์ประกอบสุดท้ายในอาร์เรย์บนเซิร์ฟเวอร์ ในการอัปเดตรายการที่มีอยู่ เราจะส่งต่อค่าใหม่สำหรับรายการและ ID ของรายการที่จะอัปเดต บนเซิร์ฟเวอร์ เราจะวนซ้ำแต่ละรายการด้วยวิธี forEach
และอัปเดตรายการในตำแหน่งที่ ID ตรงกับ ID ที่ส่งพร้อมกับคำขอ สุดท้าย หากต้องการลบรายการออกจากอาร์เรย์ เราจะส่ง ID ของรายการเพื่อลบพร้อมกับคำขอ จากนั้น เรากรองผ่านอาร์เรย์ และส่งคืนอาร์เรย์ใหม่ของรายการทั้งหมดที่ไม่ตรงกับ ID ที่ส่งไปพร้อมกับคำขอ โดยกำหนดอาร์เรย์ใหม่ให้กับตัวแปร todoArray
หมายเหตุ: หากคุณดูในโฟลเดอร์แอปพลิเคชัน Next.js คุณควรเห็นไฟล์ proxy.conf.json
พร้อมการกำหนดค่าด้านล่าง:
{ "/api": { "target": "http://localhost:3333", "secure": false } }
สิ่งนี้จะสร้างพร็อกซี่ ซึ่งอนุญาตให้การเรียก API ทั้งหมดไปยังเส้นทางที่ตรงกัน /api
เพื่อกำหนดเป้าหมายเซิร์ฟเวอร์ todo-api
การสร้างหน้า Next.js ด้วย Nx
ในแอปพลิเคชัน Next.js เราจะสร้างหน้าใหม่ หน้า home
และองค์ประกอบรายการ Nx มีเครื่องมือ CLI ให้เราสร้างเพจได้อย่างง่ายดาย:
npx nx g @nrwl/next:page home
เมื่อรันคำสั่งนี้ เราจะได้รับพร้อมท์ให้เลือกไลบรารีสไตล์ที่เราต้องการใช้สำหรับเพจ สำหรับบทความนี้ เราจะเลือก styled-components
โว้ว! เพจของเราถูกสร้างขึ้น ในการสร้างส่วนประกอบ ให้รัน npx nx g @nrwl/next:component todo-item
; สิ่งนี้จะสร้างโฟลเดอร์ component
ที่มีส่วนประกอบ todo-item
ที่ต้องทำ
การใช้ API ในแอปพลิเคชัน Next.js
ในแต่ละรายการสิ่งที่ต้องทำ เราจะมีปุ่มสองปุ่ม เพื่อแก้ไขและลบรายการสิ่งที่ต้องทำ ฟังก์ชันอะซิงโครนัสที่ดำเนินการเหล่านี้จะถูกส่งผ่านเป็นอุปกรณ์ประกอบฉากจากโฮมเพจ
… export interface TodoItemProps { updateItem(id: string, updatedItem: string): Promise<void>; deleteItem(id: string): Promise<void>; fetchItems(): Promise<any>; item: string; id: string; } export const FlexWrapper = styled.div` width: 100%; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #ccc; padding-bottom: 10px; margin-top: 20px; @media all and (max-width: 470px) { flex-direction: column; input { width: 100%; } button { width: 100%; } } `; export function TodoItem(props: TodoItemProps) { const [isEditingItem, setIsEditingItem] = useState<boolean>(false); const [item, setNewItem] = useState<string | null>(null); return ( <FlexWrapper> <Input disabled={!isEditingItem} defaultValue={props.item} isEditing={isEditingItem} onChange={({ target }) => setNewItem(target.value)} /> {!isEditingItem && <Button onClick={() => setIsEditingItem(true)} > Edit </Button>} {isEditingItem && <Button onClick={async () => { await props.updateItem(props.id, item); //fetch updated items await props.fetchItems(); setIsEditingItem(false) }}> Update </Button>} <Button danger onClick={async () => { await props.deleteItem(props.id); //fetch updated items await await props.fetchItems(); }} > Delete </Button> </FlexWrapper> ); }
สำหรับฟังก์ชันการอัปเดต เรามีอินพุตที่ถูกปิดใช้งานเมื่อสถานะ isEditingItem
เป็น false
เมื่อคลิกปุ่ม "แก้ไข" ระบบจะสลับสถานะ isEditingItem
true
และแสดงปุ่ม "อัปเดต" ที่นี่ ส่วนประกอบอินพุตถูกเปิดใช้งาน และผู้ใช้สามารถป้อนค่าใหม่ได้ เมื่อคลิกปุ่ม "อัปเดต" จะเรียกใช้ฟังก์ชัน updateItem
ด้วยพารามิเตอร์ที่ส่งผ่านเข้ามา และสลับ isEditingItem
กลับ false
ในองค์ประกอบ home
เรามีฟังก์ชันอะซิงโครนัสที่ดำเนินการ CRUD
… const [items, setItems] = useState<Array<{ item: string; id: string }>>([]); const [newItem, setNewItem] = useState<string>(''); const fetchItems = async () => { try { const data = await fetch('/api/fetch'); const res = await data.json(); setItems(res.data); } catch (error) { console.log(error); } }; const createItem = async (item: string) => { try { const data = await fetch('/api', { method: 'POST', body: JSON.stringify({ item }), headers: { 'Content-Type': 'application/json', }, }); } catch (error) { console.log(error); } }; const deleteItem = async (id: string) => { try { const data = await fetch('/api', { method: 'DELETE', body: JSON.stringify({ id }), headers: { 'Content-Type': 'application/json', }, }); const res = await data.json(); alert(res.message); } catch (error) { console.log(error); } }; const updateItem = async (id: string, updatedItem: string) => { try { const data = await fetch('/api', { method: 'PATCH', body: JSON.stringify({ id, updatedItem }), headers: { 'Content-Type': 'application/json', }, }); const res = await data.json(); alert(res.message); } catch (error) { console.log(error); } }; useEffect(() => { fetchItems(); }, []); …
ในบล็อกโค้ดด้านบน เรามี fetchItems
ซึ่งส่งคืน todoArray
จากเซิร์ฟเวอร์ จากนั้น เรามีฟังก์ชัน createItem
ซึ่งรับสตริง พารามิเตอร์คือค่าของรายการสิ่งที่ต้องทำใหม่ ฟังก์ชัน updateItem
ใช้พารามิเตอร์สองตัวคือ ID ของรายการที่จะอัปเดตและค่า updatedItem
และฟังก์ชัน deleteItem
จะลบรายการที่ตรงกับ ID ที่ส่งผ่านเข้ามา
ในการแสดงรายการสิ่งที่ต้องทำ เราแมปผ่านสถานะ items
:
… return ( <StyledHome> <h1>Welcome to Home!</h1> <TodoWrapper> {items.length > 0 && items.map((val) => ( <TodoItem key={val.id} item={val.item} id={val.id} deleteItem={deleteItem} updateItem={updateItem} fetchItems={fetchItems} /> ))} </TodoWrapper> <form onSubmit={async(e) => { e.preventDefault(); await createItem(newItem); //Clean up new item setNewItem(''); await fetchItems(); }} > <FlexWrapper> <Input value={newItem} onChange={({ target }) => setNewItem(target.value)} placeholder="Add new item…" /> <Button success type="submit"> Add + </Button> </FlexWrapper> </form> </StyledHome> ); …
เซิร์ฟเวอร์และส่วนหน้าของเราได้รับการตั้งค่าแล้ว เราสามารถให้บริการแอปพลิเคชัน API ได้โดยการเรียกใช้ npx nx serve todo-api
และสำหรับแอปพลิเคชัน Next.js เราเรียกใช้ npx nx serve todo
คลิกปุ่ม "ดำเนินการต่อ" และคุณจะเห็นหน้าที่มีรายการสิ่งที่ต้องทำเริ่มต้นปรากฏขึ้น
ตอนนี้เรามีแอปพลิเคชัน Next.js และ Express ที่ทำงานร่วมกันได้ในพื้นที่ทำงานเดียว
Nx มีเครื่องมือ CLI อื่นที่ช่วยให้เราดูกราฟการพึ่งพาของแอปพลิเคชันของเราในการรันเทอร์มินัล เรียกใช้ npx nx dep-graph
และเราควรจะเห็นหน้าจอคล้ายกับภาพด้านล่าง ซึ่งแสดงถึงกราฟการพึ่งพาของแอปพลิเคชันของเรา
คำสั่ง CLI อื่นๆ สำหรับ Nx
-
nx list
แสดงรายการปลั๊กอิน Nx ที่ติดตั้งอยู่ในปัจจุบัน -
nx migrate latest
อัพเดตแพ็คเกจในpackage.json
เป็นเวอร์ชันล่าสุด -
nx affected
ดำเนินการกับแอปที่ได้รับผลกระทบหรือแก้ไขเท่านั้น -
nx run-many --target serve --projects todo-api,todo
รันคำสั่งเป้าหมายในทุกโปรเจ็กต์ที่แสดง
บทสรุป
จากภาพรวมทั่วไปของ Nx บทความนี้ครอบคลุมถึงข้อเสนอของ Nx และวิธีที่ทำให้การทำงานง่ายขึ้นสำหรับเรา นอกจากนี้เรายังดำเนินการตั้งค่าแอปพลิเคชัน Next.js ในพื้นที่ทำงาน Nx เพิ่มปลั๊กอิน Express ลงในพื้นที่ทำงานที่มีอยู่ และใช้คุณลักษณะ monorepo เพื่อจัดเก็บแอปพลิเคชันมากกว่าหนึ่งรายการในพื้นที่ทำงานของเรา
คุณจะพบซอร์สโค้ดที่สมบูรณ์ในที่เก็บ GitHub สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ Nx โปรดดูเอกสารประกอบหรือเอกสารประกอบ Nx สำหรับ Next.js