Hot Reload คือเทคโนโลยีที่การเปลี่ยนแปลงในซอร์สโค้ดถูกนำไปใช้กับแอปพลิเคชันที่กำลังทำงานอยู่โดยไม่ต้องโหลดหน้าซ้ำทั้งหมดและไม่สูญเสียสถานะปัจจุบัน แตกต่างจาก Live Reload ที่เพียงรีเฟรชทั้งหน้า การโหลดซ้ำแบบร้อนจะแทนที่เฉพาะโมดูลที่เปลี่ยนแปลงทันที ตามข้อมูลจาก Webpack, 2024 Hot Module Replacement (HMR) ช่วยลดระยะเวลาวงจรการพัฒนาได้ถึง 80% โดยการรักษาสถานะของคอมโพเนนต์
ประเด็นสำคัญ
Hot Reload คือกลไกการพัฒนาที่การเปลี่ยนแปลงในไฟล์ซอร์สสะท้อนให้เห็นทันทีในแอปพลิเคชันที่กำลังทำงานอยู่โดยไม่ต้องเริ่มต้นใหม่ นักพัฒนาแก้ไขโค้ดในโปรแกรมแก้ไข บันทึกไฟล์ และผลลัพธ์จะแสดงทันทีในเบราว์เซอร์หรืออีมูเลเตอร์
คุณสมบัติหลักของเทคโนโลยีนี้คือการรักษา สถานะของแอปพลิเคชัน ตัวแปรในหน่วยความจำ ข้อมูลในฟอร์ม การนำทางปัจจุบัน — ทุกอย่างยังคงอยู่ที่เดิม นักพัฒนาไม่ต้องทำซ้ำลำดับการดำเนินการเพื่อกลับไปยังหน้าจอที่ต้องการ
ในการพัฒนาเว็บ Hot Reload ถูกนำไปใช้ผ่านกลไก Hot Module Replacement (HMR) บัณฑلเลอร์ติดตามการเปลี่ยนแปลงไฟล์ ระบุว่าโมดูลใดได้รับผลกระทบ และส่งโค้ดที่อัปเดตไปยังเบราว์เซอร์ผ่าน WebSocket เบราว์เซอร์จะแทนที่เฉพาะโมดูลเหล่านั้นโดยไม่ต้องโหลดหน้าซ้ำ
ความนิยมของ Hot Reload เพิ่มสูงขึ้นพร้อมกับการแพร่หลายของแอปพลิเคชันหน้าเดียว (SPA) ซึ่งการโหลดหน้าซ้ำทั้งหมดจะทำลายสถานะทั้งหมดของแอปพลิเคชันฝั่งไคลเอ็นต์ React, Vue, Angular และเฟรมเวิร์กอื่นๆ แนะนำ HMR เป็นโหมดการพัฒนาหลัก
กลไก HMR ประกอบด้วยสี่ขั้นตอน บัณฑลเลอร์ ฝั่งเซิร์ฟเวอร์ คอมไพล์โมดูลที่เปลี่ยนแปลง สร้างแพตช์ JSON พร้อมเวอร์ชันโค้ดใหม่ และส่งไปยังเบราว์เซอร์ผ่านการเชื่อมต่อ WebSocket รันไทม์ HMR ฝั่งเบราว์เซอร์รับแพตช์ แทนที่โมดูลเก่าด้วยโมดูลใหม่ และแจ้งให้ผู้ติดตามโมดูลทราบ
Webpack Dev Server ใช้ WebSocket เพื่อสื่อสารกับไคลเอ็นต์ เมื่อไฟล์เปลี่ยนแปลง บัณฑลเลอร์จะสร้าง hot-update.js และ hot-update.json พร้อมรายการการเปลี่ยนแปลง ไคลเอ็นต์โหลดไฟล์เหล่านี้โดยอัตโนมัติผ่าน JSONP
// webpack.config.js - การตั้งค่า HMR
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devServer: {
hot: true,
liveReload: false
},
plugins: [new HtmlWebpackPlugin()]
};
รันไทม์ HMR ในเบราว์เซอร์ตรวจสอบว่าโมดูลที่กำลังถูกแทนที่รองรับ accept หรือไม่ หากโมดูลประกาศ module.hot.accept() รันไทม์จะแทนที่ในที่และเรียกใช้ callback การอัปเดต หากไม่ได้ประกาศ accept HMR จะถอยกลับไปยังระดับโมดูลหลัก
ใน React Fast Refresh ตรรกะนี้ถูกสร้างไว้ในระดับเฟรมเวิร์ก — ไม่จำเป็นต้องเขียน module.hot.accept ด้วยตนเองในทุกไฟล์ ปลั๊กอิน Babel react-refresh/babel เพิ่มโค้ดที่แทนที่คอมโพเนนต์อย่างปลอดภัยโดยไม่สูญเสียสถานะ
สามคำนี้มักสับสน แต่แตกต่างกันอย่างสิ้นเชิงในพฤติกรรม Live Reload โหลดทั้งหน้าใหม่ทุกครั้งที่มีการเปลี่ยนแปลง — สถานะทั้งหมดหายไป Full Reload เกิดขึ้นเมื่อเซิร์ฟเวอร์พัฒนาถูกรีสตาร์ทอย่างสมบูรณ์ ทำให้สูญเสียไม่เพียงแต่สถานะไคลเอ็นต์แต่ยังรวมถึงสถานะเซิร์ฟเวอร์ด้วย
| ประเภท | โหลดหน้าซ้ำ | การรักษาสถานะ | ความเร็ว |
|---|---|---|---|
| Hot Reload (HMR) | ไม่ | ใช่ | ทันที |
| Live Reload | ใช่ | ไม่ | 1-3 วินาที |
| Full Reload | ใช่ | ไม่ | 3-10 วินาที |
การเลือกระหว่าง Hot และ Live ส่งผลโดยตรงต่อ ความเร็วในการพัฒนา ด้วย HMR วงจร “แก้ไขโค้ด → ดูผลลัพธ์” ใช้เวลา 50-200 มิลลิวินาที ด้วย Live Reload — 1-3 วินาทีบวกกับเวลาในการกู้คืนบริบท ตลอดทั้งวันทำงาน ความแตกต่างคิดเป็นนาทีที่ประหยัดได้หลายสิบ าที
React Fast Refresh คือการนำ Hot Reload ไปใช้อย่างเป็นทางการที่ปรับให้เหมาะสมสำหรับคอมโพเนนต์ React มันแทนที่กลไก React Hot Loader แบบเก่าที่ไม่เสถียรและต้องการการกำหนดค่าด้วยตนเอง Fast Refresh ถูกสร้างไว้ใน Create React App, Next.js, Vite และ Webpack ผ่าน react-refresh/babel
Fast Refresh รักษาสถานะของฟังก์ชันนัลคอมโพเนนต์อย่างถูกต้องเมื่อโค้ดของมันเปลี่ยนไป หากคุณเปลี่ยนเฉพาะมาร์กอัป JSX useState และฮุกอื่นๆ จะคงค่าของมันไว้ หากตรรกะของฮุกเปลี่ยนไป คอมโพเนนต์จะถูกเมาท์ใหม่
// ตัวอย่าง - สถานะถูกเก็บรักษาเมื่อ JSX เปลี่ยนแปลง
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
Fast Refresh จะไม่รักษาสถานะหากโค้ดใหม่มี ข้อผิดพลาดในการคอมไพล์ หากคอมโพเนนต์ไม่ได้ถูกส่งออกเป็นค่าเริ่มต้น (named export) สถานะอาจไม่ได้รับการเก็บรักษาเช่นกัน แนะนำให้ใช้ default export สำหรับคอมโพเนนต์และห่อ export ด้วย memo หากจำเป็นต้องรับประกันการรักษาสถานะ
บัณฑลเลอร์สมัยใหม่มี HMR Webpack เป็นผู้บุกเบิก HMR พร้อมการกำหนด�ี่ยืดหยุ่นผ่าน devServer.hot Vite ใช้ ESM ดั้งเดิมและ esbuild สำหรับ HMR ทันทีโดยไม่ต้องบันเดิล Parcel มี HMR แบบไม่ต้องกำหนดค่า Turbopack เป็นบัณฑลเลอร์ใหม่จาก Vercel พร้อม HMR บน Rust
| เครื่องมือ | ประเภท HMR | การกำหนดค่า | ระบบนิเวศ |
|---|---|---|---|
| Webpack 5 | HMR ผ่าน WebSocket | devServer.hot: true | React, Vue, Angular |
| Vite | HMR แบบ ESM ดั้งเดิม | ในตัว | React, Vue, Svelte |
| Parcel 2 | HMR แบบไม่ต้องกำหนดค่า | ไม่จำเป็น | React, Vue, TS |
| Turbopack | HMR แบบเพิ่มขึ้น | ในตัว | Next.js |
Vite โดดเด่นด้านความเร็ว — HMR ใน Vite ทำงานผ่านโมดูล ES ดั้งเดิมของเบราว์เซอร์ เมื่อไฟล์เปลี่ยนแปลง Vite จะส่งเฉพาะโมดูลนั้นผ่าน import() โดยไม่ต้องคอมไพล์ทั้งโปรเจกต์ใหม่ Webpack คอมไพล์แต่ละโมดูลทั้งหมด ซึ่งทำให้ HMR ช้าลงในโปรเจกต์ขนาดใหญ่
มาดูการตั้งค่า HMR สำหรับบัณฑลเลอร์ต่างๆ ใน Webpack ต้องตั้งค่า hot: true ใน devServer และเพิ่ม react-refresh/babel ใน Vite HMR ทำงานในตัวกับพรีเซ็ตใดๆ ใน Next.js Fast Refresh เปิดใช้งานโดยค่าเริ่มต้นผ่านเทอร์โบคอมไพเลอร์
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
hmr: {
port: 3001
}
}
});
// webpack.config.js
const ReactRefreshWebpackPlugin =
require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
mode: 'development',
devServer: { hot: true },
plugins: [new ReactRefreshWebpackPlugin()],
module: {
rules: [
{
test: /\.jsx?$/u,
use: 'babel-loader'
}
]
}
};
HMR ทำงานได้ไม่ถูกต้องในทุกสถานการณ์ โมดูล CSS ที่มีคลาสไดนามิกอาจไม่ถูกนำไปใช้ ตัวแปรโกลบอล และซิงเกิลตัน (Redux store, เราเ���อร์) อาจรีเซ็ตเมื่อโหลดซ้ำ ไลบรารีหนัก ใน HMR บางครั้งอาจทำให้เกิดการรั่วไหลของหน่วยความจำเนื่องจากการแทนที่โมดูลโดยไม่มีการเก็บขยะ
วิธีแก้ปัญหา HMR คือการแยก โค้ดร้อน ออกเป็นคอมโพเนนต์บริสุทธิ์ที่ไม่มีผลข้างเคียง ผลข้างเคียง (การสมัครรับ WebSocket, ตัวจับเวลา) ควรทำความสะอาดใน useEffect สำหรับ Redux แนะนำให้ใช้ persister ที่บันทึกสถานะใน sessionStorage โซลูชัน CSS เช่น CSS Modules หรือ styled-components ทำงานอย่างถูกต้องกับ HMR โดยไม่ต้องกำหนดค่าเพิ่มเติม
ข้อจำกัดอีกประการคือ การดีบักในโปรดักชัน: HMR ถูกปิดใช้งานโดยเจตนาในบิลด์โปรดักชัน ซอร์สโค้ดถูกย่อขนาดและการแทนที่แบบร้อนในโปรดักชันเป็นไปไม่ได้ ในการดีบักข้อผิดพลาดในโปรดักชัน ให้ใช้ source maps และการบันทึก แต่ไม่ใช่ HMR
คำถามที่พบบ่อย
Hot Reload จะแทนที่เฉพาะโมดูลที่เปลี่ยนแปลงโดยไม่โหลดหน้าซ้ำและรักษาสถานะไว้ Live Reload โหลดทั้งหน้าใหม่ — สถานะหายไป แอปพลิเคชันเริ่มต้นใหม่จากหน้าจอเริ่มต้น
ในโปรเจกต์ที่ใช้ Create React App และ Vite HMR เปิดใช้งานโดยค่าเริ่มต้น ใน Webpack ต้องเพิ่ม devServer: { hot: true } และ @pmmmwh/react-refresh-webpack-plugin ใน Next.js Fast Refresh ทำงานโดยไม่ต้องกำหนดค่า
สาเหตุคือ ข้อผิดพลาดในการคอมไพล์ ในโค้ดใหม่ named export แทน default การเปลี่ยนแปลงลายเซ็นของฮุก หรือการกลายพันธุ์สถานะโดยตรง React Fast Refresh จะรีเซ็ตคอมโพเนนต์อย่างปลอดภัยเมื่อจำเป็นสำหรับการทำงานที่ถูกต้องเท่านั้น
ใช่ บัณฑลเลอร์สมัยใหม่ทั้งหมดรองรับ HMR กับ TypeScript Vite ใช้ esbuild สำหรับการแปลง TS Webpack ใช้ ts-loader หรือ babel กับ @babel/preset-typescript Fast Refresh ทำงานอย่างถูกต้องกับคอมโพเนนต์ที่พิมพ์แล้ว
ตรวจสอบ คอนโซลเบราว์เซอร์ สำหรับข้อผิดพลาดการเชื่อมต่อ WebSocket ตรวจสอบให้แน่ใจว่า devServer.hot: true เปิดใช้งานอยู่ สำหรับ Vite ตรวจสอบพอร์ต HMR หากปัญหายังคงอยู่ ให้โหลดหน้าซ้ำทั้งหมด (F5) และตรวจสอบคอนโซลของบัณฑลเลอร์
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม