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

来源:互联网 发布:小黑裙香水淘宝 编辑:程序博客网 时间:2024/05/22 03:10

原文链接: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_addcordova platform addTo be executed before and after adding a platform.after_platform_addbefore_preparecordova prepare
cordova platform add
cordova build
cordova run
To be executed before and after preparing your application.after_preparebefore_compilecordova compile
cordova build
To be executed before and after compiling your application.after_compilebefore_deploycordova emulate
cordova run
To be executed before deploying your application.before_buildcordova buildTo 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 nodemodule.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本来就快)

0 0
原创粉丝点击