[Cordova/Phonegap] 打包Cordova App时,合并所有插件的js文件并压缩,提升App加载速度

原文链接:http://blog.csdn.net/lovelyelfpop/article/details/56014300



问题的背景和现象


Cordova App 添加的各种plugin插件,每个plugin都有自己的js文件。

App入口index.html中只引入了一个www目录下的cordova.js文件,此文件会在同一级目录下寻找cordova_plugins.js,然后根据这个文件中描述的各个plugin的路径,找到它们的js,并加载。

www目录结构如下:


注意:

  • 安卓的www目录为cordova/platforms/android/assets/www
  • ios的www目录为cordova/platforms/ios/www

如果我们用了很多的plugin,那么最后app需要加载的js文件就会变得非常多,加载变慢就拖慢了app的启动速度。如下图:



解决办法


browserify


那么,如何才能把这些js文件整合成为一个js文件呢?其实,cordova-cli命令行提供了参数--browserify,具体可以查看官方文档:

https://cordova.apache.org/docs/en/latest/reference/cordova-cli/#cordova-prepare-command


cordova prepare、cordova build、cordova run命令都可以使用--browserify参数,以build为例:

cordova build android --browserify


最后打包的目录下的cordova.js文件变大了:


这就是因为cordova.js、cordova_plugins.js以及cordova-js-src目录和plugins目录下的js,都打包到了一起。


最后App加载的结果是这样:



是不是少多了?


Cordova Hooks


当然,上面的--browserify参数只是把插件js打包进了cordova.js,但是并没有把cordova_plugins.js、cordova-js-src目录和plugins目录下的js删除掉。

如果你想把这些无用的js删掉的话,变成下面的结构:


此时,cordova hooks就派上用场了。

hooks,中文意思是钩子。

cordova在build过程中分为各个阶段,如下表格:

Hook 类型 相关命令 描述
before_platform_add cordova platform add To be executed before and after adding a platform.
after_platform_add
before_prepare cordova prepare
cordova platform add
cordova build
cordova run
To be executed before and after preparing your application.
after_prepare
before_compile cordova compile
cordova build
To be executed before and after compiling your application.
after_compile
before_deploy cordova emulate
cordova run
To be executed before deploying your application.
before_build cordova build To be executed before and after building your application.
after_build
具体请看官方文档 https://cordova.apache.org/docs/en/latest/guide/appdev/hooks/index.html

每个hook就相当于一个事件,我们可以监听它,然后执行我们的自定义操作。


我们要做的就是在after_prepare阶段,把www目录下的无用文件删除掉,并且压缩cordova.js。

我就直接提供代码了。

1、建立目录cordova/scripts/after_prepare,把下面的代码保存为minify.js,放到after_prepare目录下

#!/usr/bin/env node

module.exports = function(ctx) {
    var fs = ctx.requireCordovaModule('fs'),
        path = ctx.requireCordovaModule('path'),
        shell = ctx.requireCordovaModule("shelljs"),
        UglifyJS = require('uglify-js'),
        CleanCSS = require('clean-css'),
        htmlMinify = require('html-minifier').minify,
        cssOptions = {
            keepSpecialComments: 0
        },
        cssMinifier = new CleanCSS(cssOptions),

        rootDir = ctx.opts.projectRoot,
        platformPath = path.join(rootDir, 'platforms'),
        platforms = ctx.opts.cordova.platforms,
        platform = platforms.length ? platforms[0] : '',
        cliCommand = ctx.cmdLine,

        debug = true, //false

        htmlOptions = {
            removeAttributeQuotes: true,
            removeComments: true,
            minifyJS: true,
            minifyCSS: cssOptions,
            collapseWhitespace: true,
            conservativeCollapse: true,
            removeComments: true,
            removeEmptyAttributes: true
        },

        successCounter = 0,
        errorCounter = 0,
        notProcessedCounter = 0,
        pendingCounter = 0,

        hasStartedProcessing = false,
        processRoot = false,
        isBrowserify = (cliCommand.indexOf('--browserify') > -1), //added
        //isRelease = true;
        isRelease = (cliCommand.indexOf('--release') > -1); // comment the above line and uncomment this line to turn the hook on only for release

    function processFiles(dir, _noRecursive) {
        fs.readdir(dir, function (err, list) {
            if (err) {
                // console.error('processFiles - reading directories error: ' + err);
                return;
            }
            list.forEach(function(file) {
                file = path.join(dir, file);
                fs.stat(file, function(err, stat) {
                    hasStartedProcessing = true;
                    if (stat.isDirectory()) {
                        if (!_noRecursive) processFiles(file);
                    } else {
                        compress(file);
                    }
                });
            });
        });
    }
    function processFile(file) {
        fs.stat(file, function(err, stat) {
            hasStartedProcessing = true;
            compress(file);
        });
    }

    function compress(file) {
        var ext = path.extname(file);
        switch(ext.toLowerCase()) {
            case '.js':
                (debug) && console.log('Compressing/Uglifying JS File: ' + file);
                var result = UglifyJS.minify(file, {
                    compress: {
                        dead_code: true,
                        loops: true,
                        if_return: true,
                        keep_fargs: true,
                        keep_fnames: true
                    }
                });
                if (!result || !result.code || result.code.length == 0) {
                    errorCounter++;
                    console.error('\x1b[31mEncountered an error minifying a file: %s\x1b[0m', file);
                }
                else {
                    successCounter++;
                    fs.writeFileSync(file, result.code, 'utf8');
                    (debug) && console.log('Optimized: ' + file);
                }
                break;
            case '.css':
                (debug) && console.log('Minifying CSS File: ' + file);
                var source = fs.readFileSync(file, 'utf8');
                if (!source || source.length == 0) {
                    errorCounter++;
                    console.error('Encountered an empty file: ' + file);
                }
                else {
                    var result = cssMinifier.minify(source).styles;
                    if (!result || result.length == 0) {
                        errorCounter++;
                        console.error('\x1b[31mEncountered an error minifying a file: %s\x1b[0m', file);
                    }
                    else {
                        successCounter++;
                        fs.writeFileSync(file, result, 'utf8');
                        (debug) && console.log('Optimized: ' + file);
                    }
                }
                break;
            case '.html':
                (debug) && console.log('Minifying HTML File: ' + file);
                var source = fs.readFileSync(file, 'utf8');
                if (!source || source.length == 0) {
                    errorCounter++;
                    console.error('Encountered an empty file: ' + file);
                }
                else {
                    var result = htmlMinify(source, htmlOptions);
                    if (!result || result.length == 0) {
                        errorCounter++;
                        console.error('\x1b[31mEncountered an error minifying a file: %s\x1b[0m', file);
                    }
                    else {
                        successCounter++;
                        fs.writeFileSync(file, result, 'utf8');
                        (debug) && console.log('Optimized: ' + file);
                    }
                }
                break;
            default:
                console.error('Encountered file with ' + ext + ' extension - not compressing.');
                notProcessedCounter++;
                break;
        }
    }

    function checkIfFinished() {
        if (hasStartedProcessing && pendingCounter == 0) console.log('\x1b[36m%s %s %s\x1b[0m', successCounter + (successCounter == 1 ? ' file ' : ' files ') + 'minified.', errorCounter + (errorCounter == 1 ? ' file ' : ' files ') + 'had errors.', notProcessedCounter + (notProcessedCounter == 1 ? ' file was ' : ' files were ') + 'not processed.');
        else setTimeout(checkIfFinished, 10);
    }


    switch (platform) {
        case 'android':
            platformPath = path.join(platformPath, platform, "assets", "www");
            break;
        case 'ios':
            platformPath = path.join(platformPath, platform, "www");
            break;
        default:
            console.error('Hook currently supports only Android and iOS');
            return;
    }

    if(isBrowserify) {
        shell.rm('-rf', path.join(platformPath, 'cordova-js-src'));
        shell.rm('-rf', path.join(platformPath, 'plugins'));
        shell.rm('-f', path.join(platformPath, 'cordova_plugins.js'));
    }
    if (!isRelease) {
        return;
    }

    console.log('cordova-minify STARTING - minifying your js, css, html. Sit back and relax!');


    //minify files inside these directories
    //var foldersToProcess = ['javascript', 'style', 'js', 'css', 'html'];
    var foldersToProcess = isBrowserify ? [] : ['cordova-js-src', 'plugins'];

    if (processRoot) processFiles(platformPath, true);

    foldersToProcess.forEach(function(folder) {
        processFiles(path.join(platformPath, folder));
    });

    //minify files one by one
    var filesToProcess = ['cordova.js'];
    if(!isBrowserify) filesToProcess.push('cordova_plugins.js');

    filesToProcess.forEach(function(file) {
        processFile(path.join(platformPath, file));
    });

    checkIfFinished();
};

上述代码来自:https://github.com/adamerny/cordova-minify-v2


2、打开cordova/plugins.xml文件,在其中加一句

<hook type="after_prepare" src="scripts/after_prepare/minify.js" />

如下图:


3、打开命令行界面,全局安装下面的npm包

npm install -g clean-css html-minifier uglify-js

minify.js 会用到上面的包


好了,现在执行Cordova的打包命令

Debug 打包:运行cordova build android --browserify,完成后就没有多余的插件js文件了;

Release打包:运行cordova build android --release --browserify,不仅删除了多余的插件js文件,还把cordova.js压缩了。

ps: 如果你想要在Debug打包时,也压缩cordova.js文件,那就把上面hook脚本中的isRelease设为true。



Sencha+Cordova


上面是针对cordova项目来说的,如果你是Sencha项目(Touch或ExtJS6 Modern),需要修改文件:

Sencha项目\.sencha\app\cordova-impl.xml

在每个命令后面加上--browserify


这样,以后Sencha app build xxx就可以整合压缩cordova.js了。




实际测试,app的加载速度提升不到1秒。

应该设备越低端,提升越明显吧。(忽略ios,ios本来就快)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神秘_博士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值