ปรับปรุงประสิทธิภาพโดยการเปิดทรัพยากร Dependency และเอาต์พุต JavaScript ที่ทันสมัย
เบราว์เซอร์กว่า 90% สามารถเรียกใช้ JavaScript ที่ทันสมัย แต่ ความแพร่หลายของ JavaScript แบบเดิมยังคงเป็นแหล่งที่มาขนาดใหญ่ของปัญหาด้านประสิทธิภาพ บนเว็บวันนี้
JavaScript สมัยใหม่
JavaScript สมัยใหม่ไม่มีลักษณะเป็นโค้ดที่เขียนด้วย ECMAScript ที่เฉพาะเจาะจง เวอร์ชันที่กำหนด แต่อยู่ในรูปแบบไวยากรณ์ที่สนับสนุนโดยรุ่นสมัยใหม่ทั้งหมด เบราว์เซอร์ เว็บเบราว์เซอร์สมัยใหม่ เช่น Chrome, Edge, Firefox และ Safari กว่า 90% ของตลาดเบราว์เซอร์ และ เบราว์เซอร์ต่างๆ ที่อาศัยเครื่องมือแสดงผลพื้นฐานเดียวกันจะประกอบเป็น เพิ่มเติมอีก 5% ซึ่งหมายความว่า 95% ของการเข้าชมเว็บทั่วโลกมาจากเบราว์เซอร์ ที่รองรับคุณลักษณะภาษา JavaScript ที่ใช้อย่างแพร่หลายที่สุดตั้งแต่ ปี รวมถึง:
- หมวดหมู่ (ES2015)
- ฟังก์ชันลูกศร (ES2015)
- เครื่องกำเนิดไฟฟ้า (ES2015)
- ขอบเขตของการบล็อก (ES2015)
- การทำลายโครงสร้าง (ES2015)
- พารามิเตอร์ส่วนที่เหลือและสเปรด (ES2015)
- ชวเลขของออบเจ็กต์ (ES2015)
- ไม่พร้อมกัน/รอ (ES2017)
โดยทั่วไปแล้วฟีเจอร์ในข้อกำหนดภาษาเวอร์ชันใหม่จะมีข้อกำหนดน้อยกว่า การสนับสนุนที่สอดคล้องกันในเบราว์เซอร์สมัยใหม่ ตัวอย่างเช่น หลายรายการ ES2020 และ ES2021 ได้รับการสนับสนุนใน 70% ของตลาดเบราว์เซอร์เท่านั้น และยังคงโดยส่วนใหญ่ เบราว์เซอร์แต่ยังไม่เพียงพอที่จะใช้งานคุณลักษณะเหล่านั้นโดยตรงได้อย่างปลอดภัย ช่วงเวลานี้ หมายความว่าแม้ "สมัยใหม่" JavaScript กำลังเดินหน้ากำหนดเป้าหมาย โดย ES2017 ได้ ความสามารถในการใช้งานร่วมกันของเบราว์เซอร์ที่หลากหลายที่สุด ในขณะเดียวกันก็มีฟีเจอร์ไวยากรณ์สมัยใหม่ที่ใช้กันโดยทั่วไปด้วย กล่าวคือ ES2017 ใกล้เคียงกับไวยากรณ์สมัยใหม่มากที่สุดในปัจจุบัน
JavaScript เดิม
JavaScript แบบเดิมคือโค้ดที่หลีกเลี่ยงการใช้ภาษาข้างต้นทั้งหมดโดยเฉพาะ ใหม่ๆ นักพัฒนาซอฟต์แวร์ส่วนใหญ่เขียนซอร์สโค้ดโดยใช้ไวยากรณ์สมัยใหม่ แต่ รวบรวมทุกอย่างเป็นไวยากรณ์แบบเดิมเพื่อเพิ่มการรองรับเบราว์เซอร์ กำลังคอมไพล์ ไวยากรณ์เดิมจะเพิ่มการสนับสนุนเบราว์เซอร์ แต่ผลกระทบมักจะ น้อยกว่าที่เราคิด ในหลายกรณี การสนับสนุนจะเพิ่มขึ้นจากประมาณ 95% ถึง 98% ในขณะที่มีค่าใช้จ่ายสูง:
โดยปกติแล้ว JavaScript แบบเดิมมีขนาดใหญ่กว่าและช้ากว่าประมาณ 20% รหัสสมัยใหม่ที่เทียบเท่ากัน ข้อบกพร่องของเครื่องมือและการกำหนดค่าที่ไม่ถูกต้องบ่อยครั้ง ขยายช่องว่างนี้ให้กว้างขึ้นอีก
ไลบรารีที่ติดตั้งคิดเป็น 90% ของเวอร์ชันที่ใช้งานจริงทั่วไป โค้ด JavaScript โค้ดไลบรารีต้องใช้ JavaScript แบบเดิมสูงขึ้นไปอีก ค่าใช้จ่ายจาก Polyfill และความซ้ำซ้อนของตัวช่วยที่ควรหลีกเลี่ยงได้ โดยการเผยแพร่โค้ดสมัยใหม่
JavaScript สมัยใหม่บน npm
เมื่อเร็วๆ นี้ Node.js ได้ทำให้ฟิลด์ "exports"
เป็นมาตรฐานเพื่อกำหนด
จุดแรกเข้าสำหรับแพ็กเกจ
{
"exports": "./index.js"
}
โมดูลที่อ้างอิงโดยช่อง "exports"
หมายถึงเวอร์ชันโหนดอย่างน้อย
12.8 ซึ่งรองรับ ES2019 ซึ่งหมายความว่าโมดูลใดก็ตามที่อ้างอิงโดยใช้
ฟิลด์ "exports"
เขียนด้วย JavaScript สมัยใหม่ได้ ผู้บริโภคที่ใช้แพ็กเกจต้อง
จะถือว่าโมดูลที่มีช่อง "exports"
มีโค้ดสมัยใหม่และเปลี่ยนรูปแบบหาก
ตามความจำเป็น
ทันสมัยเท่านั้น
หากคุณต้องการเผยแพร่แพ็กเกจด้วยโค้ดสมัยใหม่และปล่อยให้เป็น
ที่จะจัดการกับการเปลี่ยนรูปแบบเมื่อใช้เซิร์ฟเวอร์เป็นทรัพยากร Dependency ให้ใช้เฉพาะ
"exports"
{
"name": "foo",
"exports": "./modern.js"
}
ทันสมัยพร้อมรายการสำรองแบบเดิม
ใช้ช่อง "exports"
ร่วมกับ "main"
เพื่อเผยแพร่แพ็กเกจ
โดยใช้โค้ดสมัยใหม่ และยังมี ES5 + CommonJS สำรอง สำหรับรุ่นเดิม
เบราว์เซอร์
{
"name": "foo",
"exports": "./modern.js",
"main": "./legacy.cjs"
}
ความทันสมัยด้วยทางเลือกเดิมและการเพิ่มประสิทธิภาพ Bundler ด้าน ESM
นอกจากการกำหนดจุดแรกเข้า CommonJS สำรองแล้ว ฟิลด์ "module"
ยังสามารถ
เพื่อชี้ไปยังแพ็กเกจวิดีโอสำรองแบบเดิมที่คล้ายกัน แต่เป็นชุดที่ใช้
ไวยากรณ์โมดูล JavaScript (import
และ export
)
{
"name": "foo",
"exports": "./modern.js",
"main": "./legacy.cjs",
"module": "./module.js"
}
Bundler จำนวนมาก เช่น Webpack และ Rollup อาศัยช่องนี้เพื่อใช้ประโยชน์
คุณลักษณะโมดูลและเปิดใช้
การสั่นสะเทือนของต้นไม้
นี่ยังคงเป็นแพ็กเกจเดิมที่ไม่มีโค้ดสมัยใหม่นอกเหนือจาก
import
/export
ดังนั้นให้ใช้แนวทางนี้เพื่อจัดส่งโค้ดสมัยใหม่ที่มี
วิดีโอสำรองรุ่นเดิมที่ยังคงได้รับการเพิ่มประสิทธิภาพสำหรับการรวมกลุ่ม
JavaScript สมัยใหม่ในแอปพลิเคชัน
ทรัพยากร Dependency ของบุคคลที่สามเป็นการใช้งานจริงส่วนใหญ่ของเวอร์ชันที่ใช้งานจริง โค้ด JavaScript ในเว็บแอปพลิเคชัน แม้ว่าที่ผ่านมาทรัพยากร Dependency ของ npm จะ ได้รับการเผยแพร่เป็นไวยากรณ์ ES5 แบบเดิมแล้ว นี่ไม่ใช่สมมติฐานที่ปลอดภัยอีกต่อไปและ ความเสี่ยง การอัปเดตการพึ่งพิงทำให้การสนับสนุนเบราว์เซอร์ในแอปพลิเคชันของคุณเสียหาย
ด้วยจำนวนแพ็กเกจ npm ที่เปลี่ยนมาใช้ JavaScript สมัยใหม่ คือตรวจสอบว่าได้ตั้งค่าเครื่องมือสร้าง ไว้เพื่อจัดการ มี มีโอกาสมาก แพ็กเกจ NPM บางรายการที่คุณใช้อยู่แล้ว ฟีเจอร์ภาษาต่างๆ มีตัวเลือกมากมายสำหรับใช้รหัสสมัยใหม่ จาก npm โดยไม่ทำให้แอปพลิเคชันของคุณเสียหาย ในเบราว์เซอร์รุ่นเก่า แต่ แนวคิดคือให้ระบบบิลด์เปลี่ยนทรัพยากร Dependency เป็นไวยากรณ์เดียวกัน เป็นซอร์สโค้ดของคุณ
Webpack
สำหรับ Webpack 5 ตอนนี้คุณสามารถกำหนดค่าไวยากรณ์ Webpack ที่จะใช้ได้แล้ว เมื่อสร้างโค้ดสำหรับแพ็กเกจและโมดูล การดำเนินการนี้จะไม่เปลี่ยนรูปแบบ ก็จะส่งผลต่อ "Glue" เท่านั้น ที่ Webpack สร้างขึ้น หากต้องการระบุเป้าหมายการสนับสนุนเบราว์เซอร์ ให้เพิ่ม การกำหนดค่ารายการเบราว์เซอร์ กับโปรเจ็กต์ของคุณ หรือทำโดยตรงในการกำหนดค่า Webpack ดังนี้
module.exports = {
target: ['web', 'es2017'],
};
นอกจากนี้ ยังกำหนดค่า Webpack เพื่อสร้าง Bundle ที่เพิ่มประสิทธิภาพซึ่ง
ละเว้นฟังก์ชัน Wrapper ที่ไม่จำเป็นเมื่อกำหนดเป้าหมายโมดูล ES สมัยใหม่
ของคุณ การดำเนินการนี้ยังกำหนดค่า Webpack ให้โหลดแพ็กเกจที่แยกโค้ดโดยใช้
<script type="module">
module.exports = {
target: ['web', 'es2017'],
output: {
module: true,
},
experiments: {
outputModule: true,
},
};
มีปลั๊กอิน Webpack จำนวนมากที่ช่วยให้สามารถ คอมไพล์และส่ง JavaScript ที่ทันสมัย ขณะที่ยังคงสนับสนุนเบราว์เซอร์รุ่นเก่า เช่น ปลั๊กอิน Optimize และ BabelEsmPlugin
ปลั๊กอิน Optimize
ปลั๊กอิน Optimize เป็นเว็บแพ็ก ปลั๊กอินที่เปลี่ยนรูปแบบโค้ดชุดสุดท้ายจาก JavaScript สมัยใหม่เป็นแบบเดิม แทนที่จะเป็นไฟล์ต้นฉบับแต่ละไฟล์ เครื่องมือนี้เป็นการตั้งค่าในตัวที่ช่วยให้ การกำหนดค่า Webpack เพื่อให้ทุกอย่างเป็น JavaScript ที่ทันสมัยโดยไม่มี การแตกกิ่งแบบพิเศษสำหรับเอาต์พุตหรือไวยากรณ์หลายรายการ
เนื่องจากปลั๊กอิน Optimize ทำงานในรูปแบบชุดแทนที่จะเป็นโมดูลแต่ละโมดูล จะประมวลผลโค้ดของแอปพลิเคชันและทรัพยากร Dependency ได้เท่ากัน จึงทำให้ สามารถใช้ทรัพยากร Dependency ของ JavaScript สมัยใหม่จาก npm ได้ เนื่องจากโค้ดจะถูก กลุ่มและแปลงเป็นไวยากรณ์ที่ถูกต้อง และยังเร็วกว่า วิธีแก้ปัญหาแบบดั้งเดิมที่มี 2 ขั้นตอนในการรวบรวม ในขณะที่ยังคงสร้าง สำหรับเบราว์เซอร์สมัยใหม่และเบราว์เซอร์รุ่นเก่าแยกกัน แพ็กเกจทั้ง 2 ชุด ได้แก่ ออกแบบมาเพื่อให้โหลดโดยใช้ รูปแบบโมดูล/ไม่มีโมดูล
// webpack.config.js
const OptimizePlugin = require('optimize-plugin');
module.exports = {
// ...
plugins: [new OptimizePlugin()],
};
Optimize Plugin
รวดเร็วและมีประสิทธิภาพมากกว่า Webpack ที่กำหนดเอง
ซึ่งโดยทั่วไปจะรวมโค้ดสมัยใหม่กับโค้ดเดิมแยกกัน ทั้งนี้
ยังจัดการการเรียกใช้ Babel ให้กับคุณ และลดขนาด
แพ็กเกจที่ใช้ Terser ซึ่งมีการตั้งค่าที่เหมาะสมแยกกันสำหรับ
ทั้งสมัยใหม่และแบบเดิม สุดท้าย โพลีฟิลล์ที่เครื่องมือสร้าง
ระบบจะแยก Bundle เดิมออกมาเป็นสคริปต์เฉพาะ เพื่อไม่ให้มี
ซ้ำซ้อนหรือโหลดโดยไม่จำเป็นในเบราว์เซอร์รุ่นใหม่
BabelEsmPlugin
BabelEsmPlugin เป็นเว็บแพ็ก ปลั๊กอินที่ทำงานร่วมกับ @babel/preset-env สร้าง Bundle ที่มีอยู่เป็นเวอร์ชันที่ทันสมัย เพื่อให้จัดส่งโค้ดที่มีการเปลี่ยนแปลงน้อยกว่า เบราว์เซอร์ที่ทันสมัย นี่เป็นโซลูชันที่ได้รับความนิยมมากที่สุดสำหรับ โมดูล/nomodule ใช้โดย Next.js และ Preact 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
รองรับการกำหนดค่า Webpack ที่หลากหลายเนื่องจาก
เรียกใช้แอปพลิเคชันของคุณแยกกัน 2 บิลด์ส่วนใหญ่ การคอมไพล์ 2 ครั้งอาจใช้เวลานาน
สำหรับแอปพลิเคชันขนาดใหญ่ โดยใช้เวลาเล็กน้อย แต่เทคนิคนี้
BabelEsmPlugin
เพื่อผสานรวมเข้ากับการกำหนดค่า Webpack ที่มีอยู่อย่างราบรื่น
และทำให้โมเดลนี้เป็นหนึ่งใน
ตัวเลือกที่สะดวกที่สุด
กำหนดค่า Babel-loader เพื่อแปลง Node_modules
หากคุณใช้ babel-loader
โดยไม่มีปลั๊กอินใดปลั๊กอินหนึ่งจาก 2 รายการก่อนหน้านี้
จะมีขั้นตอนสำคัญที่จำเป็นในการใช้ NPM ของ JavaScript ที่ทันสมัย
โมดูล การกำหนดการกําหนดค่า babel-loader
ที่แยกกัน 2 รายการช่วยให้เป็นไปได้
เพื่อรวบรวมฟีเจอร์ภาษาสมัยใหม่ที่พบใน node_modules
โดยอัตโนมัติ
ES2017 ขณะที่ยังคงเปลี่ยนรูปแบบโค้ดของบุคคลที่หนึ่งของคุณเองด้วย Babel
ปลั๊กอินและค่าที่กำหนดล่วงหน้าที่กำหนดไว้ในการกำหนดค่าของโปรเจ็กต์ การดำเนินการนี้จะไม่
สร้างแพ็กเกจที่ทันสมัยและแบบเดิมสำหรับการตั้งค่าโมดูล/ไม่มีโมดูล แต่กลับมี
ทำให้สามารถติดตั้งและใช้แพ็กเกจ npm ที่มี JavaScript ที่ทันสมัย
โดยไม่ทำให้เบราว์เซอร์รุ่นเก่าเสียหาย
webpack-plugin-modern-npm
ใช้เทคนิคนี้เพื่อรวบรวมทรัพยากร Dependency ของ npm ที่มีฟิลด์ "exports"
ใน package.json
ของตน เนื่องจากอาจมีไวยากรณ์สมัยใหม่:
// webpack.config.js
const ModernNpmPlugin = require('webpack-plugin-modern-npm');
module.exports = {
plugins: [
// auto-transpile modern stuff found in node_modules
new ModernNpmPlugin(),
],
};
หรือจะใช้เทคนิคด้วยตนเองใน Webpack ก็ได้
โดยตรวจหาช่อง "exports"
ใน package.json
ของ
เมื่อแก้ไขเรียบร้อยแล้ว การไม่ใส่แคชเพื่อความกระชับ
การใช้งานอาจมีลักษณะเช่นนี้
// 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'],
},
},
},
],
},
};
เมื่อใช้วิธีการนี้ คุณต้องตรวจสอบว่าไวยากรณ์สมัยใหม่ได้รับการสนับสนุนโดย
เครื่องมือลดขนาดของคุณ ทั้ง Terser
และ uglify-es
มีตัวเลือกในการระบุ {ecma: 2017}
เพื่อเก็บรักษา และในบางกรณี
สร้างไวยากรณ์ ES2017 ระหว่างการบีบอัดและการจัดรูปแบบ
ภาพรวม
ภาพรวมมีการสนับสนุนในตัวสำหรับการสร้างแพ็กเกจหลายชุดเป็นส่วนหนึ่งของ บิลด์เดียว และสร้างโค้ดสมัยใหม่โดยค่าเริ่มต้น ด้วยเหตุนี้ ภาพรวมอาจ มีการกำหนดค่าให้สร้าง Bundle ที่ทันสมัยและแบบเดิมที่มีปลั๊กอินอย่างเป็นทางการ คุณน่าจะใช้อยู่แล้ว
@rollup/plugin-babel
หากคุณใช้ "ภาพรวม" ฟิลด์
getBabelOutputPlugin()
วิธี
(โดย
ปลั๊กอิน Babel อย่างเป็นทางการ)
แปลงโค้ดในแพ็กเกจที่สร้างขึ้นแทนที่จะเป็นโมดูลแหล่งที่มาแต่ละรายการ
ภาพรวมมีการสนับสนุนในตัวสำหรับการสร้างแพ็กเกจหลายชุดเป็นส่วนหนึ่งของ
เพียงบิลด์เดียว และแต่ละงานมีปลั๊กอินของตนเอง คุณใช้เครื่องมือนี้เพื่อสร้าง
แพ็กเกจต่างๆ ทั้งสมัยใหม่และแบบเดิมโดยส่งแต่ละแพ็กเกจ
การกำหนดค่าปลั๊กอินเอาต์พุต Babel:
// 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'],
}),
],
},
],
};
เครื่องมือสร้างเพิ่มเติม
ภาพรวมและเว็บแพ็กสามารถกำหนดค่าได้สูง ซึ่งโดยทั่วไปหมายความว่าแต่ละโปรเจ็กต์ ต้องอัปเดตการกำหนดค่าเพื่อเปิดใช้ไวยากรณ์ JavaScript สมัยใหม่ในทรัพยากร Dependency นอกจากนี้ยังมีเครื่องมือสร้างระดับสูงขึ้นที่ยึดตามแบบแผนและค่าเริ่มต้นมากกว่า เช่น Parcel, Snowpack, Vite และ WMR เครื่องมือเหล่านี้ส่วนใหญ่ จะถือว่าทรัพยากร Dependency ของ npm อาจมีไวยากรณ์แบบใหม่ และจะแปลงเป็น ระดับไวยากรณ์ที่เหมาะสมเมื่อสร้าง สำหรับเวอร์ชันที่ใช้งานจริง
นอกเหนือจากปลั๊กอินเฉพาะสำหรับ Webpack และ Rollup แล้ว JavaScript ที่ทันสมัย สามารถเพิ่มแพ็กเกจที่มีวิดีโอสำรองแบบเดิมลงในโปรเจ็กต์ใดก็ได้โดยใช้ devolution การปฏิวัติเป็น เครื่องมือแบบสแตนด์อโลนที่แปลงเอาต์พุตจากระบบบิลด์เพื่อสร้างผลงานเก่า ตัวแปร JavaScript ทำให้การรวมกลุ่มและการแปลงอยู่ในรูปแบบของ เป้าหมายเอาต์พุต