進階 JavaScript 套裝
套裝 JavaScript 模組若要獲得更優異的效能,需要減少兩件事:
- 伺服器要求的數目。
- 這些伺服器要求的大小。
在模組化應用程式中,伺服器要求的數量可能會高達數百個。 例如,下列熒幕擷取畫面只會顯示 JavaScript 在全新安裝的首頁上載入的模組。
合併與捆綁
立即可用, Commerce 提供兩種減少伺服器請求數的方法:合併和捆綁。 這些設定預設為關閉。 您可以在的Admin UI中將其開啟: Stores > 設定 > Configuration > Advanced > Developer > JavaScript Settings,或從命令列。
基本組合
若要從命令列啟用內建組合:
php -f bin/magento config:set dev/js/enable_js_bundling 1
這是原生檔案 Commerce 此機制會結合系統中出現的所有資產,並在相同大小的組合(bundle_0.js、bundle_1.js … bundle_x.js)之間分配資產:
雖然這樣更好,但瀏覽器仍會載入 JavaScript 套件組合,而不僅僅是所需的套件組合。
Commerce 套件組合可減少每頁的連線數量,但對於每個頁面請求,它會載入所有套件組合,即使請求的頁面可能僅取決於一或兩個套件組合中的檔案。 在瀏覽器快取套件組合後,效能會有所改善。 但是,由於瀏覽器會同步載入這些套件組合,因此使用者的首次造訪將會是 Commerce storefront可能需要一些時間才能呈現,並損害使用者體驗。
基本合併
若要從命令列啟用內建合併:
php -f bin/magento config:set dev/js/merge_files 1
此命令會合併所有同步的 JavaScript 將檔案合併為一個檔案。 啟用合併而不啟用繫結沒有用處,因為 Commerce 使用RequireJS。 如果您未啟用套件組合, Commerce 僅合併RequireJS及其設定。 當您同時啟用套件組合和合併時, Commerce 建立單一 JavaScript 檔案:
真實世界的演算時間
在開發環境中,之前的套件和合併載入時間看起來很棒。 但在現實世界中,許多因素都會拖慢呈現速度:連線速度緩慢、連線臨界值過大、網路有限。 此外,行動裝置的呈現速度不如桌上型電腦。
為了測試和準備您的店面部署以進行真實世界的測試,我們建議您使用Chrome的原生節流設定檔「緩慢3G」進行測試。 透過Slow 3G,我們先前提供的輸出時間現在反映了許多使用者的連線實際情況:
在慢速3G連線中,需要大約44秒的時間來載入整潔首頁的所有組合 Commerce 安裝。
將組合合併到單一檔案中也是同樣的情形。 使用者仍可等待約42秒才開始載入頁面,如下所示:
透過更進階的方法 JavaScript 套件組合,我們可以縮短載入時間。
進階套裝
記住,目標 JavaScript 套件組合是針對瀏覽器中載入的每個頁面,減少請求資產的數量和大小。 為此,我們想要建立套件組合,讓商店中的每個頁面只需要下載通用的套件組合,以及每個存取頁面的頁面特定套件組合。
其中一個方法是依頁面型別定義您的組合。 您可以分類 Commerce的頁面分為數種頁面型別,包括類別、產品、CMS、客戶、購物車和結帳。 分類為其中一種頁面型別的每個頁面都有一組不同的RequireJS模組相依性。 當您依頁面型別捆綁RequireJS模組時,您最後將只有少數捆綁包涵蓋存放區中任何頁面的相依性。
例如,您最終可能會有適用於所有頁面的通用相依性的套件、適用於僅限CMS頁面的套件、適用於僅限目錄頁面的套件、適用於僅限搜尋頁面的另一個套件和適用於出庫頁面的套件。
您也可以依用途建立組合:用於一般功能、產品相關功能、送貨功能、結帳功能、稅金及表單驗證。 如何定義您的套件組合取決於您和商店的結構。 您可能會發現某些套件組合策略的效果優於其他策略。
乾淨 Commerce 安裝可讓您透過按頁面型別分割套件組合,達到足夠的良好效能,但某些自訂可能需要更深入的分析和其他資產分佈。
必要工具
下列步驟需要您安裝並熟悉下列工具:
程式碼範例
本文中使用的範常式式碼完整版本可在此處取得:
第1部分:建立套件組合設定
1. 新增build.js檔案
建立 build.js
中的檔案 Commerce 根目錄。 此檔案將包含您的套件組合的完整組建組態。
({
optimize: 'none',
inlineText: true
})
稍後,我們將變更 optimize:
從_設定 none
至 uglify2
以縮小束輸出。 但就目前而言,在開發期間,您可以將其設為 none
以確保更快的建置。
2. 新增RequireJS相依性、填料、路徑和對應
新增下列RequireJS組建設定節點, deps
, shim
, paths
、和 map
,至您的建置檔案:
({
optimize: 'none',
inlineText: true,
deps: [],
shim: {},
paths: {},
map: { "*": {} },
})
3. 彙總requirejs-config.js例項值
在此步驟中,您需要彙總所有多個 deps
, shim
, paths
、和 map
來自您存放區的設定節點 requirejs-config.js
檔案放入中的對應節點 build.js
檔案。 若要這麼做,您可以開啟 Network 在瀏覽器的「開發人員工具」面板中按Tab鍵,然後導覽至商店中的任何頁面,例如首頁。 在Network標籤中,您會看到商店的執行個體 requirejs-config.js
頂部附近的檔案,在這裡反白顯示:
在此檔案中,您會找到每個設定節點的多個專案(deps
, shim
, paths
, map
)。 您需要將這些多個節點值彙總到您的build.js檔案的單一設定節點中。 例如,如果商店的 requirejs-config.js
執行個體有15個獨立專案 map
節點,您需要將所有15個節點的專案合併為單一節點 map
中的節點 build.js
檔案。 同樣的情況也適用於 deps
, shim
、和 paths
節點。 如果沒有指令碼來自動化此程式,則可能需要時間。
您需要變更路徑 mage/requirejs/text
至 requirejs/text
在 paths
設定節點如下:
({
//...
paths: {
//...
"text": "requirejs/text"
},
})
4. 新增模組節點
在 build.js
檔案,新增模組[] 陣列,作為您稍後為店面定義的套件組合預留位置。
({
optimize: 'none',
inlineText: true,
deps: [],
shim: {},
paths: {},
map: { "*": {} },
modules: [],
})
5. 擷取RequireJS相依性
您可以擷取所有 RequireJS 使用下列方式,從存放區的頁面型別取得模組相依性:
- PhantomJS 從命令列(假設您有 PhantomJS 已安裝)。
- 瀏覽器主控台中的RequireJS命令。
使用 PhantomJS:
在 Commerce 根目錄,建立名為的新檔案 deps.js
並複製下列程式碼。 此程式碼使用PhantomJS開啟頁面並等待瀏覽器載入所有頁面資產。 然後,輸出所有 RequireJS 指定頁面的相依性。
"use strict";
var page = require('webpage').create(),
system = require('system'),
address;
if (system.args.length === 1) {
console.log('Usage: $phantomjs deps.js url');
phantom.exit(1);
} else {
address = system.args[1];
page.open(address, function (status) {
if (status !== 'success') {
console.log('FAIL to load the address');
} else {
setTimeout(function () {
console.log(page.evaluate(function () {
return Object.keys(window.require.s.contexts._.defined);
}));
phantom.exit();
}, 5000);
}
});
}
開啟內的終端機 Commerce 根目錄並針對存放區中代表特定頁面型別的每個頁面執行指令碼:
phantomjs deps.js url-to-specific-page > text-file-reporting-pagetype-dependencies
例如,以下是Luma主題範例商店中的四個頁面,代表我們將用來建立四個組合(首頁、類別、產品、購物車)的四個頁面型別:
phantomjs deps.js http://m2.loc/ > bundle/homepage.txt
phantomjs deps.js http://m2.loc/women/tops-women/jackets-women.html > bundle/category.txt
phantomjs deps.js http://m2.loc/beaumont-summit-kit.html > bundle/product.txt
phantomjs deps.js http://m2.loc/checkout/cart/?SID=m2tjdt7ipvep9g0h8pmsgie975 > bundle/cart.txt (prepare a shopping cart)
..............
若要使用瀏覽器主控台:
如果您不想要使用 PhantomJS,您可以在檢視店面中的每種頁面型別時,從瀏覽器的主控台執行以下命令:
Object.keys(window.require.s.contexts._.defined)
此命令(用於 PhantomJS 指令碼)建立相同的 RequireJS 相依性並在瀏覽器的主控台中顯示它們。 此方法的缺點是,您必須建立自己的組合/頁面型別文字檔。
6. 格式化並篩選輸出
合併之後 RequireJS 相依性轉換為頁面型別文字檔案,您可以在每個頁面型別相依性檔案上使用下列指令,以換行來取代檔案中的逗號:
sed -i -e $'s/,/\\\n/g' bundle/category.txt
sed -i -e $'s/,/\\\n/g' bundle/homepage.txt
sed -i -e $'s/,/\\\n/g' bundle/product.txt
....
您也應該移除每個檔案的所有mixin,因為mixin重複相依性。 對每個相依性檔案使用以下指令:
sed -i -e 's/mixins\!.*$//g' bundle/homepage.txt
sed -i -e 's/mixins\!.*$//g' bundle/category.txt
sed -i -e 's/mixins\!.*$//g' bundle/product.txt
...
7. 識別唯一和常見的組合
目標是建立一個共同的組合 JavaScript 所有頁面所需的檔案。 如此一來,瀏覽器只需要載入通用套件組合以及一或多個特定頁面型別。
在中開啟終端機 Commerce 根目錄,並使用下列指令來驗證您是否有相依性可以分割成個別的組合:
sort bundle/*.txt |uniq -c |sort -n
這個指令會合併和排序在 bundle/*.txt
檔案。 輸出也顯示包含每個相依性的檔案數:
1 buildTools,
1 jquery/jquery.parsequery,
1 jsbuild,
2 jquery/jquery.metadata,
2 jquery/validate,
2 mage/bootstrap,
3 jquery
3 jquery/ui
3 knockoutjs/knockout
...
此輸出顯示 buildTools
只在bundle/*.txt檔案中的一個檔案中有相依性。 此 jquery/jquery.metadata
相依性位於兩(2)個檔案中,且 es6-collections
在三(3)個檔案中。
我們的輸出僅顯示三種頁面型別(首頁、類別和產品),這會告訴我們:
- 只有一種頁面型別有三種相依性(由數字1顯示)。
- 兩種頁面型別還會發生三種相依性(如數字2所示)。
- 最後三個相依性對於我們的全部三個頁面型別都是共同的(由數字3顯示)。
這告訴我們,一旦我們知道哪些頁面型別需要哪些相依性,就可以將相依性分割為不同的套件,藉此提高存放區的頁面載入速度。
8. 建立相依性發佈檔案
若要找出哪些頁面型別需要哪些相依性,請在 Commerce 已呼叫的根目錄 deps-map.sh
並複製下列程式碼:
awk 'END {
for (R in rec) {
n = split(rec[R], t, "/")
if (n > 1)
dup[n] = dup[n] ? dup[n] RS sprintf("\t%-20s -->\t%s", rec[R], R) : \
sprintf("\t%-20s -->\t%s", rec[R], R)
}
for (D in dup) {
printf "records found in %d files:\n\n", D
printf "%s\n\n", dup[D]
}
}
{
rec[$0] = rec[$0] ? rec[$0] "/" FILENAME : FILENAME
}' bundle/*.txt
您也可以在此找到指令碼 https://www.unix.com/shell-programming-and-scripting/140390-get-common-lines-multiple-files.html
在中開啟終端機 Commerce 根目錄並執行檔案:
bash deps-map.sh
此指令碼的輸出套用至我們的三個範例頁面型別,看起來應該像這樣(但時間更長):
bundle/product.txt --> buildTools,
bundle/category.txt --> jquery/jquery.parsequery,
bundle/product.txt --> jsbuild,
bundle/category.txt/bundle/homepage.txt --> jquery/jquery.metadata,
bundle/category.txt/bundle/homepage.txt --> jquery/validate,
bundle/category.txt/bundle/homepage.txt --> mage/bootstrap,
bundle/category.txt/bundle/homepage.txt/bundle/product.txt --> jquery,
bundle/category.txt/bundle/homepage.txt/bundle/product.txt --> jquery/ui,
bundle/category.txt/bundle/homepage.txt/bundle/product.txt --> knockoutjs/knockout,
這些資訊足以建置套件組合設定。
9. 在您的build.js檔案中建立套件組合
開啟 build.js
組態檔並將您的套件組合新增至 modules
節點。 每個束都應該定義以下屬性:
-
name
— 束的名稱。 例如,名稱bundles/cart
產生cart.js
套件組合在bundles
子目錄。 -
create
— 用於建立搭售方案的布林值標幟(值:true
或false
)。 -
include
— 作為頁面相依性包含的資產(字串)陣列。 RequireJS會追蹤所有相依性,並將它們包含在套件中(除非排除)。 -
exclude
— 要從套件組合排除的套件組合或資產陣列。
{
name: 'bundles/catalog',
create: true,
include: [
'addToWishlist',
'priceBundle',
'priceUtils',
'priceOptions',
'sticky',
'productSummary',
'slide'
],
exclude: [
'requirejs/require',
'bundles/default',
'mage/bootstrap'
],
}
此範例重複使用 mage/bootstrap
和 requirejs/require
資產,對於必須同步載入的最重要元件和元件設定較高的優先順序。 呈現的套件組合包括:
requirejs/require
— 唯一同步載入的組合mage/bootstrap
— 具有UI元件的啟動程式套件bundles/default
— 所有頁面都需要預設套件組合bundles/cart
— 購物車頁面所需的套件組合bundles/shipping
— 購物車和結帳頁面的通用套件組合(假設從未直接開啟結帳,如果先前已開啟購物車頁面且已載入送貨套件,結帳頁面載入速度會更快)bundles/checkout
— 結帳所需的一切bundles/catalog
— 產品和類別頁面的一切
第2部分:產生組合
以下步驟說明提高效率的基本程式 Commerce 套件組合。 您可以透過任何所需方式將此程式自動化,但您仍需使用 nodejs
和 r.js
以實際產生您的組合。 如果您的主題有 JavaScript — 相關的自訂,無法重複使用 build.js
檔案,您可能需要建立多個 build.js
每個主題的設定。
1.產生靜態存放區網站
在產生套件組合之前,請執行靜態部署命令:
php -f bin/magento setup:static-content:deploy -f -a frontend
這個命令會為您設定的每個主題和地區設定產生靜態存放區部署。 例如,如果您使用Luma主題和自訂主題搭配英文和法文語言環境,則會產生四個靜態部署:
- …luma/en_US
- …luma/fr_FR
- …custom/en_US
- …custom/fr_FR
若要產生所有商店主題與地區設定的組合,請對每個商店主題與地區設定重複下列步驟。
2.將靜態存放區內容移至暫存目錄
首先,您必須將靜態內容從目標目錄移至某個暫存目錄,因為RequireJS會取代目標目錄中的所有內容。
mv pub/static/frontend/Magento/{theme}/{locale} pub/static/frontend/Magento/{theme}/{locale}_tmp
例如:
mv pub/static/frontend/Magento/luma/en_US pub/static/frontend/Magento/luma/en_US_tmp
3.執行r.js最佳化程式
接著在網頁上執行r.js最佳化程式 build.js
檔案來源 Commerce的根目錄。 所有目錄和檔案的路徑都相對於工作目錄。
r.js -o build.js baseUrl=pub/static/frontend/Magento/luma/en_US_tmp dir=pub/static/frontend/Magento/luma/en_US
此指令會在以下專案中產生組合: bundles
目標目錄的子目錄,在此案例中會產生 pub/static/frontend/Magento/luma/en_US/bundles
.
列出新組合目錄的內容可能如下所示:
ll pub/static/frontend/Magento/luma/en_US/bundles
total 1900
drwxr-xr-x 2 root root 4096 Mar 28 11:24 ./
drwxr-xr-x 70 root root 4096 Mar 28 11:24 ../
-rw-r--r-- 1 root root 116417 Mar 28 11:24 cart.js
-rw-r--r-- 1 root root 187090 Mar 28 11:24 catalog.js
-rw-r--r-- 1 root root 307619 Mar 28 11:24 checkout.js
-rw-r--r-- 1 root root 1240608 Mar 28 11:24 default.js
-rw-r--r-- 1 root root 74233 Mar 28 11:24 shipping.js
4.設定RequireJS使用套件組合
若要讓RequireJS使用您的套件組合,請新增 onModuleBundleComplete
回呼晚於 modules
中的節點 build.js
檔案:
[
{
//...
exclude: [
'requirejs/require',
'bundles/default',
'bundles/checkout',
'bundles/cart',
'bundles/shipping',
'mage/bootstrap'
],
},
],
bundlesConfigOutFile: `${config.dir}/requirejs-config.js`,
onModuleBundleComplete: function(data) {
if (this.bundleConfigAppended) {
return;
}
this.bundleConfigAppended = true;
// bundlesConfigOutFile requires a simple require.config call in order to modify the configuration
const bundleConfigPlaceholder = `
(function (require) {
require.config({});
})(require);
`;
fs.appendFileSync(this.bundlesConfigOutFile, bundleConfigPlaceholder);
}
5.重新執行部署命令
執行以下命令以部署:
r.js -o app/design/frontend/Magento/luma/build.js baseUrl=pub/static/frontend/Magento/luma/en_US_tmp dir=pub/static/frontend/Magento/luma/en_US
開啟 requirejs-config.js
在 pub/static/frontend/Magento/luma/en_US
目錄,以驗證RequireJS是否已將檔案附加至套件組合設定呼叫:
require.config({
bundles: {
"bundles/default": ["mage/template", "mage/apply/scripts", "mage/apply/main", "mage/mage", "mage/translate", "mage/loader"],
"bundles/cart": ["Magento_Ui/js/lib/validation/utils", "Magento_Ui/js/lib/validation/rules", "Magento_Ui/js/lib/validation/validation"]
}
}
requirejs.config()
呼叫的執行順序,因為呼叫是以其出現的順序執行。6.測試結果
頁面載入後,請注意瀏覽器載入不同的相依性和套件組合。 例如,以下是「慢3G」設定檔的結果:
空白首頁的頁面載入時間現在比使用原生快一倍 Commerce 套件組合。 但是我們可以做得更好。
7.最佳化組合
即使gzipped, JavaScript 檔案仍然很大。 使用RequireJS來縮制這些值,後者使用精簡符號來縮制 JavaScript 以取得良好的結果。
若要啟用中的最佳化工具 build.js
檔案,新增 uglify2
做為頂端最佳化屬性的值 build.js
檔案:
({
optimize: 'uglify2',
inlineText: true
})
結果可能會相當可觀:
載入時間現在比原生快三倍 Commerce 套件組合。