插件使用案例教程
插件使用案例教程
引言
本文不详细介绍各插件的细节,只展示使用方法,如果开发者对细节有兴趣可以参考 API 文档。
为了让开发者以更简单的使用Egret内置的插件,我们将通过一个案例来展示插件的具体使用方法和注意事项。本文基于引擎5.2.7,低于该版本的引擎可能部分功能无效
本文案例从一个刚编写完成的eui卡牌项目开始发布到微信小游戏,为了让代码包的体积更小,更好管理,逐步添加使用不同的插件,以实现不同的需求。源文件下载地址
todos
- 使用UglifyPlugin将代码混淆压缩
- 使用ResSplitPlugin把部分资源分离出去
- 使用ZipPlugin把文件压缩成zip格式
- 使用TextureMergerPlugin将纹理合并,且用ConvertResConfigFilePlugin修改res.json配置文件
项目初始化
- 把index.html中的data-scale-mode改成fixedWidth
- 打开EgretLauncher,将本项目发布成微信小游戏
- 打开微信开发者工具
使用UglifyPlugin压缩代码
在微信开发者工具可以看到,js文件夹中5个库文件和一个main.js。
现在需求是是要把库文件压缩到一个文件lib.min.js中。
回到EgretWing,编辑sctipts下的config.wxgame.ts:
//***其他代码***
//
if (command == "build") {
 return {
    outputDir,
    commands: [
        // 清理js,resource文件夹
        new CleanPlugin({ matchers: ["js", "resource"] }),
        new CompilePlugin({ libraryType: "debug", defines: { DEBUG: true, RELEASE: false }  }),
        new ExmlPlugin("commonjs"),
        new WxgamePlugin(),
        //  压缩插件
        new UglifyPlugin([
        {
            // 需要被压缩的文件
            sources: [
            "libs/modules/egret/egret.js",
            "libs/modules/eui/eui.js",
            "libs/modules/assetsmanager/assetsmanager.js",
            "libs/modules/tween/tween.js",
            ],
            // 压缩后的文件
            target: "lib.min.js"
        }
        ]),
        new ManifestPlugin({ output: "manifest.js" })
    ]
  };
}
//
// ***其他代码***
保存后在终端执行:
egret build
可以在微信开发者工具看到发布后的代码,js文件夹内的库文件已经被压缩到lib.min.js。
但是报错,找不到eui,这是因为自动生成的manifest.js里面对js的引用顺序出错,需要优先引用lib.min.js
打开根目录下的manifest.js, 修改一下引用顺序。
require("js/lib.min.js");
require("js/main.js");
require("js/default.thm.js");
每次编译的时候manifest.js都会被重新生成,所以我们使用一个自定义脚本来修改他们的顺序
打开 scripts下的myPlugin.ts :
/*
 * 示例,了解如何开发一个自定义插件
 */
export  class  CustomPlugin  implements  plugins.Command  {
  private buffer;
  constructor()  {
  }
  async  onFile(file: plugins.File)  {
    // 保存manifest.js文件的内容
    if  (file.basename.indexOf("manifest.js")  \>  -1)  {
        this.buffer  = file.contents;
    }
    return file;
  }
  async  onFinish(commandContext: plugins.CommandContext)  {
    // 把'lib.min.js'移到第一位
    if  (this.buffer)  {
        let contents:  string  =  this.buffer.toString();
        let arr = contents.split("\n");
        let lib;
        arr.forEach((item, index)  =>  {
            if  (item.indexOf("lib.min.js")  \>  -1)  {
                lib = item;
                arr.splice(index,  1);
            }
        });
        if  (lib !=  null)  {
            arr.unshift(lib);
        }
        let newCont = arr.join("\n");
        commandContext.createFile("manifest.js",  new  Buffer(newCont));
    }
  }
}
这个文件就是用来自定义插件的,在config.wxgame.ts中已经默认引用,所以只需要调用即可,注意调用顺序
new  ManifestPlugin({ output:  "manifest.js"  }),
  // 在manifest.js生成之后调用
  new  CustomPlugin();
使用ResSplitPlugin分离资源文件
因为微信对代码包的大小是有限制的,总大小不能超过4M(使用分包功能可以提升到8M),所以我们需要通过ResSplitPlugin把某些游戏资源文件分离出去,将游戏资源放置在一个外部CDN服务器上,需要的时候动态加载即可。
编辑config.wxgame.ts:
// ***其他代码***
//
new  ResSplitPlugin({
 verbose:  false, matchers:  [
  // from 使用glob表达式来匹配文件,  projectName就是项目的名字
  {  from:  "resource/art/about/**.**", to:  `${projectName}_wxgame_remote`  },
  {  from:  "resource/art/heros_goods/**.**", to:  `${projectName}_wxgame_remote`  }
  ]
});
// ***其他代码***
保存后在终端执行:
egret build
微信开发者工具中resource > art 下的about和heros_goods已经不在了。
被分离出去的在 Egret 项目根目录中 egret-eui-demo_wxgame_remote 文件夹内。
- 注意 1: 需要开发者自己写逻辑,判断一下如果是微信游戏时,哪些资源是从远程加载的,哪些是放在本地的
- 注意 2: 为了便于调试,我们把资源放在了 Egret 项目根目录中 egret-eui-demo_wxgame_remote文件夹内,正式发布的一般放在 CDN 上
使用ZipPlugin把文件压缩成zip格式
为了减少加载次数和传输量,我们可以把文件压缩成zip格式,使用的时候可以使用第三方库JSZip来读取使用zip文件。
使用ZipPlugin插件之前,需要安装cross-zip 和 cross-zip-cli , 在终端中输入:
//全局安装
npm  install cross-zip -g 
npm  install cross-zip-cli -g
安装完成之后,在config.wxgame.ts添加代码:
new  ZipPlugin({
  mergeSelector:  p  =>  {
    // 如果文件是assets/路径下的, 压缩到assets.zip
    if  (p.indexOf("assets/")  \>=  0)  {
        return  "assets.zip";
    }
  }
});
项目中其实assets里面的资源都是没有用到的,这里我们用它来演示压缩插件的使用。
保存后在终端执行:
egret build
执行之后可以在微信开发者工具看到,resource目录下原来的assets文件夹已经被压缩成了assets.zip。
使用TextureMergerPlugin,ConvertResConfigFilePlugin合并纹理集
项目中使用的图片资源都是单独的png文件,在加载的时候每张图片都会单独请求。我们可以通过合并纹理集的方式把这些图片合成一张图,以减少请求数量。 使用插件之前,我们需要有纹理集的配置文件tmpropject, 可以用两种方式生成:
- 安装TextureMerger工具
- 执行脚本生成
这里使用第二种方法,使用脚本autoMerger.js:
    "use strict";
    Object.defineProperty(exports,  "__esModule",  { value:  true  });
    const fs =  require("fs");
    const path =  require("path");
    const resjsons =  ["resource/default.res.json"];  //要扫描的res.json文件
    const targetDir =  "resource/TextureMerger";  //输出目录
    const pathNor = path.relative(targetDir,  "resource");  //返回一个相对路径
    const tempindex =  0;
    //创建输出文件夹
    if  (resjsons.length  \>  0)  {
    if  (!fs.existsSync(targetDir))  {
        // var paths = path.normalize(targetDir).split("\\");   //windows 下使用
        const paths = path.normalize(targetDir).split("/");  //mac linux 下使用
        let target =  ".";
        for  (let _i =  0, paths_1 = paths; _i < paths_1.length; _i++)  {
            const p = paths_1[_i];
            // target += ("\\" + p);  // windows 下使用
            target +=  ("/"  + p);  // mac linux 下使用
            if  (!fs.existsSync(target))
            // 根据路径创建文件夹
            fs.mkdirSync(target);
        }
    }
    }
    const  _loop_1  =  function  (resJson)  {
  // 判断是否是res.json文件
  if  (fs.existsSync(resJson)  && resJson.indexOf("res.json")  \>  -1)  {
        const defaultJson = fs.readFileSync(resJson,  "utf-8");
        // 解析res.json文件内容
        const defaultObject =  JSON.parse(defaultJson);
        const groups = defaultObject.groups;  //组
        const resources = defaultObject.resources;  //资源
        const resourcesHash_1 =  {};  // 用来存放resources的资源信息
        // 遍历resources
        for  (let _i =  0, resources_1 = resources; _i < resources_1.length; _i++)  {
            const resource = resources_1[_i];
            resourcesHash_1[resource.name]  = resource.url;
        }
        // 遍历groups
        for  (let _a =  0, groups_1 = groups; _a < groups_1.length; _a++)  {
        const group = groups_1[_a];
        let tmproject =  {};  //用来存放tmproject文件的信息
        // tmproject文件配置
        tmproject["options"]  =  {
            "layoutMath":  "2",
            "sizeMode":  "2n",
            "useExtension":  1,
            "layoutGap":  1,
            "extend":  0
        };
        // projectName
        tmproject["projectName"]  = group.name  +  "_"  + tempindex;
        // 版本
        tmproject["version"]  =  5;
        tempindex++;
        // 获取res.json分组的keys, 并分割成数组
        let oldkeys = group.keys.split(",");
        let oldkeysHash =  {};
        // 遍历oldkeys
        for  (let _b =  0, oldkeys_1 = oldkeys; _b < oldkeys_1.length; _b++)  {
            const key = oldkeys_1[_b];
            // 保存到oldkeysHash对象中
            oldkeysHash[key]  =  true;
        }
        const newKeys =  [];
        // 遍历oldkeys
        for  (let _c =  0, oldkeys_2 = oldkeys; _c < oldkeys_2.length; _c++)  {
            const key = oldkeys_2[_c];
            if  (key.indexOf("json")  ==  -1)  {
                if  (!oldkeysHash[key.replace("png",  "json")])  {  //粒子和龙骨对应的图集不合图
                    if  (!oldkeysHash[key.replace("png",  "fnt")])  //位图字体
                    newKeys.push(key);
                }
                else  if  (key.indexOf("jpg")  \>  -1)  {
                    newKeys.push(key);
                }
            }
        }
        oldkeysHash =  {};
        oldkeys =  [];
        // files路径
        const urls = newKeys.map(function  (key)  {
            return path.join(pathNor, resourcesHash_1[key]);
        });
        tmproject["files"]  = urls;
        // 根据tmproject写入文件
        if  (urls.length  \>  0)  {
            fs.writeFileSync(path.join(targetDir, tmproject["projectName"]  +  ".tmproject"),  JSON.stringify(tmproject));
        }
        tmproject =  {};
    }
  }
};
//根据数组开始扫描
for  (let _a =  0, resjsons_1 = resjsons; _a < resjsons_1.length; _a++)  {
  const resJson = resjsons_1[_a];
  _loop_1(resJson);
}
把这个脚本放在scripts文件夹内,这个脚本是根据项目的default.res.json文件的内容来生成tmpropject文件
在终端中执行:
node scripts/autoMerger.js
执行成功之后可以在resource文件夹中看到多出了一个TextureMerger文件夹,里面就是根据default.res.json分组生成的tmpropject文件。
现在只需要执行TextureMergerPlugin插件就可以自动合并,这里需要注意TextureMergerPlugin依赖 TextureMerger 1.7 以上的版本,如果不符合请自行安装,并且在运行时TextureMerger需要处于关闭状态。
new  TextureMergerPlugin({ textureMergerRoot:  ["resource"]  });
保存后在终端执行:
egret build
执行完成后,在微信开发者工具可以看到,resource > TextureMerger 内新增了三个png文件,就是合并之后的纹理集。游戏运行的时候只需要加载这三个纹理集就可以,无需加载那些单独的png文件但是需要去res.json里面配置,把单独的资源引用都删除,加上纹理集的引用。
这些操作当然不需要手动去完成,现在只需要使用ConvertResConfigFilePlugin插件就可以实现这个功能。
编辑config.wxgame.ts:
new  TextureMergerPlugin({ textureMergerRoot:  ["resource"]  });
new  ConvertResConfigFilePlugin({
 resourceConfigFiles:  [{ filename:  "resource/default.res.json", root:  "resource/"  }],
  nameSelector:  (p)  =>  {
  return path.basename(p).split(".").join("_");
  },
  TM_Verbose:  true
});
保存后在终端执行:
egret build
在微信开发者工具中,打开调试器,在network面板可以看到加载的纹理集。
这里有个注意事项,在游戏中点击英雄按钮,切换到英雄场景时,会发现列表里面的图片加载不出来。
在network面板可以看到加载请求是单独的png文件,而不是纹理集。
这是因为列表中的图片地址是直接使用url。
// 原始数组
let dataArr:  any[]  =  [
  { image:  "resource/art/heros_goods/heros01.png", name:  "亚特伍德", value:  "评价: 很特么厉害, 为所欲为", isSelected:  false  },
  { image:  "resource/art/heros_goods/heros02.png", name:  "亚特伍德", value:  "评价: 很特么厉害, 为所欲为", isSelected:  false  },
  { image:  "resource/art/heros_goods/heros03.png", name:  "亚特伍德", value:  "评价: 很特么厉害, 为所欲为", isSelected:  true  },
  { image:  "resource/art/heros_goods/heros04.png", name:  "亚特伍德", value:  "评价: 很特么厉害, 为所欲为", isSelected:  false  },
  { image:  "resource/art/heros_goods/heros05.png", name:  "亚特伍德", value:  "评价: 很特么厉害, 为所欲为", isSelected:  false  },
  { image:  "resource/art/heros_goods/heros06.png", name:  "亚特伍德", value:  "评价: 很特么厉害, 为所欲为", isSelected:  false  },
  { image:  "resource/art/heros_goods/heros07.png", name:  "亚特伍德", value:  "评价: 很特么厉害, 为所欲为", isSelected:  false  }
];
// 转成eui数据
let euiArr: eui.ArrayCollection  =  new  eui.ArrayCollection(dataArr);
// 把list_hero数据源设置成euiArr
this.list_hero.dataProvider  = euiArr;
// 设置list_hero的项呈视器 (这里直接写类名,而不是写实例)
this.list_hero.itemRenderer  = heroList_item;
这种引用方式的图片,需要开发者手动在代码中修改,将图片地址修改成纹理集中的图片。
结语
本文通过使用UglifyPlugin,ResSplitPlugin,ZipPlugin,TextureMergerPlugin,ConvertResConfigFilePlugin插件,使项目发布到微信小程序之后的代码包体积减小,用户发起的请求数变少,且将代码混淆压缩。
使用Egret自带的插件,已经可以满足开发者的基本需求,如果有针对项目的特殊需求,可以选择自定义插件。

