ถึงเวลาใช้โหนด 8 แล้วหรือยัง

เผยแพร่แล้ว: 2022-03-11

โหนด 8 ออกแล้ว! อันที่จริงแล้ว Node 8 นั้นออกมานานพอที่จะเห็นการใช้งานจริงในโลกแห่งความเป็นจริง มันมาพร้อมกับเอ็นจิ้น V8 ใหม่ที่รวดเร็วและคุณสมบัติใหม่ รวมถึง async/await, HTTP/2 และ async hooks แต่พร้อมสำหรับโครงการของคุณหรือไม่? มาหาคำตอบกัน!

หมายเหตุจากบรรณาธิการ: คุณน่าจะทราบแล้วว่า Node 10 (ชื่อรหัส Dubnium ) ก็ออกด้วยเช่นกัน เราเลือกที่จะมุ่งเน้นไปที่ Node 8 ( Carbon ) ด้วยเหตุผลสองประการ: (1) Node 10 เพิ่งเข้าสู่ระยะการสนับสนุนระยะยาว (LTS) และ (2) Node 8 ทำเครื่องหมายการทำซ้ำที่สำคัญกว่า Node 10 .

ประสิทธิภาพในโหนด 8 LTS

เราจะเริ่มต้นด้วยการพิจารณาการปรับปรุงประสิทธิภาพและคุณลักษณะใหม่ของรุ่นที่โดดเด่นนี้ การปรับปรุงที่สำคัญอย่างหนึ่งคือในเอ็นจิ้น JavaScript ของโหนด

เอ็นจิ้น JavaScript คือ อะไรกันแน่?

เอ็นจิ้น JavaScript ดำเนินการและปรับแต่งโค้ดให้เหมาะสม อาจเป็นล่ามมาตรฐานหรือคอมไพเลอร์แบบทันเวลา (JIT) ที่คอมไพล์ JavaScript เป็น bytecode เอ็นจิ้น JS ที่ใช้โดย Node.js เป็นคอมไพเลอร์ JIT ทั้งหมด ไม่ใช่ล่าม

เครื่องยนต์ V8

Node.js ใช้เครื่องมือ Chrome V8 JavaScript ของ Google หรือเพียงแค่ V8 ตั้งแต่เริ่มต้น โหนดบางรุ่นใช้เพื่อซิงค์กับ V8 เวอร์ชันใหม่กว่า แต่ระวังอย่าสับสนระหว่าง V8 กับ Node 8 ในขณะที่เราเปรียบเทียบเวอร์ชัน V8 ที่นี่

นี่เป็นเรื่องง่ายที่จะข้ามไป เนื่องจากในบริบทของซอฟต์แวร์ เรามักใช้ “v8” เป็นคำแสลงหรือแม้แต่รูปแบบสั้นที่เป็นทางการสำหรับ “เวอร์ชัน 8” ดังนั้นบางตัวจึงอาจรวม “Node V8” หรือ “Node.js V8” กับ “NodeJS 8” ” แต่เราได้หลีกเลี่ยงสิ่งนี้ตลอดบทความนี้เพื่อช่วยให้ชัดเจน: V8 จะหมายถึงเครื่องยนต์เสมอ ไม่ใช่เวอร์ชันของ Node

V8 ปล่อย 5

โหนด 6 ใช้ V8 รีลีส 5 เป็นเอ็นจิ้น JavaScript (การเปิดตัวโหนด 8 สองสามจุดแรกยังใช้ V8 รีลีส 5 ด้วย แต่พวกเขาใช้จุดปล่อย V8 ที่ใหม่กว่าโหนด 6 ที่ทำ)

คอมไพเลอร์

V8 รีลีส 5 และรุ่นก่อนหน้ามีคอมไพเลอร์สองตัว:

  • Full-codegen เป็นคอมไพเลอร์ JIT ที่ง่ายและรวดเร็ว แต่สร้างรหัสเครื่องที่ช้า
  • เพลาข้อเหวี่ยง เป็นคอมไพเลอร์ JIT ที่ซับซ้อนซึ่งสร้างรหัสเครื่องที่เหมาะสมที่สุด
กระทู้

ลึกลงไป V8 ใช้เธรดมากกว่าหนึ่งประเภท:

  • เธรดหลักดึงโค้ด คอมไพล์แล้วรัน
  • เธรดรองรันโค้ดในขณะที่เธรดหลักกำลังปรับโค้ดให้เหมาะสม
  • เธรดตัวสร้างโปรไฟล์แจ้งรันไทม์เกี่ยวกับวิธีการที่ไม่มีประสิทธิภาพ เพลาข้อเหวี่ยงจึงปรับวิธีการเหล่านี้ให้เหมาะสม
  • เธรดอื่นจัดการการรวบรวมขยะ
กระบวนการรวบรวม

ขั้นแรก คอมไพเลอร์ Full-codegen รันโค้ด JavaScript ขณะที่โค้ดกำลังดำเนินการ เธรดตัวสร้างโปรไฟล์จะรวบรวมข้อมูลเพื่อกำหนดว่าเอ็นจิ้นใดจะปรับให้เหมาะสม ในเธรดอื่น Crankshaft จะปรับวิธีการเหล่านี้ให้เหมาะสม

ปัญหา

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

V8 รีลีส 6

โหนดเวอร์ชันแรกที่ใช้เอ็นจิ้น V8 รีลีส 6 คือโหนด 8.3

ในรุ่น 6 ทีม V8 ได้สร้าง Ignition และ TurboFan เพื่อบรรเทาปัญหาเหล่านี้ การจุดระเบิดและ TurboFan จะแทนที่ Full-codegen และ CrankShaft ตามลำดับ

สถาปัตยกรรมใหม่มีความตรงไปตรงมามากกว่าและใช้หน่วยความจำน้อยลง

การจุดระเบิดจะคอมไพล์โค้ด JavaScript เป็น bytecode แทนรหัสเครื่อง ซึ่งช่วยประหยัดหน่วยความจำได้มาก หลังจากนั้น TurboFan ซึ่งเป็นคอมไพเลอร์ที่ปรับให้เหมาะสมที่สุด จะสร้างรหัสเครื่องที่ปรับให้เหมาะสมจากไบต์โค้ดนี้

การปรับปรุงประสิทธิภาพเฉพาะ

มาดูส่วนที่ประสิทธิภาพใน Node 8.3+ เปลี่ยนไปเมื่อเทียบกับ Node เวอร์ชันเก่า

การสร้างวัตถุ

การสร้างวัตถุจะเร็วกว่าในโหนด 8.3+ ประมาณห้าเท่า

ขนาดฟังก์ชัน

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

ขนาดฟังก์ชันคำนวณอย่างไร?

เพลาข้อเหวี่ยงในเครื่องยนต์ V8 เก่าใช้ "จำนวนอักขระ" เพื่อกำหนดขนาดฟังก์ชัน ช่องว่างและความคิดเห็นในฟังก์ชันช่วยลดโอกาสที่จะถูกปรับให้เหมาะสม ฉันรู้ว่าสิ่งนี้อาจทำให้คุณประหลาดใจ แต่ในตอนนั้น ความคิดเห็นสามารถลดความเร็วลงได้ประมาณ 10%

ใน Node 8.3+ อักขระที่ไม่เกี่ยวข้อง เช่น ช่องว่างและความคิดเห็น จะไม่ส่งผลเสียต่อประสิทธิภาพของฟังก์ชัน ทำไมจะไม่ล่ะ?

เนื่องจาก TurboFan ใหม่ไม่นับอักขระเพื่อกำหนดขนาดฟังก์ชัน แต่จะนับโหนดโครงสร้างไวยากรณ์นามธรรม (AST) แทน ดังนั้นจึงพิจารณาเฉพาะ คำสั่งฟังก์ชันจริง เท่านั้น เมื่อใช้ Node 8.3+ คุณสามารถเพิ่มความคิดเห็นและช่องว่างได้มากเท่าที่คุณต้องการ

Array -ifying อาร์กิวเมนต์

ฟังก์ชันปกติใน JavaScript มีอ็อบเจ็กต์ argument คล้าย Array โดยนัย

Array -like หมายถึงอะไร?

วัตถุ arguments ทำหน้าที่ เหมือน อาร์เรย์ มันมีคุณสมบัติด้าน length แต่ไม่มีวิธีการในตัวของ Array เช่น forEach และ map

นี่คือวิธีการทำงานของวัตถุ arguments :

 function foo() { console.log(arguments[0]); // Expected output: a console.log(arguments[1]); // Expected output: b console.log(arguments[2]); // Expected output: c } foo("a", "b", "c");

ดังนั้นเราจะแปลงวัตถุ arguments เป็นอาร์เรย์ได้อย่างไร? โดยใช้ terse Array.prototype.slice.call(arguments) .

 function test() { const r = Array.prototype.slice.call(arguments); console.log(r.map(num => num * 2)); } test(1, 2, 3); // Expected output: [2, 4, 6]

Array.prototype.slice.call(arguments) ทำให้ประสิทธิภาพลดลงในเวอร์ชันโหนดทั้งหมด ดังนั้น การคัดลอกคีย์ผ่าน for loop จะดีกว่า:

 function test() { const r = []; for (index in arguments) { r.push(arguments[index]); } console.log(r.map(num => num * 2)); } test(1, 2, 3); // Expected output [2, 4, 6]

for loop ค่อนข้างยุ่งยากใช่ไหม เราสามารถใช้ตัวดำเนินการสเปรดได้ แต่ช้าในโหนด 8.2 และลง:

 function test() { const r = [...arguments]; console.log(r.map(num => num * 2)); } test(1, 2, 3); // Expected output [2, 4, 6]

สถานการณ์เปลี่ยนไปในโหนด 8.3+ ตอนนี้สเปรดทำงานเร็วกว่ามาก เร็วกว่า for-loop

การใช้งานบางส่วน (การแกง) และการเข้าเล่ม

Currying เป็นการแยกฟังก์ชันที่รับหลายอาร์กิวเมนต์เป็นชุดของฟังก์ชัน โดยที่แต่ละฟังก์ชันใหม่รับอาร์กิวเมนต์เพียงอาร์กิวเมนต์เดียว

สมมติว่าเรามีฟังก์ชัน add อย่างง่าย รุ่น cured ของฟังก์ชันนี้รับหนึ่งอาร์กิวเมนต์ num1 ส่งคืนฟังก์ชันที่รับอาร์กิวเมนต์อื่น num2 และส่งคืนผลรวมของ num1 และ num2 :

 function add(num1, num2) { return num1 + num2; } add(4, 6); // returns 10 function curriedAdd(num1) { return function(num2) { return num1 + num2; }; } const add5 = curriedAdd(5); add5(3); // returns 8

วิธีการ bind ส่งคืนฟังก์ชัน curried ด้วยไวยากรณ์เทอร์เซอร์

 function add(num1, num2) { return num1 + num2; } const add5 = add.bind(null, 5); add5(3); // returns 8

ดังนั้นการ bind จึงเป็นเรื่องเหลือเชื่อ แต่ใน Node เวอร์ชันเก่าจะช้า ใน Node 8.3+ การ bind จะเร็วกว่ามากและคุณสามารถใช้งานได้โดยไม่ต้องกังวลกับประสิทธิภาพการทำงานใดๆ

การทดลอง

มีการทดลองหลายครั้งเพื่อเปรียบเทียบประสิทธิภาพของโหนด 6 กับโหนด 8 ในระดับสูง โปรดทราบว่าสิ่งเหล่านี้ดำเนินการบน Node 8.0 ดังนั้นจึงไม่รวมการปรับปรุงที่กล่าวถึงข้างต้นที่เป็นเฉพาะกับ Node 8.3+ ด้วยการอัพเกรด V8 รีลีส 6

เวลาในการแสดงผลของเซิร์ฟเวอร์ในโหนด 8 น้อยกว่าในโหนด 6 ถึง 25% ในโครงการขนาดใหญ่ จำนวนอินสแตนซ์ของเซิร์ฟเวอร์อาจลดลงจาก 100 เป็น 75 ซึ่งเป็นเรื่องที่น่าอัศจรรย์ การทดสอบชุดการทดสอบ 500 ชุดในโหนด 8 เร็วขึ้น 10% การสร้าง Webpack เร็วขึ้น 7% โดยทั่วไป ผลลัพธ์แสดงการเพิ่มประสิทธิภาพที่เห็นได้ชัดเจนในโหนด 8

คุณสมบัติของโหนด 8

ความเร็วไม่ได้เป็นเพียงการปรับปรุงอย่างเดียวใน Node 8 แต่ยังมาพร้อมคุณสมบัติใหม่ที่มีประโยชน์หลายอย่าง—บางทีที่สำคัญที่สุดคือ async/await

Async/รอในโหนด 8

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

และในขณะที่สามารถใช้ async/await ใน Node เวอร์ชันก่อนหน้าได้ แต่ต้องใช้ไลบรารีและเครื่องมือภายนอก เช่น การประมวลผลล่วงหน้าเพิ่มเติมผ่าน Babel ตอนนี้มีวางจำหน่ายแล้วตั้งแต่แกะกล่อง

ฉันจะพูดถึงบางกรณีที่ async/await เหนือกว่าสัญญาทั่วไป

เงื่อนไข

ลองนึกภาพว่าคุณกำลังดึงข้อมูลและคุณจะพิจารณาว่าจำเป็นต้องมีการเรียก API ใหม่ โดยพิจารณาจากส่วนของข้อมูล หรือไม่ ดูโค้ดด้านล่างเพื่อดูว่าดำเนินการผ่านแนวทาง "Conventional Promise" ได้อย่างไร

 const request = () => { return getData().then(data => { if (!data.car) { return fetchForCar(data.id).then(carData => { console.log(carData); return carData; }); } else { console.log(data); return data; } }); };

อย่างที่คุณเห็น โค้ดด้านบนดูยุ่งเหยิงอยู่แล้ว จากเงื่อนไขพิเศษเพียงข้อเดียว Async/await เกี่ยวข้องกับการซ้อนน้อยลง:

 const request = async () => { const data = await getData(); if (!data.car) { const carData = await fetchForCar(data); console.log(carData); return carData; } else { console.log(data); return data; } };

การจัดการข้อผิดพลาด

Async/await ให้สิทธิ์คุณในการเข้าถึงเพื่อจัดการทั้งข้อผิดพลาดแบบซิงโครนัสและแบบอะซิงโครนัสในการลอง/จับ สมมติว่าคุณต้องการแยกวิเคราะห์ JSON ที่มาจากการเรียก API แบบอะซิงโครนัส ลอง/จับครั้งเดียวสามารถจัดการทั้งข้อผิดพลาดในการแยกวิเคราะห์และข้อผิดพลาด API

 const request = async () => { try { console.log(await getData()); } catch (err) { console.log(err); } };

ค่ากลาง

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

เมื่อใช้คำสัญญาทั่วไป คุณอาจลงเอยด้วยโค้ดดังนี้:

 const request = () => { return fetchUserData() .then(userData => { return fetchCompanyData(userData); }) .then(companyData => { return fetchRetiringPlan(userData, companyData); }) .then(retiringPlan => { const retiringPlan = retiringPlan; }); };

Async/await ส่องสว่างในกรณีนี้ ซึ่งจำเป็นต้องมีการเรียกแบบอะซิงโครนัสแบบเชน:

 const request = async () => { const userData = await fetchUserData(); const companyData = await fetchCompanyData(userData); const retiringPlan = await fetchRetiringPlan(userData, companyData); };

Async ในแบบคู่ขนาน

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

 function fetchHouseData() { return new Promise(resolve => setTimeout(() => resolve("Mansion"), 1000)); } function fetchCarData() { return new Promise(resolve => setTimeout(() => resolve("Ferrari"), 1000)); } async function action() { const house = await fetchHouseData(); // Wait one second const car = await fetchCarData(); // ...then wait another second. console.log(house, car, " in series"); } action();

วิธีที่ดีกว่าคือการประมวลผลการเรียกแบบอะซิงโครนัสแบบขนาน ตรวจสอบรหัสด้านล่างเพื่อรับทราบวิธีการดำเนินการนี้แบบ async/await

 async function parallel() { houseDataPromise = fetchHouseData(); carDataPromise = fetchCarData(); const house = await houseDataPromise; // Wait one second for both const car = await carDataPromise; console.log(house, car, " in parallel"); } parallel();

การประมวลผลการโทรเหล่านี้แบบขนานทำให้คุณรอเพียงหนึ่งวินาทีสำหรับทั้งสองสาย

ฟังก์ชันไลบรารีหลักใหม่

โหนด 8 ยังนำเสนอฟังก์ชันหลักใหม่บางอย่างอีกด้วย

คัดลอกไฟล์

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

 const fs = require('fs'); const rd = fs.createReadStream('sourceFile.txt'); rd.on('error', err => { console.log(err); }); const wr = fs.createWriteStream('target.txt'); wr.on('error', err => { console.log(err); }); wr.on('close', function(ex) { console.log('File Copied'); }); rd.pipe(wr);

ใน Node 8 fs.copyFile และ fs.copyFileSync เป็นแนวทางใหม่ในการคัดลอกไฟล์โดยมีความยุ่งยากน้อยกว่ามาก

 const fs = require("fs"); fs.copyFile("firstFile.txt", "secondFile.txt", err => { if (err) { console.log(err); } else { console.log("File copied"); } });

สัญญาและโทรกลับ

util.promisify แปลงฟังก์ชันปกติเป็นฟังก์ชัน async โปรดทราบว่าฟังก์ชันที่ป้อนควรเป็นไปตามรูปแบบการเรียกกลับของ Node.js ทั่วไป ควรเรียกกลับเป็นอาร์กิวเมนต์สุดท้าย เช่น (error, payload) => { ... }

 const { promisify } = require('util'); const fs = require('fs'); const readFilePromisified = promisify(fs.readFile); const file_path = process.argv[2]; readFilePromisified(file_path) .then((text) => console.log(text)) .catch((err) => console.log(err));

อย่างที่คุณเห็น util.promisify ได้แปลง fs.readFile เป็นฟังก์ชัน async

ในทางกลับกัน Node.js มาพร้อมกับ util.callbackify util.callbackify ตรงกันข้ามกับ util.promisify : มันแปลงฟังก์ชัน async เป็นฟังก์ชันสไตล์การเรียกกลับของ Node.js

destroy ฟังก์ชันสำหรับการอ่านและเขียนได้

ฟังก์ชัน destroy ในโหนด 8 เป็นวิธีที่บันทึกไว้ในการทำลาย/ปิด/ยกเลิกสตรีมที่อ่านได้หรือเขียนได้:

 const fs = require('fs'); const file = fs.createWriteStream('./big.txt'); file.on('error', errors => { console.log(errors); }); file.write(`New text.\n`); file.destroy(['First Error', 'Second Error']);

โค้ดด้านบนส่งผลให้เกิดการสร้างไฟล์ใหม่ชื่อ big.txt (หากยังไม่มี) โดยมีข้อความ New text. .

ฟังก์ชัน Readable.destroy และ Writeable.destroy ในโหนด 8 ปล่อยเหตุการณ์ close และเหตุการณ์ error ทางเลือก การ destroy ไม่ได้หมายความว่ามีข้อผิดพลาดเกิดขึ้นเสมอไป

ตัวดำเนินการสเปรด

ตัวดำเนินการสเปรด (aka ... ) ทำงานในโหนด 6 แต่เฉพาะกับอาร์เรย์และ iterables อื่น ๆ เท่านั้น:

 const arr1 = [1,2,3,4,5,6] const arr2 = [...arr1, 9] console.log(arr2) // expected output: [1,2,3,4,5,6,9]

ในโหนด 8 ออบเจ็กต์ยังสามารถใช้ตัวดำเนินการกระจาย:

 const userCarData = { type: 'ferrari', color: 'red' }; const userSettingsData = { lastLoggedIn: '12/03/2019', featuresPlan: 'premium' }; const userData = { ...userCarData, name: 'Youssef', ...userSettingsData }; console.log(userData); /* Expected output: { type: 'ferrari', color: 'red', name: 'Youssef', lastLoggedIn: '12/03/2019', featuresPlan: 'premium' } */

คุณลักษณะทดลองในโหนด 8 LTS

คุณลักษณะทดลองไม่เสถียร อาจเลิกใช้งาน และสามารถอัปเดตได้เมื่อเวลาผ่านไป อย่าใช้คุณสมบัติเหล่านี้ใน การผลิต จนกว่าจะเสถียร

ตะขอ Async

Async hooks ติดตามอายุการใช้งานของทรัพยากรแบบอะซิงโครนัสที่สร้างขึ้นภายใน Node ผ่าน API

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

ดูโค้ดด้านล่างได้เลยครับ โปรดสังเกตว่า console.log เป็นฟังก์ชัน async ดังนั้นจึงไม่สามารถใช้ภายใน async hooks ได้ ใช้ fs.writeSync แทน

 const asyncHooks = require('async_hooks'); const fs = require('fs'); const init = (asyncId, type, triggerId) => fs.writeSync(1, `${type} \n`); const asyncHook = asyncHooks.createHook({ init }); asyncHook.enable();

ดูวิดีโอนี้เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ async hooks ในแง่ของคู่มือ Node.js โดยเฉพาะ บทความนี้ช่วยให้เข้าใจ async hooks อย่างกระจ่างชัดผ่านแอปพลิเคชันที่แสดงภาพประกอบ

โมดูล ES6 ในโหนด 8

ขณะนี้ Node 8 รองรับโมดูล ES6 ซึ่งทำให้คุณสามารถใช้ไวยากรณ์นี้ได้:

 import { UtilityService } from './utility_service';

ในการใช้โมดูล ES6 ในโหนด 8 คุณต้องทำสิ่งต่อไปนี้

  1. เพิ่ม --experimental-modules ไปยังบรรทัดคำสั่ง
  2. เปลี่ยนชื่อนามสกุลไฟล์จาก . .js เป็น .mjs

HTTP/2

HTTP/2 เป็นการอัปเดตล่าสุดสำหรับโปรโตคอล HTTP ที่ไม่ได้อัปเดตบ่อยครั้ง และโหนด 8.4+ รองรับฟีเจอร์นี้ในโหมดทดลองโดยกำเนิด เร็วกว่า ปลอดภัยกว่า และมีประสิทธิภาพมากกว่า HTTP/1.1 รุ่นก่อน และ Google แนะนำให้คุณใช้ แต่มันทำอะไรได้อีก?

มัลติเพล็กซ์

ใน HTTP/1.1 เซิร์ฟเวอร์สามารถส่งการตอบกลับได้เพียงหนึ่งครั้งต่อการเชื่อมต่อในแต่ละครั้ง ใน HTTP/2 เซิร์ฟเวอร์สามารถส่งการตอบกลับพร้อมกันได้มากกว่าหนึ่งรายการ

พุชเซิร์ฟเวอร์

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

  1. ลูกค้าร้องขอเอกสาร HTML
  2. ลูกค้าค้นพบทรัพยากรที่จำเป็นจากเอกสาร HTML
  3. ไคลเอนต์ส่งคำขอ HTTP สำหรับทรัพยากรที่จำเป็นแต่ละรายการ ตัวอย่างเช่น ไคลเอ็นต์ส่งคำขอ HTTP สำหรับทรัพยากร JS และ CSS แต่ละรายการที่กล่าวถึงในเอกสาร

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

จัดลำดับความสำคัญ

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

เลิกนิสัยแย่ๆ

เนื่องจาก HTTP/1.1 ไม่อนุญาตให้ทำมัลติเพล็กซ์ จึงมีการใช้การเพิ่มประสิทธิภาพและวิธีแก้ปัญหาหลายอย่างเพื่อปกปิดความเร็วที่ช้าและการโหลดไฟล์ น่าเสียดายที่เทคนิคเหล่านี้ทำให้การใช้ RAM เพิ่มขึ้นและการเรนเดอร์ล่าช้า:

  • การแบ่งส่วนโดเมน: มีการใช้โดเมนย่อยหลายโดเมนเพื่อให้การเชื่อมต่อถูกกระจายและประมวลผลแบบขนาน
  • การรวมไฟล์ CSS และ JavaScript เพื่อลดจำนวนคำขอ
  • แผนที่ Sprite: การรวมไฟล์รูปภาพเพื่อลดคำขอ HTTP
  • Inlining: CSS และ JavaScript ถูกวางไว้ใน HTML โดยตรงเพื่อลดจำนวนการเชื่อมต่อ

ด้วย HTTP/2 ในตอนนี้ คุณจะลืมเทคนิคเหล่านี้ไปได้เลยและโฟกัสไปที่โค้ดของคุณ

แต่คุณใช้ HTTP/2 อย่างไร?

เบราว์เซอร์ส่วนใหญ่รองรับ HTTP/2 ผ่านการเชื่อมต่อ SSL ที่ปลอดภัยเท่านั้น บทความนี้สามารถช่วยคุณกำหนดค่าใบรับรองที่ลงนามเองได้ เพิ่มไฟล์ .crt และ .key ที่สร้างขึ้นในไดเร็กทอรีชื่อ ssl จากนั้น เพิ่มโค้ดด้านล่างลงในไฟล์ชื่อ server.js

อย่าลืมใช้ --expose-http2 ในบรรทัดคำสั่งเพื่อเปิดใช้งานคุณลักษณะนี้ เช่นคำสั่ง run สำหรับตัวอย่างของเราคือ node server.js --expose-http2

 const http2 = require('http2'); const path = require('path'); const fs = require('fs'); const PORT = 3000; const secureServerOptions = { cert: fs.readFileSync(path.join(__dirname, './ssl/server.crt')), key: fs.readFileSync(path.join(__dirname, './ssl/server.key')) }; const server = http2.createSecureServer(secureServerOptions, (req, res) => { res.statusCode = 200; res.end('Hello from Toptal'); }); server.listen( PORT, err => err ? console.error(err) : console.log(`Server listening to port ${PORT}`) );

แน่นอนว่า Node 8, Node 9, Node 10 และอื่นๆ ยังคงรองรับ HTTP 1.1 รุ่นเก่า—เอกสารประกอบของ Node.js อย่างเป็นทางการเกี่ยวกับธุรกรรม HTTP มาตรฐานจะไม่ค้างเป็นเวลานาน แต่ถ้าคุณต้องการใช้ HTTP/2 คุณสามารถเจาะลึกลงไปได้โดยใช้คู่มือ Node.js นี้

ดังนั้นฉันควรใช้ Node.js 8 ในตอนท้ายหรือไม่

Node 8 มาพร้อมกับการปรับปรุงประสิทธิภาพและคุณสมบัติใหม่ๆ เช่น async/await, HTTP/2 และอื่นๆ การทดลองแบบ end-to-end แสดงให้เห็นว่าโหนด 8 เร็วกว่าโหนด 6 ประมาณ 25% ซึ่งนำไปสู่การประหยัดต้นทุนได้มาก ดังนั้นสำหรับโครงการกรีนฟิลด์อย่างแน่นอน! แต่สำหรับโปรเจ็กต์ที่มีอยู่ คุณควรอัปเดต Node หรือไม่

ขึ้นอยู่กับว่าคุณจะต้องเปลี่ยนรหัสที่มีอยู่มากหรือไม่ เอกสารนี้แสดงรายการการเปลี่ยนแปลงการทำลาย Node 8 ทั้งหมดหากคุณมาจาก Node 6 อย่าลืมหลีกเลี่ยงปัญหาทั่วไปด้วยการติดตั้งแพ็คเกจ npm ของโปรเจ็กต์ใหม่ทั้งหมดโดยใช้เวอร์ชันล่าสุดของ Node 8 นอกจากนี้ ให้ใช้เวอร์ชัน Node.js เดียวกันบนเครื่องสำหรับการพัฒนาเสมอเช่นเดียวกับในเซิร์ฟเวอร์ที่ใช้งานจริง ขอให้โชคดี!

ที่เกี่ยวข้อง:
  • ทำไมฉันถึงต้องใช้ Node.js? บทช่วยสอนเป็นกรณีๆ ไป
  • การดีบักหน่วยความจำรั่วไหลใน Node.js Applications
  • การสร้าง Secure REST API ใน Node.js
  • การเข้ารหัส Cabin Fever: บทช่วยสอน Back-end ของ Node.js