使用Gulp在PHP文件中压缩内联Javascript

3
我想做的是使用Gulp压缩php文件中的内联javascript <script>。原因是我想压缩整个文件的大小(在我的情况下确实很重要)。javascript包含php变量。
我已经使用gulp-minify-inline-scripts插件开始了这项工作,但更改了它,以便识别php文件。不幸的是,除非使用html文件,否则我无法成功输出javascript。
我的想法是保留php变量并将内容保存回php文件中,而不是编译实际的php。 插件代码:
var path = require('path');

var gutil = require('gulp-util');
var uglify = require('uglify-js');
var through = require('through2');

var PLUGIN_NAME = 'gulp-minify-inline-scripts';

module.exports = function (options) {
    return through.obj(function (file, enc, cb) {
        var self = this;

        if (file.isNull()) {
            this.push(file);
            return cb();
        }

        if (file.isStream()) {
            this.emit('error', new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
            return cb();
        }

        var fileName = file.path.split(path.sep).pop();

        //html file only
        if (!/^\.php?$/.test(path.extname(file.path))) {
            gutil.log(gutil.colors.red('[WARN] file ' + fileName + ' is not a html file'));
            this.push(file);
            return cb();
        }

        gutil.log("uglify inline scripts in html file: " + file.path);

        var html = file.contents.toString('utf8');
        var reg = /(<script(?![^>]*?\b(type=['"]text\/template['"]|src=["']))[^>]*?>)([\s\S]*?)<\/script>/g;


        html = html.replace(reg, function (str, tagStart, attr, script) {
            try {
                var result = uglify.minify(script, { fromString: true });

                return tagStart + result.code + '</script>';
            } catch (e) {
                self.emit('error', new gutil.PluginError(PLUGIN_NAME, 'uglify inline scripts error: ' + (e && e.stack)));
            }
        });


        file.contents = new Buffer(html);
        this.push(file);
        cb();
    });
};

将PHP内联Javascript压缩:

<?php 

/* Start: This is a php file

?>

<script>
    <?php $var = 'test'; ?>    
    A.config = {
        test: '<?php echo $var; ?>'
    };


    a.visible = function (image, distance) {
        var viewportWidth  = window.innerWidth || document.documentElement.clientWidth;
        var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
        var bounds;
        var gap;

        if (!image.getBoundingClientRect) {
            return false;   
        }
    };
</script>

<?php 

/* End: This is a php file

?>

1
一般情况下是不可能做到的。 - zerkms
1
通常情况下,结果取决于PHP运行时。 - zerkms
1
可能是有可能的,但我怀疑gulp能否处理这个。你需要编写自己的版本来“识别”JS代码中的PHP变量,并知道它们必须保持“不变”。此外,根据你的PHP代码,这可能会引入无法通过合理解析器处理的复杂性。 - CBroe
我建议你还是寻找一个用PHP编写的JS压缩工具,这样你就可以在替换其中的PHP变量后,直接将你的JS代码传递给它。(使用Heredoc语法来嵌入PHP中的JS代码可能是个不错的主意。) - CBroe
感谢@CBroe的帮助。 - stwhite
显示剩余2条评论
1个回答

2
对于任何遇到此问题的人,我已经根据 @Cbroe 在评论中的提示撰写了一个解决方案。它非常简单:将php代码包装起来,以避免破坏jsminifier,然后在代码被压缩后,删除包装的php字符串以释放php变量。你可以通过选择更好的包装字符串来进一步简化这个过程。
但需要注意的是,由于我仅测试了特定使用情况,因此在使用时请谨慎考虑。 更新:我已经使用了几个月,从未出现过问题。只需确保使用与正则表达式中放置的脚本标记模式相同的标记,否则它可能无法在页面中找到该标记。 新的Gulp插件代码:
var path = require('path');
var gutil = require('gulp-util');
var uglify = require('uglify-js');
var through = require('through2');
var PLUGIN_NAME = 'gulp-minify-inline-scripts';

module.exports = function(options) {
    return through.obj(function(file, enc, cb) {
        var self = this;

        if (file.isNull()) {
            this.push(file);
            return cb();
        }

        if (file.isStream()) {
            this.emit('error', new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
            return cb();
        }

        var fileName = file.path.split(path.sep).pop();

        //html file only
        if (!/^\.php?$/.test(path.extname(file.path))) {
            gutil.log(gutil.colors.red('[WARN] file ' + fileName + ' is not a html file'));
            this.push(file);
            return cb();
        }

        gutil.log("uglify inline scripts in html file: " + file.path);

        var html = file.contents.toString('utf8');
        var reg = /(<script(?![^>]*?\b(type=['"]text\/template['"]|src=["']))[^>]*?>)([\s\S]*?)<\/script>/g;

        html = html.replace(reg, function(str, tagStart, attr, script) {
            try {
                var result = uglify.minify(script, {
                    fromString: true
                });

                var code = result.code;

                code = code.split("STRIP__>\"").join('');
                code = code.split("\"<__STRIP").join('');

                return tagStart + code + '</script>';
            } catch (e) {
                self.emit('error', new gutil.PluginError(PLUGIN_NAME, 'uglify inline scripts error: ' + (e && e.stack)));
            }
        });

        file.contents = new Buffer(html);

        this.push(file);

        cb();
    });
};

PHP内联JavaScript压缩:

<?php 

/* Start: This is a php file

?>

<script>
    <?php $var = 'test'; ?>    
    A.config = {
        test: "<__STRIP'<?php echo $var; ?>'STRIP__>"
    };

    a.visible = function (image, distance) {
        var viewportWidth  = window.innerWidth || document.documentElement.clientWidth;
        var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
        var bounds;
        var gap;

        if (!image.getBoundingClientRect) {
            return false;   
        }
    };
</script>

<?php 

/* End: This is a php file

?>

你不必要地将代码压缩的特定实现与修复不是最佳实践的代码约定紧密联系在一起。你应该查看WordPress的https://codex.wordpress.org/Function_Reference/wp_localize_script,以了解如何向页面添加数据以供脚本使用,而无需将其转换为内联php输出。 - NoBugs
我正在努力进一步了解这里存在什么代码约定问题?因为如果我从内联脚本调用函数到JS文件中,那么JS文件需要被加载以便该函数运行。将脚本放置在内联位置,则不需要加载JS文件。我也不想每次请求页面时都要在php中编译内联JS。 - stwhite
将每个脚本或CSS都放在页面上而不是单独的文件中,也是为了节省加载各种文件的一点开销的一种方式,但这通常不被推荐。最好使用闭包编译器或其他工具来组合它,如果您需要更快的速度。在一个文件中混合数据、模型和两种不同的编程语言也不是最佳实践。 - NoBugs
当然。你完全正确。我只是看到了一些情况,其中一点行内JS并不会造成太大的影响。当涉及到“最佳实践”与速度时,肯定存在权衡。特别是考虑到许多大型网站都在做我正在做的事情... 其中之一就是谷歌...话虽如此,我明白你的意思。 - stwhite
是的,谷歌发明了Closure编译器和GWT以进行优化。我敢肯定他们的开发人员实际上并没有在JS中编写那些两个字母的变量或者在Google主页上输入内联代码 :) - NoBugs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接