Cải thiện hiệu suất bằng cách bật các phần phụ thuộc và đầu ra JavaScript hiện đại.
Hơn 90% trình duyệt có khả năng chạy JavaScript hiện đại, nhưng sự phổ biến của JavaScript cũ vẫn là một nguồn lớn gây ra các vấn đề về hiệu suất trên web ngay hôm nay.
JavaScript hiện đại
JavaScript hiện đại không có đặc điểm là mã được viết bằng một ECMAScript cụ thể phiên bản chỉ dẫn kỹ thuật mà là trong cú pháp được hỗ trợ bởi trình duyệt. Các trình duyệt web hiện đại như Chrome, Edge, Firefox và Safari tạo thành hơn 90% thị trường trình duyệt và các trình duyệt khác nhau dựa vào cùng một công cụ kết xuất cơ bản tạo nên 5%. Điều này có nghĩa là 95% lưu lượng truy cập web trên toàn cầu đến từ các trình duyệt hỗ trợ các tính năng ngôn ngữ JavaScript được sử dụng rộng rãi nhất trong 10 năm, bao gồm:
- Lớp (ES2015)
- Các hàm mũi tên (ES2015)
- Máy phát điện (ES2015)
- Phạm vi khối (ES2015)
- Phá vỡ cấu trúc (ES2015)
- Tham số nghỉ và trải rộng (ES2015)
- Viết tắt đối tượng (ES2015)
- Không đồng bộ/đang chờ (ES2017)
Các tính năng trong phiên bản mới hơn của thông số kỹ thuật ngôn ngữ thường có ít hỗ trợ nhất quán trên các trình duyệt hiện đại. Ví dụ: nhiều ES2020 và ES2021 các tính năng mới chỉ được hỗ trợ tại 70% thị trường trình duyệt—vẫn còn phần lớn trình duyệt, nhưng không đủ an toàn để dựa trực tiếp vào các tính năng đó. Chiến dịch này có nghĩa là mặc dù quảng cáo "hiện đại" JavaScript là một mục tiêu luôn thay đổi, ES2017 có khả năng tương thích rộng nhất với trình duyệt trong khi bao gồm hầu hết tính năng cú pháp hiện đại thường dùng. Nói cách khác, ES2017 là cú pháp gần giống nhất với cú pháp hiện đại hiện nay.
JavaScript cũ
JavaScript cũ là mã giúp tránh sử dụng tất cả ngôn ngữ trên các tính năng AI mới. Hầu hết các nhà phát triển đều viết mã nguồn bằng cú pháp hiện đại, nhưng biên dịch mọi thứ thành cú pháp cũ để tăng cường hỗ trợ trình duyệt. Biên dịch cú pháp cũ giúp tăng khả năng hỗ trợ của trình duyệt, tuy nhiên hiệu quả này thường nhỏ hơn chúng ta có thể nghĩ. Trong nhiều trường hợp, mức hỗ trợ tăng từ khoảng 95% lên 98%, đồng thời phải chịu một khoản chi phí đáng kể:
JavaScript cũ thường lớn hơn và chậm hơn khoảng 20% so với mã hiện đại tương đương. Lỗi công cụ và cấu hình sai thường xuyên giúp mở rộng khoảng cách này hơn nữa.
Các thư viện đã cài đặt chiếm đến 90% bản phát hành thông thường Mã JavaScript. Mã thư viện phát sinh JavaScript cũ thậm chí cao hơn mức hao tổn do có thể tránh được polyfill và trùng lặp trình trợ giúp bằng cách xuất bản mã hiện đại.
JavaScript hiện đại trên npm
Gần đây, Node.js đã chuẩn hoá một trường "exports"
để xác định
điểm truy cập cho gói:
{
"exports": "./index.js"
}
Các mô-đun được tham chiếu bởi trường "exports"
ngụ ý một phiên bản Nút ít nhất là
12.8, hỗ trợ ES2019. Điều này có nghĩa là bất kỳ mô-đun nào được tham chiếu bằng cách sử dụng
Trường "exports"
có thể được viết bằng JavaScript hiện đại. Người tiêu dùng gói phải
giả định các mô-đun có trường "exports"
chứa mã hiện đại và mã hoá nếu
nếu cần.
Chỉ hiện đại
Nếu bạn muốn xuất bản một gói có mã hiện đại và để tuỳ chọn này
để xử lý việc chuyển đổi mã khi họ sử dụng như một phần phụ thuộc—chỉ sử dụng
Trường "exports"
.
{
"name": "foo",
"exports": "./modern.js"
}
Hiện đại với tính năng dự phòng cũ
Sử dụng trường "exports"
cùng với "main"
để xuất bản gói
bằng cách sử dụng mã hiện đại nhưng cũng bao gồm tính năng dự phòng ES5 + CommonJS cho phiên bản cũ
trình duyệt.
{
"name": "foo",
"exports": "./modern.js",
"main": "./legacy.cjs"
}
Hiện đại với tính năng tối ưu hoá bộ gói ESM và tính năng dự phòng cũ
Ngoài việc xác định điểm truy cập CommonJS dự phòng, trường "module"
có thể
được dùng để trỏ đến một gói dự phòng cũ tương tự, nhưng là gói sử dụng
Cú pháp mô-đun JavaScript (import
và export
).
{
"name": "foo",
"exports": "./modern.js",
"main": "./legacy.cjs",
"module": "./module.js"
}
Nhiều trình gói dịch vụ, chẳng hạn như webpack và Rollup, dựa vào trường này để tận dụng
các tính năng của mô-đun và cho phép
cây rung lắc.
Đây vẫn là một gói cũ không chứa mã hiện đại nào ngoài
import
/export
, vì vậy, hãy sử dụng phương pháp này để gửi mã hiện đại bằng một
dự phòng cũ mà vẫn được tối ưu hoá để nhóm.
JavaScript hiện đại trong các ứng dụng
Các phần phụ thuộc của bên thứ ba chiếm phần lớn trong quy trình sản xuất thông thường Mã JavaScript trong ứng dụng web. Mặc dù các phần phụ thuộc npm trước đây đã được xuất bản dưới dạng cú pháp ES5 cũ, đây không còn là một giả định an toàn và dẫn đến rủi ro việc cập nhật phần phụ thuộc làm hỏng khả năng hỗ trợ trình duyệt trong ứng dụng của bạn.
Với số lượng gói npm ngày càng tăng được chuyển sang JavaScript hiện đại, bạn cần đảm bảo rằng công cụ xây dựng được thiết lập để xử lý các lỗi đó. Có một khả năng một số gói npm mà bạn phụ thuộc vào đã sử dụng các tính năng ngôn ngữ khác. Có một số cách để sử dụng mã hiện đại từ npm mà không phá vỡ ứng dụng của bạn trong các trình duyệt cũ hơn, nhưng việc xem chung ý tưởng là đảm bảo hệ thống xây dựng chuyển đổi các phần phụ thuộc thành cùng một cú pháp làm mã nguồn của mình.
gói web
Kể từ webpack 5, giờ đây bạn có thể định cấu hình cú pháp webpack sẽ sử dụng khi tạo mã cho gói và mô-đun. Điều này không chuyển mã của mã hoặc phần phụ thuộc, thì điều này chỉ ảnh hưởng đến "glue" mã do webpack tạo. Để chỉ định mục tiêu hỗ trợ trình duyệt, hãy thêm cấu hình danh sách trình duyệt vào dự án của bạn hoặc thực hiện trực tiếp trong cấu hình gói web:
module.exports = {
target: ['web', 'es2017'],
};
Bạn cũng có thể định cấu hình gói web để tạo các gói được tối ưu hoá
bỏ qua các hàm trình bao bọc không cần thiết khi nhắm đến các Mô-đun ES hiện đại
môi trường. Thao tác này cũng định cấu hình webpack để tải các gói phân tách mã bằng cách sử dụng
<script type="module">
.
module.exports = {
target: ['web', 'es2017'],
output: {
module: true,
},
experiments: {
outputModule: true,
},
};
Hiện có một số trình bổ trợ webpack giúp bạn có thể biên dịch và đưa JavaScript hiện đại vào trong khi vẫn hỗ trợ các trình duyệt cũ, chẳng hạn như Optimize Plugin và v0EsmPlugin.
Trình bổ trợ Optimize
Trình bổ trợ Optimize là một gói web trình bổ trợ biến đổi mã đi kèm cuối cùng từ JavaScript hiện đại sang JavaScript cũ thay vì từng tệp nguồn riêng lẻ. Đây là một thiết lập độc lập cho phép cấu hình gói web của bạn để giả định mọi thứ đều là JavaScript hiện đại, không có phân nhánh đặc biệt cho nhiều đầu ra hoặc cú pháp.
Vì Trình bổ trợ Optimize hoạt động trên các gói thay vì từng mô-đun riêng lẻ, nên trình bổ trợ này xử lý mã của ứng dụng và các phần phụ thuộc như nhau. Điều này giúp các phần phụ thuộc JavaScript hiện đại từ npm, vì mã của các phần phụ thuộc này sẽ được nhóm và chuyển đổi sang cú pháp chính xác. Cũng có thể nhanh hơn các giải pháp truyền thống bao gồm 2 bước biên dịch, mà vẫn tạo ra các gói riêng cho trình duyệt hiện đại và cũ. Hai tập hợp gói là được thiết kế để tải bằng cách sử dụng mô-đun/không mô-đun mẫu.
// webpack.config.js
const OptimizePlugin = require('optimize-plugin');
module.exports = {
// ...
plugins: [new OptimizePlugin()],
};
Optimize Plugin
có thể nhanh và hiệu quả hơn gói web tuỳ chỉnh
, thường là gói mã hiện đại và mã cũ một cách riêng biệt. Nó
cũng xử lý việc chạy Babel cho bạn và giảm kích thước
các gói bằng Terser với chế độ cài đặt tối ưu riêng cho
kết quả hiện đại và cũ. Cuối cùng, polyfill cần thiết cho
các gói cũ được trích xuất vào một tập lệnh chuyên dụng để chúng không bao giờ
trùng lặp hoặc được tải một cách không cần thiết trong các trình duyệt mới.
BabelEsmPlugin
BabelEsmPlugin là một gói web trình bổ trợ hoạt động cùng với @babel/preset-env để tạo phiên bản hiện đại của các gói hiện có nhằm gửi mã ít được dịch hơn đến các trình duyệt hiện đại. Đây là giải pháp phổ biến nhất có sẵn để module/nomodule, được Next.js sử dụng và Dự đoán CLI.
// webpack.config.js
const BabelEsmPlugin = require('babel-esm-plugin');
module.exports = {
//...
module: {
rules: [
// your existing babel-loader configuration:
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
],
},
plugins: [new BabelEsmPlugin()],
};
BabelEsmPlugin
hỗ trợ nhiều cấu hình gói web vì đây là
chạy hai bản dựng ứng dụng lớn riêng biệt. Việc biên dịch hai lần có thể mất
thêm một chút thời gian cho các ứng dụng lớn, tuy nhiên kỹ thuật này cho phép
BabelEsmPlugin
để tích hợp liền mạch vào các cấu hình webpack hiện có
và làm cho nó trở thành một trong những tuỳ chọn thuận tiện nhất hiện có.
Định cấu hình trình tải babel để chuyển đổi nút_modules
Nếu bạn đang sử dụng babel-loader
mà không có một trong hai trình bổ trợ trước,
bạn cần thực hiện một bước quan trọng để sử dụng npm JavaScript hiện đại
các mô-đun. Việc xác định hai cấu hình babel-loader
riêng biệt giúp bạn có thể
để tự động biên dịch các tính năng ngôn ngữ hiện đại có trong node_modules
thành
ES2017, trong khi vẫn chuyển đổi mã bên thứ nhất của riêng bạn bằng JDK
các trình bổ trợ và giá trị đặt trước được xác định trong cấu hình của dự án. Điều này không
tạo các gói hiện đại và cũ cho việc thiết lập mô-đun/không mô-đun, nhưng có
giúp bạn có thể cài đặt và sử dụng các gói npm chứa JavaScript hiện đại
mà không làm hỏng các trình duyệt cũ.
webpack-plugin-modern-npm
sử dụng kỹ thuật này để biên dịch các phần phụ thuộc npm có trường "exports"
trong package.json
, vì các mã này có thể chứa cú pháp hiện đại:
// webpack.config.js
const ModernNpmPlugin = require('webpack-plugin-modern-npm');
module.exports = {
plugins: [
// auto-transpile modern stuff found in node_modules
new ModernNpmPlugin(),
],
};
Ngoài ra, bạn có thể triển khai kỹ thuật này theo cách thủ công trong gói web của mình
bằng cách kiểm tra trường "exports"
trong package.json
các mô-đun khi chúng được giải quyết. Bỏ qua việc lưu vào bộ nhớ đệm để ngắn gọn, một tuỳ chỉnh
phương thức triển khai có thể giống như sau:
// webpack.config.js
module.exports = {
module: {
rules: [
// Transpile for your own first-party code:
{
test: /\.js$/i,
loader: 'babel-loader',
exclude: /node_modules/,
},
// Transpile modern dependencies:
{
test: /\.js$/i,
include(file) {
let dir = file.match(/^.*[/\\]node_modules[/\\](@.*?[/\\])?.*?[/\\]/);
try {
return dir && !!require(dir[0] + 'package.json').exports;
} catch (e) {}
},
use: {
loader: 'babel-loader',
options: {
babelrc: false,
configFile: false,
presets: ['@babel/preset-env'],
},
},
},
],
},
};
Khi dùng phương pháp này, bạn cần đảm bảo cú pháp hiện đại được hỗ trợ bởi
công cụ khai thác của bạn. Cả hai Terser
và uglify-es
có thể chỉ định {ecma: 2017}
để duy trì và trong một số trường hợp
tạo cú pháp ES2017 trong quá trình nén và định dạng.
Kết quả tổng hợp
Công cụ hợp nhất có sẵn tính năng hỗ trợ tạo nhiều nhóm gói trong một bản dựng duy nhất và tạo mã hiện đại theo mặc định. Do đó, Công cụ hợp nhất có thể được định cấu hình để tạo các gói hiện đại và cũ có trình bổ trợ chính thức mà bạn có thể đang sử dụng.
@rollup/plugin-babel
Nếu bạn sử dụng hợp nhất,
Phương thức getBabelOutputPlugin()
(được cung cấp bởi
trình bổ trợ JDK chính thức)
biến đổi mã trong các gói được tạo thay vì từng mô-đun nguồn.
Công cụ hợp nhất có sẵn tính năng hỗ trợ tạo nhiều nhóm gói trong
một bản dựng, mỗi bản dựng có trình bổ trợ riêng. Bạn có thể sử dụng công cụ này để tạo
các gói khác nhau
cho hiện đại và cũ
bằng cách truyền tải từng gói
Cấu hình trình bổ trợ đầu ra adb:
// rollup.config.js
import {getBabelOutputPlugin} from '@rollup/plugin-babel';
export default {
input: 'src/index.js',
output: [
// modern bundles:
{
format: 'es',
plugins: [
getBabelOutputPlugin({
presets: [
[
'@babel/preset-env',
{
targets: {esmodules: true},
bugfixes: true,
loose: true,
},
],
],
}),
],
},
// legacy (ES5) bundles:
{
format: 'amd',
entryFileNames: '[name].legacy.js',
chunkFileNames: '[name]-[hash].legacy.js',
plugins: [
getBabelOutputPlugin({
presets: ['@babel/preset-env'],
}),
],
},
],
};
Công cụ xây dựng bổ sung
Cuộn và webpack có cấu hình cao, thường nghĩa là mỗi dự án phải cập nhật cấu hình để bật cú pháp JavaScript hiện đại trong các phần phụ thuộc. Ngoài ra còn có các công cụ xây dựng cấp cao hơn ưu tiên quy ước và chế độ mặc định hơn như Parcel, Snowpack, Vite và kiện. Hầu hết các công cụ này giả định các phần phụ thuộc npm có thể chứa cú pháp hiện đại và sẽ chuyển đổi các phần đó thành (các) cấp cú pháp thích hợp khi tạo bản dựng cho phiên bản chính thức.
Ngoài các trình bổ trợ chuyên dụng cho webpack và Rollup, JavaScript hiện đại các gói có tính năng dự phòng cũ có thể được thêm vào bất kỳ dự án nào sử dụng phát triển. Quá trình phát triển là một công cụ độc lập biến đổi đầu ra từ một hệ thống xây dựng để tạo ra các sản phẩm cũ Các biến thể JavaScript, cho phép gói và chuyển đổi giả định mục tiêu đầu ra.