ใน Codelab นี้ คุณสามารถปรับปรุงประสิทธิภาพของแอปพลิเคชันที่เรียบง่ายนี้ ซึ่งอนุญาตให้ผู้ใช้ให้คะแนนแมวแบบสุ่ม ดูวิธีเพิ่มประสิทธิภาพแพ็กเกจ JavaScript โดยการลดจํานวนโค้ดที่มีการแปลง
ในแอปตัวอย่าง คุณสามารถเลือกคำหรืออีโมจิเพื่อบอกว่าคุณชอบแมวแต่ละตัวมากแค่ไหน เมื่อคลิกปุ่ม แอปจะแสดงค่าของปุ่มด้านล่างภาพแมวปัจจุบัน
วัดระยะทาง
ควรเริ่มต้นด้วยการตรวจสอบเว็บไซต์ก่อนที่จะเพิ่มการเพิ่มประสิทธิภาพ ดังนี้
- หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกด เต็มหน้าจอ
- กด "Control+Shift+J" (หรือ "Command+Option+J" ใน Mac) เพื่อเปิดเครื่องมือสำหรับนักพัฒนาเว็บ
- คลิกแท็บเครือข่าย
- เลือกช่องทำเครื่องหมายปิดใช้แคช
- โหลดแอปซ้ำ
แอปพลิเคชันนี้ใช้พื้นที่มากกว่า 80 KB เวลาตรวจสอบว่าไม่มีการใช้ส่วนต่างๆ ของแพ็กเกจหรือไม่
กด
Control+Shift+P
(หรือCommand+Shift+P
ใน Mac) เพื่อเปิดเมนู Commandป้อน
Show Coverage
แล้วกดEnter
เพื่อแสดงแท็บความครอบคลุมในแท็บการครอบคลุม ให้คลิกโหลดซ้ำเพื่อโหลดแอปพลิเคชันอีกครั้งขณะจับภาพการครอบคลุม
ลองดูจำนวนโค้ดที่ใช้เทียบกับจำนวนการโหลดสำหรับแพ็กเกจหลัก ดังนี้
ไม่มีการใช้งานไฟล์เกินกว่าครึ่ง (44 KB) เนื่องจากโค้ดจำนวนมากภายในประกอบด้วย Polyfill เพื่อให้มั่นใจได้ว่าแอปพลิเคชันจะทำงานในเบราว์เซอร์รุ่นเก่าได้
ใช้ @babel/preset-env
ไวยากรณ์ของภาษา JavaScript เป็นไปตามมาตรฐานที่เรียกว่า ECMAScript หรือ ECMA-262 ข้อกำหนดเวอร์ชันใหม่ จะเผยแพร่ทุกปีและมีฟีเจอร์ใหม่ๆ ที่ผ่านขั้นตอนข้อเสนอแล้ว เบราว์เซอร์หลักแต่ละเบราว์เซอร์จะมีระดับ การรองรับฟีเจอร์เหล่านี้แตกต่างกันไป
ฟีเจอร์ ES2015 ต่อไปนี้ถูกใช้ในแอปพลิเคชัน
รวมถึงใช้ฟีเจอร์ ES2017 ต่อไปนี้ด้วย
คุณเจาะลึกซอร์สโค้ดใน src/index.js
เพื่อดูการใช้โค้ดทั้งหมดนี้ได้อย่างอิสระ
คุณลักษณะเหล่านี้ทั้งหมดได้รับการรองรับใน Chrome เวอร์ชันล่าสุด แต่เบราว์เซอร์อื่นๆ ที่ไม่รองรับเบราว์เซอร์อื่นๆ ล่ะ Babel ซึ่งมีอยู่ในแอปพลิเคชันเป็นไลบรารียอดนิยมที่ใช้คอมไพล์โค้ดซึ่งมีไวยากรณ์ที่ใหม่กว่าเป็นโค้ดที่เบราว์เซอร์และสภาพแวดล้อมที่เก่ากว่าจะเข้าใจได้ โดยมี 2 วิธีคือ
- Polyfills มีไว้เพื่อจำลองฟังก์ชัน ES2015+ เวอร์ชันใหม่เพื่อให้ใช้ API ได้ แม้ว่าเบราว์เซอร์จะไม่รองรับก็ตาม ต่อไปนี้คือตัวอย่างของ polyfill ของเมธอด
Array.includes
- ปลั๊กอินใช้ในการเปลี่ยนรูปแบบโค้ด ES2015 (หรือใหม่กว่า) เป็นไวยากรณ์ ES5 รุ่นเก่า เนื่องจากการเปลี่ยนแปลงเหล่านี้เป็นการเปลี่ยนแปลงที่เกี่ยวข้องกับไวยากรณ์ (เช่น ฟังก์ชันลูกศร) จึงไม่สามารถจำลองด้วย Polyfill ได้
ดู package.json
ว่ามีห้องสมุด Babel ใดบ้าง
"dependencies": {
"@babel/polyfill": "^7.0.0"
},
"devDependencies": {
//...
"babel-loader": "^8.0.2",
"@babel/core": "^7.1.0",
"@babel/preset-env": "^7.1.0",
//...
}
@babel/core
เป็นคอมไพเลอร์หลักของ Babel ด้วยเหตุนี้ การกำหนดค่า Babel ทั้งหมดจึงได้รับการกำหนดไว้ใน.babelrc
ที่รูทของโปรเจ็กต์babel-loader
มี Babel ในกระบวนการสร้าง Webpack
ตอนนี้ดูที่ webpack.config.js
เพื่อดูว่าระบบรวม babel-loader
ไว้เป็นกฎอย่างไร
module: { rules: [ //... { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } ] },
@babel/polyfill
มี Polyfill ที่จําเป็นทั้งหมดสําหรับฟีเจอร์ ECMAScript ใหม่ๆ เพื่อให้ทํางานได้ในสภาพแวดล้อมที่ไม่รองรับฟีเจอร์ดังกล่าว นำเข้าไว้ที่ตำแหน่งบนสุดของsrc/index.js.
แล้ว
import "./style.css";
import "@babel/polyfill";
@babel/preset-env
จะระบุว่าการเปลี่ยนรูปแบบและ Polyfill รายการใดที่จำเป็นสำหรับเบราว์เซอร์หรือสภาพแวดล้อมที่เลือกเป็นเป้าหมาย
ลองดูไฟล์การกำหนดค่า Babel ชื่อ .babelrc
ว่ามีการรวมไฟล์ดังกล่าวไว้อย่างไร
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions"
}
]
]
}
นี่คือการตั้งค่า Babel และ Webpack ดูวิธีรวม Babel ในแอปพลิเคชันของคุณหากคุณใช้ Bundler โมดูลที่ต่างจาก Webpack
แอตทริบิวต์ targets
ใน .babelrc
จะระบุเบราว์เซอร์ที่กำลังกำหนดเป้าหมาย @babel/preset-env
ผสานรวมกับรายการเบราว์เซอร์ ซึ่งหมายความว่าคุณดูรายการคำค้นหาทั้งหมดที่เข้ากันได้ซึ่งจะใช้ได้ในช่องนี้ในเอกสารประกอบเกี่ยวกับรายการเบราว์เซอร์
ค่า "last 2 versions"
จะแปลงโค้ดในแอปพลิเคชันสำหรับ 2 เวอร์ชันล่าสุดของแต่ละเบราว์เซอร์
การแก้ไขข้อบกพร่อง
หากต้องการดูเป้าหมาย Babel ทั้งหมดของเบราว์เซอร์ รวมถึงการเปลี่ยนรูปแบบและโพลีฟิลล์ทั้งหมดที่รวมอยู่ด้วย ให้เพิ่มช่อง debug
ลงใน .babelrc:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"debug": true
}
]
]
}
- คลิกเครื่องมือ
- คลิกบันทึก
โหลดแอปพลิเคชันซ้ำแล้วดูที่บันทึกสถานะของ Glitch ที่ด้านล่างตัวแก้ไข
เบราว์เซอร์เป้าหมาย
Babel บันทึกรายละเอียดจำนวนมากเกี่ยวกับกระบวนการคอมไพล์ลงในคอนโซล รวมถึงสภาพแวดล้อมเป้าหมายทั้งหมดที่มีการรวบรวมโค้ดไว้
ให้สังเกตว่ามีเบราว์เซอร์ที่ปิดให้บริการแล้ว เช่น Internet Explorer รวมอยู่ในรายการนี้อย่างไร ปัญหานี้เป็นเพราะเบราว์เซอร์ที่ไม่รองรับจะไม่มีการเพิ่มฟีเจอร์ใหม่ๆ และ Babel ยังคงส่งต่อไวยากรณ์ที่เฉพาะเจาะจงสำหรับเบราว์เซอร์ดังกล่าวต่อไป ซึ่งจะเพิ่มขนาดของแพ็กเกจโดยไม่จำเป็นหากผู้ใช้ไม่ได้ใช้เบราว์เซอร์นี้เพื่อเข้าถึงเว็บไซต์ของคุณ
Babel ยังบันทึกรายการปลั๊กอินการแปลงที่ใช้ ดังนี้
เรามีข้อมูลจำนวนมาก นี่คือปลั๊กอินทั้งหมดที่ Babel ต้องใช้เพื่อเปลี่ยนไวยากรณ์ ES2015+ เป็นไวยากรณ์เวอร์ชันเก่าสำหรับเบราว์เซอร์เป้าหมายทั้งหมด
อย่างไรก็ตาม Babel ไม่ได้แสดงโพลีฟิลล์เฉพาะเจาะจงที่ใช้
เนื่องจากระบบนำเข้า @babel/polyfill
ทั้งหมดโดยตรง
โหลด Polyfill ทีละรายการ
โดยค่าเริ่มต้น Babel จะมี Polyfill ทุกรายการที่จำเป็นสำหรับสภาพแวดล้อม ES2015+ ที่สมบูรณ์เมื่อนำเข้า @babel/polyfill
ลงในไฟล์ หากต้องการนำเข้าโพลีฟิลล์ที่เฉพาะเจาะจงซึ่งจำเป็นสำหรับเบราว์เซอร์เป้าหมาย ให้เพิ่ม useBuiltIns: 'entry'
ลงในการกำหนดค่า
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"debug": true
"useBuiltIns": "entry"
}
]
]
}
โหลดแอปพลิเคชันซ้ำ จากนั้นคุณจะเห็นโพลีฟิลล์ที่เฉพาะเจาะจงทั้งหมดที่รวมอยู่ในรายการต่อไปนี้
แม้ว่าตอนนี้จะรวมเฉพาะ Polyfill ที่จำเป็นสำหรับ "last 2 versions"
แต่ก็ยังมีรายการจำนวนมากอยู่ ทั้งนี้เนื่องจากโพลีฟิลล์ที่จำเป็นสำหรับเบราว์เซอร์เป้าหมายสำหรับฟีเจอร์ใหม่ทั้งหมดจะยังคงรวมอยู่ด้วย เปลี่ยนค่าของแอตทริบิวต์เป็น usage
เพื่อรวมเฉพาะค่าที่จำเป็นสำหรับฟีเจอร์ที่ใช้ในโค้ด
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"debug": true,
"useBuiltIns": "entry"
"useBuiltIns": "usage"
}
]
]
}
วิธีนี้ทำให้ระบบรวม Polyfill โดยอัตโนมัติตามความจำเป็น
ซึ่งหมายความว่าคุณสามารถนำการนำเข้า @babel/polyfill
ออกจาก src/index.js.
ได้
import "./style.css";
import "@babel/polyfill";
โดยตอนนี้ระบบจะรวมเฉพาะ Polyfill ที่จำเป็นสำหรับแอปพลิเคชันเท่านั้น
ขนาดของแพ็กเกจแอปพลิเคชันจะลดลงอย่างมาก
จำกัดรายการเบราว์เซอร์ที่รองรับให้แคบลง
จำนวนเป้าหมายเบราว์เซอร์ที่รวมไว้มีค่อนข้างมาก และมีผู้ใช้ไม่มากนักที่ใช้เบราว์เซอร์ที่ปิดให้บริการแล้ว เช่น Internet Explorer อัปเดตการกำหนดค่าเป็นดังนี้
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"targets": [">0.25%", "not ie 11"],
"debug": true,
"useBuiltIns": "usage",
}
]
]
}
โปรดดูรายละเอียดของ Bundle ที่ดึงข้อมูลมา
เนื่องจากแอปพลิเคชันมีขนาดเล็กมาก จึงไม่มีความแตกต่างมากนัก
กับการเปลี่ยนแปลงเหล่านี้ อย่างไรก็ตาม แนวทางที่แนะนำคือการใช้เปอร์เซ็นต์ส่วนแบ่งตลาดของเบราว์เซอร์ (เช่น ">0.25%"
) ควบคู่ไปกับการยกเว้นเบราว์เซอร์บางรายการที่คุณมั่นใจว่าไม่ได้ใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ในบทความ "2 เวอร์ชันล่าสุด" ที่ถือว่าเป็นอันตรายของ James Kyle
ใช้ <script type="module">
ยังมีอะไรให้ปรับปรุงได้อีก แม้ว่าจะมีการนำ Polyfill ที่ไม่ได้ใช้ออกไปแล้ว แต่ก็มีจำนวนมากที่กำลังจัดส่งซึ่งไม่จำเป็นสำหรับบางเบราว์เซอร์ การใช้โมดูลทำให้เขียนและจัดส่งไวยากรณ์ที่ใหม่กว่าไปยังเบราว์เซอร์ได้โดยตรงโดยไม่ต้องใช้ Polyfill ที่ไม่จำเป็น
โมดูล JavaScript เป็นฟีเจอร์ใหม่ที่ค่อนข้างรองรับในเบราว์เซอร์หลักๆ ทั้งหมด
คุณสร้างโมดูลได้โดยใช้แอตทริบิวต์ type="module"
เพื่อกำหนดสคริปต์ที่นำเข้าและส่งออกจากโมดูลอื่นๆ เช่น
// math.mjs
export const add = (x, y) => x + y;
<!-- index.html -->
<script type="module">
import { add } from './math.mjs';
add(5, 2); // 7
</script>
ฟีเจอร์ใหม่ๆ มากมายของ ECMAScript ได้รับการรองรับแล้วในสภาพแวดล้อมที่รองรับโมดูล JavaScript (แทนการใช้ Babel) ซึ่งหมายความว่าคุณสามารถแก้ไขการกำหนดค่า Babel ให้ส่งแอปพลิเคชันที่แตกต่างกัน 2 เวอร์ชันไปยังเบราว์เซอร์ได้ ดังนี้
- เวอร์ชันที่จะทำงานได้ในเบราว์เซอร์รุ่นใหม่ๆ ที่รองรับโมดูลและมีโมดูลที่ไม่มีการแปลงส่วนใหญ่แต่มีขนาดไฟล์เล็กกว่า
- เวอร์ชันที่มีสคริปต์ขนาดใหญ่ที่เปลี่ยนรูปแบบแล้ว ซึ่งสามารถใช้งานได้ในเบราว์เซอร์รุ่นเก่าทั้งหมด
การใช้โมดูล ES กับ Babel
หากต้องการตั้งค่า @babel/preset-env
แยกกันสำหรับแอปพลิเคชันทั้ง 2 เวอร์ชัน ให้นำไฟล์ .babelrc
ออก คุณเพิ่มการตั้งค่า Babel ไปยังการกำหนดค่าเว็บแพ็กได้โดยระบุรูปแบบการคอมไพล์ที่แตกต่างกัน 2 รูปแบบสำหรับแอปพลิเคชันแต่ละเวอร์ชัน
เริ่มต้นโดยเพิ่มการกำหนดค่าสำหรับสคริปต์เดิมไปยัง webpack.config.js
:
const legacyConfig = {
entry,
output: {
path: path.resolve(__dirname, "public"),
filename: "[name].bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env", {
useBuiltIns: "usage",
targets: {
esmodules: false
}
}]
]
}
},
cssRule
]
},
plugins
}
โปรดสังเกตว่าแทนที่จะใช้ค่า targets
สำหรับ "@babel/preset-env"
ระบบจะใช้ esmodules
ที่มีค่า false
แทน ซึ่งหมายความว่า Babel ได้รวมการแปลงและ Polyfill ที่จำเป็นทั้งหมดเพื่อกำหนดเป้าหมายไปยังทุกเบราว์เซอร์ที่ยังไม่รองรับโมดูล ES
เพิ่มออบเจ็กต์ entry
, cssRule
และ corePlugins
ไปยังจุดเริ่มต้นของไฟล์ webpack.config.js
สคริปต์เหล่านี้จะแชร์ระหว่างโมดูลและสคริปต์เดิมที่แสดงบนเบราว์เซอร์
const entry = {
main: "./src"
};
const cssRule = {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
};
const plugins = [
new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
new HtmlWebpackPlugin({template: "./src/index.html"})
];
ในทำนองเดียวกัน ให้สร้างออบเจ็กต์การกำหนดค่าสำหรับสคริปต์โมดูลด้านล่างที่กำหนดไว้ legacyConfig
const moduleConfig = {
entry,
output: {
path: path.resolve(__dirname, "public"),
filename: "[name].mjs"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env", {
useBuiltIns: "usage",
targets: {
esmodules: true
}
}]
]
}
},
cssRule
]
},
plugins
}
ความแตกต่างที่สำคัญคือนามสกุลไฟล์ .mjs
จะใช้สำหรับชื่อไฟล์เอาต์พุต ค่า esmodules
ได้รับการตั้งค่าเป็นจริงที่นี่ ซึ่งหมายความว่าโค้ดที่แสดงลงในโมดูลนี้เป็นสคริปต์ขนาดเล็กที่คอมไพล์น้อยกว่าซึ่งไม่เปลี่ยนรูปแบบใดๆ ในตัวอย่างนี้ เนื่องจากฟีเจอร์ทั้งหมดที่ใช้รองรับอยู่แล้วในเบราว์เซอร์ที่รองรับโมดูล
ที่ส่วนท้ายสุดของไฟล์ ให้ส่งออกการกำหนดค่าทั้ง 2 รายการในอาร์เรย์เดียว
module.exports = [
legacyConfig, moduleConfig
];
ตอนนี้การดำเนินการนี้จะสร้างทั้งโมดูลขนาดเล็กสำหรับเบราว์เซอร์ที่รองรับและสคริปต์ที่มีการเปลี่ยนภาพขนาดใหญ่สำหรับเบราว์เซอร์รุ่นเก่า
เบราว์เซอร์ที่รองรับโมดูลจะไม่สนใจสคริปต์ที่มีแอตทริบิวต์ nomodule
ในทางกลับกัน เบราว์เซอร์ที่ไม่รองรับโมดูลจะไม่สนใจองค์ประกอบของสคริปต์ที่มี type="module"
ซึ่งหมายความว่าคุณสามารถรวมโมดูลและโฆษณาสำรองที่คอมไพล์แล้วได้ ตามหลักการแล้ว แอปพลิเคชัน 2 เวอร์ชันควรอยู่ในรูปแบบ index.html
ดังนี้
<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js"></script>
เบราว์เซอร์ที่รองรับโมดูลจะดึงข้อมูลและเรียกใช้ main.mjs
และละเว้น
main.bundle.js.
เบราว์เซอร์ที่ไม่รองรับโมดูลจะทำงานตรงกันข้าม
สิ่งสำคัญคือต้องทราบว่าสคริปต์โมดูลจะถูกเลื่อนเวลาไว้โดยค่าเริ่มต้นเสมอ ซึ่งต่างจากสคริปต์ทั่วไป
หากต้องการเลื่อนเวลาสคริปต์ nomodule
ที่เทียบเท่าและดำเนินการหลังจากการแยกวิเคราะห์เท่านั้น คุณจะต้องเพิ่มแอตทริบิวต์ defer
ดังนี้
<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js" defer></script>
ขั้นตอนสุดท้ายที่ต้องทำคือเพิ่มแอตทริบิวต์ module
และ nomodule
ลงในโมดูลและสคริปต์เดิมตามลำดับ โดยนำเข้า ScriptExtHtmlWebpackPlugin ที่ด้านบนสุดของ webpack.config.js
ดังนี้
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
อัปเดตอาร์เรย์ plugins
ในการกำหนดค่าเพื่อรวมปลั๊กอินนี้ไว้
const plugins = [ new ExtractTextPlugin({filename: "[name].css", allChunks: true}), new HtmlWebpackPlugin({template: "./src/index.html"}), new ScriptExtHtmlWebpackPlugin({ module: /\.mjs$/, custom: [ { test: /\.js$/, attribute: 'nomodule', value: '' }, ] }) ];
การตั้งค่าปลั๊กอินเหล่านี้จะเพิ่มแอตทริบิวต์ type="module"
สำหรับเอลิเมนต์สคริปต์ .mjs
ทั้งหมด รวมถึงแอตทริบิวต์ nomodule
สำหรับโมดูลสคริปต์ .js
ทั้งหมด
โมดูลการแสดงผลในเอกสาร HTML
ขั้นตอนสุดท้ายที่ต้องทำคือการแสดงผลทั้งองค์ประกอบสคริปต์แบบเดิมและสมัยใหม่ไปยังไฟล์ HTML ขออภัย ปลั๊กอินที่สร้างไฟล์ HTML สุดท้าย HTMLWebpackPlugin
ยังไม่รองรับเอาต์พุตของทั้งโมดูลและสคริปต์ nomodule แม้ว่าจะมีวิธีแก้ปัญหาเบื้องต้นและปลั๊กอินแยกต่างหากที่สร้างขึ้นเพื่อแก้ไขปัญหานี้ เช่น BabelMultiTargetPlugin และ HTMLWebpackMultiBuildPlugin ซึ่งเป็นวิธีที่ง่ายขึ้นในการเพิ่มองค์ประกอบสคริปต์โมดูลด้วยตนเองเพื่อให้เป็นไปตามวัตถุประสงค์ของบทแนะนำนี้
เพิ่มโค้ดต่อไปนี้ลงใน src/index.js
ที่ท้ายไฟล์
...
</form>
<script type="module" src="main.mjs"></script>
</body>
</html>
คราวนี้ให้โหลดแอปพลิเคชันในเบราว์เซอร์ที่รองรับโมดูล เช่น Chrome เวอร์ชันล่าสุด
ระบบดึงข้อมูลเฉพาะโมดูลด้วยขนาดแพ็กเกจที่เล็กกว่ามากเนื่องจากไม่ได้รับการแปลงข้อมูลเป็นส่วนมาก เบราว์เซอร์จะไม่สนใจองค์ประกอบสคริปต์อื่นๆ โดยสิ้นเชิง
หากคุณโหลดแอปพลิเคชันในเบราว์เซอร์รุ่นเก่า ระบบจะดึงข้อมูลเฉพาะสคริปต์ขนาดใหญ่ที่เปลี่ยนรูปแบบแล้วพร้อมกับ Polyfill และการเปลี่ยนรูปแบบที่จำเป็นทั้งหมด ต่อไปนี้เป็นภาพหน้าจอสำหรับคำขอทั้งหมดที่สร้างขึ้นใน Chrome เวอร์ชันเก่า (เวอร์ชัน 38)
บทสรุป
ตอนนี้คุณเข้าใจวิธีใช้ @babel/preset-env
เพื่อระบุเฉพาะ Polyfill ที่จำเป็นสำหรับเบราว์เซอร์เป้าหมายแล้ว คุณยังรู้ด้วยว่าโมดูล JavaScript ช่วยปรับปรุงประสิทธิภาพให้ดียิ่งขึ้นด้วยการจัดส่งแอปพลิเคชันเวอร์ชันแปล 2 เวอร์ชันที่แตกต่างกันได้อย่างไร ความเข้าใจเป็นอย่างดีว่าเทคนิคทั้ง 2 นี้ช่วยลดขนาดกลุ่มให้เล็กลงอย่างเห็นได้ชัดได้อย่างไร จากนั้นจึงเลือกใช้และเพิ่มประสิทธิภาพ