随着游戏往重度方向发展,游戏代码包越来越大,为了更好的加载体验,分包加载迫在眉睫。 早在2018年微信小游戏就推出分包API了,但是白鹭引擎官方却迟迟没发布高效的分包解决方案,就目前能找到的分包方案,却有很多问题。
我们先来分析一下目前公开的方案: 方案1:白鹭官方推出的资源分包,只能针对素材分包,没无法针对js代码分包,这里查看文档 方案2:在方案1的基础上,自己把原项目拆分成几个独立项目,一个分包对应一个项目,用类库的方式进行编译输出js。这种方式开发时不灵活,需要管理太多的项目,很不方便。 方案3:白鹭在5.3.6引擎推出了webpack 编译模式,提供的WebpackBundlePlugin插件支持js拆分输出,但是该方式有问题:拆分输出的文件必须一次性加载才能运行,无法按需加载(分包加载),详情参考这里(截止到2021年8月官方还未修正) 目前公开渠道能找到的这几种方法都解决不了实际需求,那就只能自己开发了。
需求目标:开发的时候只有1个项目,根据文件夹将代码编译出多个独立的js,输出到对应的包里面按需分包加载。 解决思路:定义一套输出规则,修改引擎的编译代码,使得项目代码输出成独立的多个js,最终实现分包加载。 1、配置js输出规则:在config.wxgame.ts里面修改ConpilePlugin调用,配合编译代码实现js拆分输出
new CompilePlugin({ libraryType: "release", defines: { DEBUG: false, RELEASE: true
, subpackages:[{name: "game_main", matcher:(filepath)=>{
if(filepath.indexOf("miniGame/") >-1 ) return true;
return false;
}
}]
} }),
2.1、在config.wxgame.ts里面导入SubPackagePlugin(代码见文末)
import { SubPackagePlugin } from './wxgame/subpackage';
2.2、在config.wxgame.ts里面ManifestPlugin代码处下一行加上分包文件路径配置
, new SubPackagePlugin({ output: 'manifest.js', subPackages: [
{ root: "stage_game", "includes": ["js/game_main.min.js","js/xxxxx.js"]}
]
})
3、修改引擎编译代码:找到插件CompilePlugin的位置:C:\Users\用户名\AppData\Roaming\Egret\engine\5.2.33\tools\tasks\compile.js,修改tinyCompiler方法
function tinyCompiler(defines, pluginContext) {
var os = require('os');
var outfile = FileUtil.joinPath(os.tmpdir(), 'main.min.js');
var options = egret.args;
options.minify = true;
options.publish = true;
var configParsedResult = compiler.parseTsconfig(options.projectDir, options.publish);
var compilerOptions = configParsedResult.options;
var fileNames = configParsedResult.fileNames;
var tsconfigError = configParsedResult.errors.map(function (d) { return d.messageText.toString(); });
compilerOptions.outFile = outfile;
compilerOptions.allowUnreachableCode = true;
compilerOptions.emitReflection = true;
compilerOptions.defines = defines;
var pp = defines.subpackages;
if(pp){
for(var j=0; j<pp.length; j++){
var newList = [];
for(var i=0; i<fileNames.length; i++){
if( pp[j].matcher(fileNames[i]) ){
newList.push(fileNames[i]);
fileNames.splice(i, 1);
i--;
}
}
if(newList.length > 0){
newList.push(options.projectDir + "libs/modules/egret/egret.d.ts");
newList.push(options.projectDir + "libs/modules/game/game.d.ts");
newList.push(options.projectDir + "libs/modules/assetsmanager/assetsmanager.d.ts");
var compilerHost = compiler.compile(compilerOptions, newList);
if (compilerHost.messages && compilerHost.messages.length > 0) {
global.exitCode = 1;
}
var jscode = fs.readFileSync(outfile);
pluginContext.createFile(pp[j].name + ".js", new Buffer(jscode));
}
}
}
var compilerHost = compiler.compile(compilerOptions, fileNames);
if (compilerHost.messages && compilerHost.messages.length > 0) {
global.exitCode = 1;
}
var jscode = fs.readFileSync(outfile);
return jscode;
}
jscode = tinyCompiler(this.options.defines, pluginContext);
4、执行发布命令:egret publish --target wxgame
以上,有效的解决了js文件拆分和分包的需求。 需要强调的是,拆分了js,跨js调用的Class,需要在js里面设置 window.xxxx = xxxx对外暴露,这样才能正常调用。
附:项目里面调用分包代码:
public static openSubpackage(name, fun){
if(DEBUG){
return fun && fun();
}
if (window["wx"] && wx.loadSubpackage) {
let task = wx.loadSubpackage({
name: name,
success: function () {
fun && fun();
}
});
if(!task){
console.warn("openSubpackage-fail:", name);
return fun && fun();
}
task.onProgressUpdate(res => {
console.warn("onProgressUpdate:", res);
})
}
else {
window["require"]("./" + name + "/game.js");
fun && fun();
}
}
Main.openSubpackage("stage_game", ()=>{
})
附:SubPackagePlugin插件代码
import * as path from 'path';
const manifest = {
initial: [],
game: []
}
type SubPackagePluginOptions = {
output: string,
subPackages: {
root: string,
includes: string[]
}[],
verbose?: boolean
}
export class SubPackagePlugin {
private verboseInfo: { filename: string, new_file_path: string }[] = [];
private includes: {} = {};
constructor(private options: SubPackagePluginOptions) {
console.warn("options",options);
if (this.options.subPackages) {
this.options.subPackages.forEach((sub_package) => {
sub_package.includes.forEach((file_name) => {
this.includes[file_name] = sub_package.root;
})
})
}
}
async onFile(file: plugins.File) {
const filename = file.relative;
if(filename == "manifest.js") return;
const extname = path.extname(filename);
if (extname == ".js") {
const basename = path.basename(filename);
let trans_filename = filename.split("\\").join('/');
let new_file_path = trans_filename;
let isSubPackage = false;
if (this.options.subPackages) {
if (this.includes[trans_filename]) {
new_file_path = this.includes[trans_filename] + "/" + trans_filename;
isSubPackage = true;
}
}
if (this.options.verbose) {
console.log(`SubPackagePlugin: ${filename} isSubPackage: ${isSubPackage}`);
}
file.path = path.join(file.base, new_file_path);
if (!isSubPackage) {
const relative = file.relative.split("\\").join('/');
if (file.origin.indexOf('libs/') >= 0) {
manifest.initial.push(relative);
}
else {
manifest.game.push(relative);
}
}
if (this.options.verbose) {
this.verboseInfo.push({ filename, new_file_path })
}
}
return file;
}
async onFinish(pluginContext: plugins.CommandContext) {
const output = this.options.output;
const extname = path.extname(output);
let contents = '';
switch (extname) {
case ".json":
contents = JSON.stringify(manifest, null, '\t');
break;
case ".js":
contents = manifest.initial.concat(manifest.game).map((fileName) => {
let isEgret = fileName.indexOf("egret-library") >-1;
return (isEgret ? 'requirePlugin':'require') + `("${fileName}")`;
}).join("\n")
break;
}
pluginContext.createFile(this.options.output, new Buffer(contents));
if (this.options.subPackages) {
const self = this;
this.options.subPackages.forEach(function (item) {
let gameJS = "";
gameJS = item.includes.map((file) => {
const extname = path.extname(file);
console.log('extname', extname);
let return_location = ''
switch (extname) {
case '.js':
return_location = `require("js/${path.basename(file)}")`
break;
}
return return_location
}).join("\n");
console.log('output', gameJS);
pluginContext.createFile(path.join(item.root, "game.js"), new Buffer(gameJS));
});
}
if (this.options.verbose) {
this.verboseInfo.forEach((item) => {
console.log(`SubPackagePlugin: ${item.filename} => ${item.new_file_path}`);
});
}
}
}
本文原创,未经作者同意禁止转载
|