Codelab นี้จะสำรวจว่าทั้งการลดขนาดและการบีบอัด JavaScript กลุ่มสำหรับแอปพลิเคชันต่อไปนี้ ช่วยปรับปรุงประสิทธิภาพของหน้าเว็บโดยการลด ขนาดคำขอของแอป
วัดระยะทาง
ก่อนจะเจาะลึกเพื่อเพิ่มการเพิ่มประสิทธิภาพ คุณควรวิเคราะห์ก่อน สถานะปัจจุบันของแอปพลิเคชัน
- หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกด เต็มหน้าจอ
แอปนี้ ซึ่งมีอยู่ในหัวข้อ "นำที่ไม่ได้ใช้ออกด้วย code" Codelab จะช่วยให้คุณโหวตให้กับเกมที่ชื่นชอบ ลูกแมว 🐈
ต่อไปมาดูกันว่าแอปพลิเคชันนี้มีขนาดใหญ่แค่ไหน
- กด "Control+Shift+J" (หรือ "Command+Option+J" ใน Mac) เพื่อเปิดเครื่องมือสำหรับนักพัฒนาเว็บ
- คลิกแท็บเครือข่าย
- เลือกช่องทำเครื่องหมายปิดใช้แคช
- โหลดแอปซ้ำ
แม้ว่าจะมีความคืบหน้าไปมากใน "นำโค้ดที่ไม่ได้ใช้ออก" Codelab เพื่อลดขนาด Bundle นี้ลง 225 KB ยังค่อนข้างใหญ่อยู่
การทำให้มีขนาดเล็กลง
ลองพิจารณาบล็อกโค้ดต่อไปนี้
function soNice() {
let counter = 0;
while (counter < 100) {
console.log('nice');
counter++;
}
}
หากบันทึกฟังก์ชันนี้เป็นไฟล์ของตัวเอง ไฟล์จะมีขนาดประมาณ 112 B (ไบต์)
หากนำช่องว่างออกหมดแล้ว โค้ดที่ได้จะมีลักษณะดังนี้
function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}
ขนาดไฟล์ตอนนี้จะอยู่ที่ประมาณ 83 B ถ้าปัญหานี้ทำให้เกิดความยุ่งยากมากขึ้นโดยการลด ความยาวของชื่อตัวแปร และการแก้ไขนิพจน์บางอย่าง โค้ดสุดท้ายอาจ จะมีหน้าตาดังนี้
function soNice(){for(let i=0;i<100;)console.log("nice"),i++}
ขณะนี้ไฟล์มีขนาดถึง 62 B
ในแต่ละขั้นตอน รหัสดังกล่าวจะอ่านยากขึ้น อย่างไรก็ตาม การตั้งค่า เครื่องมือ JavaScript จะแปลความหมายของแต่ละสิ่งเหล่านี้ในลักษณะเดียวกัน ประโยชน์ของโค้ดที่ปรับให้ยากต่อการอ่าน (Obfuscate) ในลักษณะนี้จะช่วยให้ไฟล์มีขนาดเล็กลง ขนาดต่างๆ 112 B ไม่ได้เริ่มต้นมากนัก แต่ก็ยังมี 50% การลดขนาด!
ในแอปพลิเคชันนี้ webpack เวอร์ชัน 4 จะใช้เป็น
Bundler ของโมดูล ดูเวอร์ชันที่เฉพาะเจาะจงได้ใน package.json
"devDependencies": {
//...
"webpack": "^4.16.4",
//...
}
เวอร์ชัน 4 จะลดขนาดแพ็กเกจโดยค่าเริ่มต้นอยู่แล้วในโหมดการใช้งานจริง โดยใช้
TerserWebpackPlugin
ปลั๊กอินสำหรับ Terser
Terser เป็นเครื่องมือที่ได้รับความนิยมในการบีบอัดโค้ด JavaScript
ถ้าต้องการทราบว่าโค้ดที่ลดขนาดลงมีลักษณะอย่างไร ให้คลิก
main.bundle.js
ขณะที่ยังอยู่ในแผงเครือข่ายเครื่องมือสำหรับนักพัฒนาเว็บ จากนั้นคลิก
การตอบกลับ
โค้ดในรูปแบบสุดท้ายซึ่งถูกลดขนาดลงและเสียหาย จะแสดงในเนื้อหาการตอบสนอง
หากต้องการดูว่าแพ็กเกจอาจมีขนาดใหญ่แค่ไหนหากไม่ลดขนาด ให้เปิด
webpack.config.js
และอัปเดตการกำหนดค่า mode
module.exports = {
mode: 'production',
mode: 'none',
//...
โหลดแอปพลิเคชันซ้ำและดูขนาดแพ็กเกจอีกครั้งผ่าน แผงเครือข่ายเครื่องมือสำหรับนักพัฒนาเว็บ
นับว่าแตกต่างไปค่อนข้างมาก 😅
โปรดตรวจสอบว่าได้เปลี่ยนกลับการเปลี่ยนแปลงที่นี่ก่อนดำเนินการต่อ
module.exports = {
mode: 'production',
mode: 'none',
//...
การรวมกระบวนการลดขนาดโค้ดในแอปพลิเคชันของคุณจะขึ้นอยู่กับเครื่องมือต่างๆ ที่คุณใช้:
- หากใช้ Webpack v4 ขึ้นไป คุณไม่จําเป็นต้องดําเนินการใดๆ เพิ่มเติม เพราะโค้ดจะถูกลดขนาดลงโดยค่าเริ่มต้นในโหมดที่ใช้งานจริง 👍
- หากใช้ Webpack เวอร์ชันเก่า ให้ติดตั้งและเพิ่ม
TerserWebpackPlugin
ไว้ด้วย ในกระบวนการสร้าง Webpack เอกสารประกอบ จะอธิบายเรื่องนี้อย่างละเอียด - และยังมีปลั๊กอินสำหรับการลดขนาดอื่นๆ อยู่และใช้แทนได้ เช่น BabelMinifyWebpackPlugin และ ClosureCompilerPlugin
- หากไม่มีการใช้งานโมดูล Bundler เลย ให้ใช้ Terser เป็นเครื่องมือ CLI หรือรวมไว้เป็นทรัพยากร Dependency โดยตรง
การบีบอัด
แม้ว่าคำว่า "การบีบอัด" บางครั้งจะมีการใช้เพื่ออธิบายว่าโค้ด ลดลงในกระบวนการลดขนาด จริงๆ แล้วไม่ได้บีบอัดใน ความหมายตามตัวอักษร
การบีบอัดมักจะหมายถึงโค้ดที่มีการแก้ไขโดยใช้ข้อมูล ของอัลกอริทึมการบีบอัด ต่างจากการลดขนาด ซึ่งจบลงแล้ว จะให้ผลลัพธ์ที่ดีที่สุด โค้ดถูกต้อง โค้ดที่บีบอัดจะต้องคลายการบีบอัดก่อนที่จะใช้งาน
ทุกครั้งที่มีคำขอและการตอบสนอง HTTP เบราว์เซอร์และเว็บเซิร์ฟเวอร์สามารถเพิ่ม
ส่วนหัวที่จะรวม
ข้อมูลเพิ่มเติมเกี่ยวกับเนื้อหาที่ดึงหรือได้รับ ประเภท
ดูในแท็บHeaders
ในแผงเครือข่ายเครื่องมือสำหรับนักพัฒนาเว็บซึ่งมี 3 ประเภท
แสดงอยู่:
- General แสดงส่วนหัวทั่วไปที่เกี่ยวข้องกับคำขอคำตอบทั้งหมด การโต้ตอบ
- ส่วนหัวการตอบกลับแสดงรายการส่วนหัวที่เกี่ยวข้องกับการตอบกลับจริงโดยเฉพาะ จากเซิร์ฟเวอร์
- ส่วนหัวของคำขอแสดงรายการส่วนหัวที่แนบมากับคำขอโดย ของคุณ
ดูส่วนหัว accept-encoding
ใน Request Headers
เบราว์เซอร์จะใช้ accept-encoding
เพื่อระบุเนื้อหา
รูปแบบการเข้ารหัส หรืออัลกอริทึมการบีบอัดไฟล์ที่รองรับ เรามี
อัลกอริทึมการบีบอัดข้อความที่มีอยู่ แต่มีอยู่ 3 แบบเท่านั้น
สนับสนุนการบีบอัด (และคลายการบีบอัด) ของคำขอเครือข่าย HTTP ที่นี่
- Gzip (
gzip
): การบีบอัดที่ใช้กันอย่างแพร่หลาย สำหรับการโต้ตอบระหว่างเซิร์ฟเวอร์และไคลเอ็นต์ ต่อยอดมาจาก Deflate อัลกอริทึม และสนับสนุนในเบราว์เซอร์ปัจจุบันทั้งหมด - Deflate (
deflate
): ไม่นิยมใช้ - Brotli (
br
): การบีบอัดที่ใหม่กว่า ซึ่งมีเป้าหมายเพื่อปรับปรุงอัตราส่วนการบีบอัด การโหลดหน้าเว็บที่เร็วขึ้น โดยใช้ได้ใน เบราว์เซอร์ส่วนใหญ่ในเวอร์ชันล่าสุด
แอปพลิเคชันตัวอย่างในบทแนะนำนี้เหมือนกับแอปที่เสร็จสมบูรณ์ใน "นำโค้ดที่ไม่ได้ใช้งานออก" Codelab ยกเว้นข้อเท็จจริงที่ ปัจจุบัน Express ใช้เป็นเฟรมเวิร์กของเซิร์ฟเวอร์ ในอีก จะมีการสำรวจการบีบอัดทั้งแบบคงที่และแบบไดนามิก
การบีบอัดแบบไดนามิก
การบีบอัดแบบไดนามิกเกี่ยวข้องกับการบีบอัดเนื้อหาทันทีที่ได้รับ ตามที่เบราว์เซอร์ขอ
ข้อดี
- การสร้างและอัปเดตชิ้นงานเวอร์ชันที่บีบอัดที่บันทึกไว้ไม่จำเป็นต้องเป็น เสร็จสิ้น
- การบีบอัดทันทีทำงานได้ดีบนหน้าเว็บที่ ที่สร้างขึ้นแบบไดนามิก
ข้อเสีย
- การบีบอัดไฟล์ในระดับที่สูงขึ้นเพื่อให้ได้อัตราส่วนการบีบอัดที่ดีขึ้น ใช้เวลานานขึ้น กรณีนี้อาจทำให้เกิด Performance Hit เกิดขึ้นได้เนื่องจากผู้ใช้กำลังรอให้เนื้อหา บีบอัดก่อนที่จะส่งโดยเซิร์ฟเวอร์
การบีบอัดแบบไดนามิกด้วยโหนด/ด่วน
ไฟล์ server.js
มีหน้าที่ในการตั้งค่าเซิร์ฟเวอร์โหนดที่โฮสต์
แอปพลิเคชัน
const express = require('express');
const app = express();
app.use(express.static('public'));
const listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
});
ปัจจุบันเหลือเพียงการนำเข้า express
และใช้ express.static
มิดเดิลแวร์ที่จะโหลดไฟล์ HTML, JS และ CSS แบบคงที่ทั้งหมดใน
ไดเรกทอรี public/
(และไฟล์เหล่านั้นสร้างโดย Webpack ในทุกบิลด์)
เพื่อให้มั่นใจว่าระบบจะบีบอัดเนื้อหาทั้งหมดทุกครั้งที่มีการขอ
การบีบอัดไลบรารีมิดเดิลแวร์สามารถ
เริ่มต้นด้วยการเพิ่มเป็น devDependency
ใน package.json
:
"devDependencies": {
//...
"compression": "^1.7.3"
},
และนำเข้าข้อมูลลงในไฟล์เซิร์ฟเวอร์ server.js
:
const express = require('express');
const compression = require('compression');
และเพิ่มเป็นมิดเดิลแวร์ก่อนจะต่อเชื่อม express.static
ดังนี้
//...
const app = express();
app.use(compression());
app.use(express.static('public'));
//...
ตอนนี้ให้โหลดแอปซ้ำแล้วดูขนาดแพ็กเกจในแผงเครือข่าย
จาก 225 KB เป็น 61.6 KB ในResponse Headers
ตอนนี้ content-encoding
ส่วนหัวแสดงให้เห็นว่าเซิร์ฟเวอร์กำลังส่งไฟล์ที่เข้ารหัสด้วย gzip
ลงมา
การบีบอัดแบบคงที่
แนวคิดเบื้องหลังการบีบอัดแบบคงที่คือการบีบอัดเนื้อหาและบันทึก ล่วงหน้า
ข้อดี
- ไม่ต้องกังวลเรื่องเวลาในการตอบสนองเนื่องจากระดับการบีบอัดที่สูงอีกต่อไป ไม่จำเป็นต้องบีบอัดไฟล์แบบเรียลไทม์ เพราะดึงข้อมูลโดยตรงได้แล้ว
ข้อเสีย
- ต้องบีบอัดเนื้อหาในทุกบิลด์ เวลาในการสร้างอาจเพิ่มขึ้น อย่างมากหากใช้ระดับการบีบอัดสูง
การบีบอัดแบบคงที่ด้วย Node/Express และ Webpack
เนื่องจากการบีบอัดแบบคงที่เกี่ยวข้องกับการบีบอัดไฟล์ล่วงหน้า Webpack
สามารถแก้ไขการตั้งค่าเพื่อบีบอัดเนื้อหาเป็นส่วนหนึ่งของขั้นตอนการสร้างได้
CompressionPlugin
สำหรับกรณีนี้
เริ่มต้นด้วยการเพิ่มเป็น devDependency
ใน package.json
:
"devDependencies": {
//...
"compression-webpack-plugin": "^1.1.11"
},
เช่นเดียวกับปลั๊กอิน Webpack อื่นๆ ให้นำเข้าในไฟล์การกำหนดค่า
webpack.config.js:
const path = require("path");
//...
const CompressionPlugin = require("compression-webpack-plugin");
และรวมไว้ในอาร์เรย์ plugins
module.exports = {
//...
plugins: [
//...
new CompressionPlugin()
]
}
โดยค่าเริ่มต้น ปลั๊กอินจะบีบอัดไฟล์บิลด์โดยใช้ gzip
ลองดูสิ
ในเอกสารประกอบ
เพื่อดูวิธีเพิ่มตัวเลือกเพื่อใช้อัลกอริทึมอื่นหรือรวม/ยกเว้น
บางไฟล์
เมื่อแอปโหลดซ้ำและสร้างใหม่ เวอร์ชันบีบอัดของ Bundle หลักจะเป็น
สร้างไว้แล้ว เปิดคอนโซล Glitch เพื่อดูสิ่งที่อยู่ข้างใน
ไดเรกทอรี public/
สุดท้ายที่แสดงโดยเซิร์ฟเวอร์โหนด
- คลิกปุ่มเครื่องมือ
- คลิกปุ่มคอนโซล
- ในคอนโซล ให้เรียกใช้คำสั่งต่อไปนี้เพื่อเปลี่ยนเป็น
public
และดูไฟล์ทั้งหมดของไดเรกทอรีดังกล่าว:
cd public
ls
ตอนนี้ไฟล์ main.bundle.js.gz
ในเวอร์ชัน gzip จะบันทึกไว้ที่นี่เป็น
CompressionPlugin
จะบีบอัด index.html
โดยค่าเริ่มต้นด้วย
ขั้นตอนต่อไปที่ต้องทำคือแจ้งให้เซิร์ฟเวอร์ส่ง gzip เหล่านี้
เมื่อใดก็ตามที่มีการขอเวอร์ชัน JS ต้นฉบับ สามารถทำได้
โดยกำหนดเส้นทางใหม่ใน server.js
ก่อนที่ไฟล์จะแสดงด้วย
express.static
const express = require('express'); const app = express(); app.get('*.js', (req, res, next) => { req.url = req.url + '.gz'; res.set('Content-Encoding', 'gzip'); next(); }); app.use(express.static('public')); //...
app.get
ใช้เพื่อบอกเซิร์ฟเวอร์ว่าจะตอบสนองต่อคำขอ GET สำหรับ
ปลายทางที่ต้องการ จากนั้นจะมีการใช้ฟังก์ชัน Callback เพื่อกำหนดวิธีจัดการ
อีกครั้ง โดยเส้นทางจะทำงานดังนี้
- การระบุ
'*.js'
เป็นอาร์กิวเมนต์แรกหมายความว่าวิธีนี้เหมาะสำหรับทุก ปลายทางที่เริ่มทำงานเพื่อดึงไฟล์ JS - ภายใน Callback จะมี
.gz
แนบอยู่กับ URL ของคําขอและ ตั้งค่าส่วนหัวการตอบกลับContent-Encoding
เป็นgzip
แล้ว - สุดท้าย
next()
จะตรวจสอบว่าลำดับต่อเนื่องไปยัง Callback ก็อาจจะเป็นโอกาสต่อไป
เมื่อโหลดแอปซ้ำแล้ว ให้ดูที่แผง Network
อีกครั้ง
เช่นเดียวกับก่อนหน้านี้ ขนาดแพ็กเกจจะลดลงอย่างมาก
บทสรุป
Codelab นี้ครอบคลุมกระบวนการลดขนาดและบีบอัดซอร์สโค้ด เทคนิคทั้งสองนี้กลายเป็นเทคนิคเริ่มต้นในเครื่องมือหลายอย่าง ที่พร้อมใช้งานในปัจจุบัน ดังนั้นจึงเป็นเรื่องสำคัญที่จะต้องทราบว่าเครื่องมือเชนของคุณ รองรับหรือคุณควรเริ่มใช้ทั้ง 2 กระบวนการด้วยตัวเอง