ลดขนาดและบีบอัดเพย์โหลดของเครือข่ายด้วย gzip

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

ภาพหน้าจอแอป

วัดระยะทาง

ก่อนจะเจาะลึกเพื่อเพิ่มการเพิ่มประสิทธิภาพ คุณควรวิเคราะห์ก่อน สถานะปัจจุบันของแอปพลิเคชัน

  • หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกด เต็มหน้าจอ เต็มหน้าจอ

แอปนี้ ซึ่งมีอยู่ในหัวข้อ "นำที่ไม่ได้ใช้ออกด้วย code" Codelab จะช่วยให้คุณโหวตให้กับเกมที่ชื่นชอบ ลูกแมว 🐈

ต่อไปมาดูกันว่าแอปพลิเคชันนี้มีขนาดใหญ่แค่ไหน

  1. กด "Control+Shift+J" (หรือ "Command+Option+J" ใน Mac) เพื่อเปิดเครื่องมือสำหรับนักพัฒนาเว็บ
  2. คลิกแท็บเครือข่าย
  3. เลือกช่องทำเครื่องหมายปิดใช้แคช
  4. โหลดแอปซ้ำ

ขนาดกลุ่มเดิมในแผงเครือข่าย

แม้ว่าจะมีความคืบหน้าไปมากใน "นำโค้ดที่ไม่ได้ใช้ออก" 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',
  //...

โหลดแอปพลิเคชันซ้ำและดูขนาดแพ็กเกจอีกครั้งผ่าน แผงเครือข่ายเครื่องมือสำหรับนักพัฒนาเว็บ

ขนาดชุด 767 KB

นับว่าแตกต่างไปค่อนข้างมาก 😅

โปรดตรวจสอบว่าได้เปลี่ยนกลับการเปลี่ยนแปลงที่นี่ก่อนดำเนินการต่อ

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 กระบวนการด้วยตัวเอง