Gói web rất tốt để xây dựng các ứng dụng React, nhưng bạn có biết nó cũng có thể giúp bạn tối ưu hóa hiệu suất ứng dụng không? Webpack có thể tự động nội tuyến dữ liệu hình ảnh, cải thiện hiệu suất bằng cách giảm số lượng yêu cầu mà trang của bạn cần thực hiện. Hãy học cách làm.
Nội tuyến hình ảnh
Thông thường, mọi hình ảnh trên trang web là một tệp duy nhất mà trình duyệt phải thực hiện yêu cầu HTTP để hiển thị. Trình duyệt cần thực hiện càng nhiều yêu cầu thì thời gian tải trang càng lâu. Ngược lại, giảm tổng số yêu cầu nói chung sẽ cải thiện hiệu suất.
Nội tuyến hình ảnh làm giảm số lượng yêu cầu bổ sung cần thiết để tải trang bằng cách nhúng dữ liệu hình ảnh trực tiếp vào gói HTML hoặc Javascript. Như với bất kỳ điều gì, tối ưu hóa này không miễn phí: Tổng số lượng yêu cầu hình ảnh được giảm xuống với mức giá của tải trọng ban đầu lớn hơn. Điều này dẫn đến một điểm đặc biệt về hiệu suất trong đó hình ảnh nhỏ được nội tuyến, nhưng hình ảnh lớn hơn được tải bình thường với một yêu cầu HTTP bổ sung.
Chào! Bạn không muốn đọc tất cả thông tin chi tiết về việc bẻ cong Webpack thành hình dạng? Chuyển đến cấu hình webpack cuối cùng.
Một ứng dụng phản ứng đơn giản
Để kiểm tra nội tuyến hình ảnh, chúng tôi đã tạo một ứng dụng React đơn giản:
Thư mục nguồn ứng dụng React cơ bản
A index.html
tệp được sử dụng để khởi động tệp JSX duy nhất (đã biên dịch):
<html>
<header>
<title>React With Inline Images</title>
</header>
<body>
<div class="images-container"></div>
</body>
<script src="index.js"></script>
</html>
import React from "react"
import ReactDOM from "react-dom/client"
import indexHtml from "./index.html"
function SomeImages(props) {
return (
<div>
<h2>{props.title}</h2>
<p>
<h3>Some small images:</h3>
<img src="images/small-bee.png" />
<img src="images/small-chick.png" />
<img src="images/small-puppy.png" />
<img src="images/small-tree.png" />
</p>
<p>
<h3>Some larger images:</h3>
<img src="images/medium-duckling.jpg" /><br />
<img src="images/medium-squirrel.jpg" />
</p>
</div>
)
}
var containerDiv = document.querySelector(".images-container");
var root = ReactDOM.createRoot(containerDiv);
root.render(SomeImages({ title: "React with Inline Images" }));
Xây dựng tệp React JSX với Webpack
Đầu tiên, các phụ thuộc Webpack và React cần được cài đặt với NPM:
npm install react react-dom
npm install --save-dev webpack webpack-cli babel-loader @babel/preset-react
Webpack không biên dịch JSX ra khỏi hộp. Thêm quy tắc mô-đun vào webpack.config.js
yêu cầu Webpack sử dụng Babel khi biên dịch các tệp JSX. Có một quy tắc bổ sung để sao chép html bootstrap của chúng tôi vào thư mục đầu ra. Tìm hiểu thêm về "mô-đun nội dung" sau:
var path = require("path");
module.exports = {
mode: "development",
entry: "./src/index.jsx",
output: {
filename: "index.js",
path: path.resolve("dist/"),
},
module: {
rules: [
{
test: /.jsx?$/,
loader: "babel-loader",
options: {
"presets": ["@babel/preset-react"]
}
},
{
test: /.html$/i,
type: "asset/resource",
generator: {
filename: "[name][ext]"
}
}
]
}
};
Chạy webpack
từ dòng lệnh biên dịch JSX của chúng tôi thành một thư mục đầu ra có tên dist/
, nhưng có một số vấn đề cần được khắc phục.
Nhập / Yêu cầu Hình ảnh
Chà, những thứ ALMOST hoạt động. Tất cả các thẻ hình ảnh của chúng tôi đều bị hỏng khi chúng tôi tải ứng dụng đã biên dịch:
Và không có hình ảnh nào được xuất ra dist/
thư mục:
Hình ảnh không hiển thị vì Webpack không đọc các url trong src
thuộc tính. Không có tệp hình ảnh nào của chúng tôi được sao chép vào dist/
vì Webpack giả định rằng chúng ta đang tham chiếu đến một phụ thuộc bên ngoài mà nó không cần phải lo lắng. JSX cần nhập hoặc yêu cầu hình ảnh để Webpack biết chúng ta cần những hình ảnh đó:
// BEFORE:
<p>
<h3>Some small images:</h3>
<img src="images/small-bee.png" />
<img src="images/small-chick.png" />
<img src="images/small-puppy.png" />
<img src="images/small-tree.png" />
</p>
<p>
<h3>Some larger images:</h3>
<img src="images/medium-duckling.jpg" /><br />
<img src="images/medium-squirrel.jpg" />
</p>
// AFTER:
<p>
<h3>Some small images:</h3>
<img src={require("./images/small-bee.png")} />
<img src={require("./images/small-chick.png")} />
<img src={require("./images/small-puppy.png")} />
<img src={require("./images/small-tree.png")} />
</p>
<p>
<h3>Some larger images:</h3>
<img src={require("./images/medium-duckling.jpg")} /><br />
<img src={require("./images/medium-squirrel.jpg")} />
</p>
Sử dụng mô-đun nội dung cho tệp hình ảnh
Và, mọi thứ vẫn bị phá vỡ. Webpack biết về hình ảnh của chúng tôi bây giờ, nhưng đang gặp lỗi:
ERROR in ./src/images/medium-duckling.jpg 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, /
currently no loaders are configured to process this file. /
See https://webpack.js.org/concepts
(Source code omitted for this binary file)
@ ./src/index.jsx 16:9-48
Webpack không thành công vì nó không biết phải làm gì với các tệp hình ảnh của chúng tôi. Cũng như với JSX, chúng ta cần một quy tắc mô-đun cho Webpack biết phải làm gì khi nó gặp một hình ảnh.
Webpack 5 có một tính năng mới được gọi là Mô-đun tài sản có nghĩa là để thay thế url-loader
, file-loader
và raw-loader
được sử dụng trong Webpack 4 cho tình huống này. Để mọi thứ hoạt động, chúng tôi sẽ yêu cầu Webpack luôn sao chép các tệp hình ảnh vào thư mục đầu ra:
module: {
rules: [
{
test: /.(png|jpg)$/i,
type: 'asset/resource'
}
]
}
Cuối cùng, webpack bao gồm hình ảnh trong đầu ra đã biên dịch:
Và trang của chúng tôi đang hoạt động:
Đặt tất cả hình ảnh trong thư mục riêng của chúng
Webpack đang sao chép các tệp hình ảnh của chúng tôi, nhưng tất cả các hình ảnh đều nằm trong thư mục gốc của thư mục đầu ra với các hàm băm không thể hiểu được cho tên. Bất kỳ hình ảnh nào khác và dist/
thư mục sẽ trở thành một mớ hỗn độn. Chúng tôi có thể yêu cầu mô-đun nội dung đặt tên cho hình ảnh của chúng tôi tốt hơn và đặt chúng vào thư mục riêng của chúng:
{
test: /.(png|jpg)$/i,
type: 'asset/resource'
// Added:
generator: {
filename: 'images/[name]-[hash][ext]'
}
}
Bây giờ tất cả các hình ảnh đều nằm trong một thư mục riêng biệt với những cái tên dễ hiểu. Giữ mã băm giúp xử lý chặn bộ nhớ cache:
Tự động làm sạch thư mục đầu ra của Webpack
Tại sao thư mục phân phối của tôi rất lộn xộn? Vì cấu hình webpack đã thay đổi, chúng tôi đã xóa thủ công các tệp cũ khỏi dist/
danh mục. Theo mặc định, Webpack không bao giờ xóa các tệp cũ không còn cần thiết nữa. Chúng ta có thể cấu hình Webpack để tự động dọn dẹp thư mục dist trong mỗi bản dựng:
output: {
// ...snip...
clean: true
},
Hình ảnh nhỏ trong dòng
Cuối cùng, hình ảnh đang hoạt động và chúng tôi có thể làm những gì chúng tôi đến đây: hình ảnh nhỏ nội tuyến! Cơ sở của Webpack asset
Mô-đun tài sản tự động xử lý nội tuyến cho chúng tôi. Webpack sẽ nội tuyến bất kỳ thứ gì dưới 8KB theo mặc định, nhưng chúng tôi cũng có thể đặt ngưỡng kích thước một cách rõ ràng. Hình ảnh vượt quá giới hạn sẽ được xuất vào dist/
thư mục như trước đây:
module: {
rules: [
// ...snip...
{
test: /.(png|jpg)$/i,
// Previously we had disabled inlining by using 'asset/resource'
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // Inline images under 10KB
}
},
generator: {
filename: 'images/[name]-[hash][ext]'
}
}
]
}
Các hình ảnh nhỏ hơn được nội dòng và thư mục đầu ra chỉ chứa các hình ảnh lớn hơn:
Chúng tôi có thể thấy các hình ảnh được mã hóa Base64 nếu chúng tôi kiểm tra trang được kết xuất:
TLDR: Cấu hình Webpack cuối cùng
Giờ đây, chúng tôi đã có Webpack tự động nội tuyến hình ảnh cùng với một số cải tiến về chất lượng cuộc sống. Với mọi thứ hoạt động, cấu hình webpack của chúng tôi trông giống như sau:
var path = require("path");
module.exports = {
mode: "development",
entry: "./src/index.jsx",
output: {
filename: "index.js",
path: path.resolve("dist/"),
clean: true
},
module: {
rules: [
{
test: /.jsx?$/,
loader: "babel-loader",
options: {
"presets": ["@babel/preset-react"]
}
},
{
test: /.html$/i,
type: "asset/resource",
generator: {
filename: "[name][ext]"
}
},
{
test: /.(png|jpg)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024
}
},
generator: {
filename: 'images/[name]-[hash][ext]'
}
}
]
}
};
Kết luận
Chúng tôi đã thuyết phục thành công Webpack tự động nội tuyến hình ảnh của chúng tôi. Điều này làm giảm số lượng yêu cầu mạng mà trang của chúng tôi cần thực hiện, nhưng nó có làm cho trang của chúng tôi nhanh hơn không? Đó là loại câu hỏi Số liệu yêu cầu được xây dựng để trả lời. Dùng thử ngay hôm nay để đo lường hiệu quả hoạt động của trang web đối với người dùng thực trong quá trình sản xuất.
Ở đây, chúng tôi chỉ đề cập đến một cách để tối ưu hóa hình ảnh, nhưng có nhiều cách khác để tối ưu hóa hiệu suất hình ảnh.