當我們將游戲構建發布到web平台時,勾選Md5 Cache選項可以開啟MD5 Pipe,它的作用是給構建后的資源加上md5后綴,避免瀏覽器的緩存導致部分資源不是最新,因為使用了md5后綴后,當資源內容發生變化時,資源的名字就不一樣了,緩存就會失效。
比如6e056173-d285-473c-b206-40a7fff5386e.json,在開啟了MD5 Cache選項之后再構建會生成6e056173-d285-473c-b206-40a7fff5386e.a548e.json,在文件后綴.json前加上了一個簡化的md5——a548e。
開啟 MD5 PIPE
勾選了Md5 Cache選項后進行構建,生成的web-mobile/src/settings.xxx.js文件中會設置一個全局變量_CCSettings,該變量中有一個md5AssetsMap字段記錄所有資源文件的簡化md5。
md5AssetsMap: {
"08/08ddbd1c9.json": "ade93",
"6e/6e056173-d285-473c-b206-40a7fff5386e.json": "a548e",
"assets/6e/6e056173-d285-473c-b206-40a7fff5386e.png": "68270"
}
在Creator生成的main.xxx.js中會獲取_CCSettings變量進行處理,一個關鍵的地方就是cc.AssetLibrary.init,將settings.md5AssetsMap傳入AssetLibrary。
cc.AssetLibrary.init({
libraryPath: 'res/import',
rawAssetsBase: 'res/raw-',
rawAssets: settings.rawAssets,
packedAssets: settings.packedAssets,
md5AssetsMap: settings.md5AssetsMap
});
在cc.AssetLibrary.init中,根據md5AssetsMap變量決定是否創建MD5Pipe,將md5Pipe插入到cc.loader.assetLoader之后。
var md5AssetsMap = options.md5AssetsMap;
if (md5AssetsMap) {
var md5Pipe = new MD5Pipe(md5AssetsMap, _libraryBase, _rawAssetsBase);
cc.loader.insertPipeAfter(cc.loader.assetLoader, md5Pipe);
cc.loader.md5Pipe = md5Pipe;
}
在一些核心模塊中,我們可以看到直接使用url時,都會檢測是否存在cc.loader.md5Pipe,有則調用其transformURL對url進行處理,比如CCVideoPlayer.js,這些處理屬於不走Pipeline的路徑處理,屬於這些組件的內部邏輯。
_updateVideoSource: function () {
var sgNode = this._sgNode;
let url = '';
if (this.resourceType === ResourceType.REMOTE) {
url = this.remoteURL;
}
else if (this._clip) {
url = this._clip.nativeUrl || '';
}
if (url && cc.loader.md5Pipe) {
url = cc.loader.md5Pipe.transformURL(url);
}
sgNode.setURL(url);
},
MD5 PIPE的實現
Md5 Pipe的實現非常簡單(位於load-pipeline下的md5-pipe.js),它只對res/import或res/raw-開頭的url進行處理,根據md5AssetsMap對應的精簡md5值,重新組裝url。
MD5Pipe.prototype.transformURL = function (url, hashPatchInFolder) {
var index = url.indexOf('?');
var key = url;
if (index !== -1) {
key = url.substr(0, index);
}
// 如果是以libraryBase開頭'res/import',去掉這個前綴
if (key.startsWith(this.libraryBase)) {
key = key.slice(this.libraryBase.length);
// 如果是以rawAssetsBase開頭'res/raw-',也去掉這個前綴
} else if (key.startsWith(this.rawAssetsBase)) {
key = key.slice(this.rawAssetsBase.length);
} else {
// 其他情況不處理,比如是一個完整的http鏈接
return url;
}
// 取出該資源對應的精簡md5
let hashValue = this.md5AssetsMap[key];
if (hashValue) {
// 如果hashPatchInFolder為true則將這個md5組裝到目錄下
// JSB下的Spine、Label都有使用hashPatchInFolder
if (hashPatchInFolder) {
var dirname = cc.path.dirname(url);
var basename = cc.path.basename(url);
url = `${dirname}.${hashValue}/${basename}`;
} else {
// 正常情況是將精簡md5插入到文件擴展名之前
var matched = false;
url = url.replace(ExtnameRegex, (function(match, p1) {
matched = true;
return "." + hashValue + p1;
}));
if (!matched) {
url = url + "." + hashValue
}
}
}
return url;
};