• 使用Egret插件压缩代码包体积,减少请求数量的实战教程


    在白鹭引擎发布了5.2.7版本中新增加了命令行,增加自动合图插件TextureMergerPlugin功能。今天,我们以一个EUI案例来展示自动合图插件的具体使用方法和注意事项。

    此外,我们在本文还融入了UglifyPlugin、ResSplitPlugin、ZipPlugin等插件使用方法。开发者利用上述4款插件,将实现代码包体积更小、更好管理的目标。

    目录:

    使用UglifyPlugin将代码混淆压缩
    使用ResSplitPlugin把部分资源分离出去
    使用ZipPlugin把文件压缩成zip格式
    使用TextureMergerPlugin将纹理合并,且用ConvertResConfigFilePlugin修改res.json配置文件

    项目初始化

    1. 把index.html中的data-scale-mode改成fixedWidth
    2. 打开EgretLauncher,将本项目发布成微信小游戏
    3. 打开微信开发者工具

    使用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'), // 非 EUI 项目关闭此设置
                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 :

    /**
     * 示例自定义插件,您可以查阅 http://developer.egret.com/cn/2d/projectConfig/cmdExtensionPluginin/
     * 了解如何开发一个自定义插件
     */
    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('
    ')
                let lib = null
                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('
    ')
                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 下的aboutheros_goods已经不在了。

    被分离出去的在项目根目录中 egret-eui-demo_wxgame_remote 文件夹内。

    使用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, 可以用两种方式生成:

    1. 使用TextureMerger工具
    2. 执行脚本生成

    这里使用第二种方法,使用脚本autoMerger.js:

    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var fs = require("fs");
    var path = require("path");
    var resjsons = ["resource/default.res.json"]; //要扫描的res.json文件
    var targetDir = "resource/TextureMerger"; //输出目录
    var pathNor = path.relative(targetDir, "resource"); //返回一个相对路径
    var tempindex = 0;
    //创建输出文件夹
    if (resjsons.length > 0) {
        if (!fs.existsSync(targetDir)) {
    
            // var paths = path.normalize(targetDir).split("\");   //windows 下使用
            var paths = path.normalize(targetDir).split("/");   //mac linux 下使用
    
            var target = ".";
            for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) {
                var p = paths_1[_i];
    
                // target += ("\" + p);  // windows 下使用
                target += ("/" + p);  // mac linux 下使用
    
                if (!fs.existsSync(target))
                    // 根据路径创建文件夹
                    fs.mkdirSync(target);
            }
        }
    }
    var _loop_1 = function (resJson) {
        // 判断是否是res.json文件
        if (fs.existsSync(resJson) && resJson.indexOf("res.json") > -1) {
            var defaultJson = fs.readFileSync(resJson, "utf-8");
            // 解析res.json文件内容
            var defaultObject = JSON.parse(defaultJson);
            var groups = defaultObject.groups; //组
            var resources = defaultObject.resources; //资源
            var resourcesHash_1 = {}; // 用来存放resources的资源信息
    
            // 遍历resources
            for (var _i = 0, resources_1 = resources; _i < resources_1.length; _i++) {
                var resource = resources_1[_i];
                resourcesHash_1[resource.name] = resource.url;
            }
    
            // 遍历groups
            for (var _a = 0, groups_1 = groups; _a < groups_1.length; _a++) {
                var group = groups_1[_a];
                var 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, 并分割成数组
                var oldkeys = group.keys.split(",");
                var oldkeysHash = {};
                // 遍历oldkeys
                for (var _b = 0, oldkeys_1 = oldkeys; _b < oldkeys_1.length; _b++) {
                    var key = oldkeys_1[_b];
                    // 保存到oldkeysHash对象中
                    oldkeysHash[key] = true;
                }
    
                var newKeys = [];
                // 遍历oldkeys
                for (var _c = 0, oldkeys_2 = oldkeys; _c < oldkeys_2.length; _c++) {
                    var 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路径
                var 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 (var _a = 0, resjsons_1 = resjsons; _a < resjsons_1.length; _a++) {
        var 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(),
    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自带的插件,已经可以满足开发者的基本需求,如果有针对项目的特殊需求,可以选择自定义插件

  • 相关阅读:
    JavaScript借助xpath操纵xml数据(一)
    JavaScript借助xpath操纵xml数据(二)
    保留域——$$Return
    ExtJS中get、getDom、getCmp、getBody、getDoc使用 javascript
    关于编辑器编码保存为utf8问题
    query 用法
    简历模版(抛砖引玉)总觉得自己简历做得不够好的朋友请进来
    sql 查询慢的48个原因分析
    Word 实用技巧整理
    JAVA文件操作大全
  • 原文地址:https://www.cnblogs.com/10manongit/p/12770759.html
Copyright © 2020-2023  润新知