在 Symfony/Twig 响应中缩小/丑化嵌入 JavaScript
Minify / Uglify embedded JavaScript in Symfony/Twig response
在我的 Symfony 2.8
项目中使用 Assetic
时,压缩链接的 JavaScript
和 CSS
文件没什么大不了的。但是使用 script
标签直接嵌入页面的脚本呢?这些脚本不会以任何方式修改。
是否也可以 compress/modify/uglify 这些脚本?
当然,我可以简单地将这些脚本移动到单独的文件中,从而也对它们应用 Assetic
过滤器。但在某些情况下,将脚本直接放在 HTML / Twig 模板中会很方便。
那么,有没有现有的解决方案可以在不移动它们的情况下过滤这些脚本?
因为我找不到解决这个问题的方法,所以我终于设法创建了自己的:将自定义标签添加到我的(现有)Twig 扩展中,以将 Assetics uglifyJS2
过滤器应用于嵌入式脚本:
第 1 步: 创建一个 Twig TokenParser
和 Node
class UglifyTokenParser extends Twig_TokenParser {
private $enabled;
public function __construct($enabled = true) {
$this->enabled = (bool) $enabled;
}
public function parse(Twig_Token $token) {
$lineNumber = $token->getLine();
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(function (Twig_Token $token) {
return $token->test('enduglify');
}, true);
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
if ($this->enabled) {
$node = new UglifyNode($body, $lineNumber, $this->getTag());
return $node;
}
return $body;
}
public function getTag() {
return 'uglify';
}
}
class UglifyNode extends \Twig_Node {
public function __construct(Twig_Node $body, $lineNumber, $tag = 'uglify') {
parent::__construct(array('body' => $body), array(), $lineNumber, $tag);
}
public function compile(Twig_Compiler $compiler) {
$compiler
->addDebugInfo($this)
->write("ob_start();\n")
->subcompile($this->getNode('body'))
->write("echo $context['_uglifier']->uglify(trim(ob_get_clean()));\n");
}
}
第2步:添加Uglifier
class,Node
使用它来将内容传递给AsseticUglifyJS2Filter
class Uglifier {
private $filter;
private $asset;
private $enabled;
public function __construct(FilterInterface $filter, $endabled) {
$this->filter = $filter;
$this->asset = new UglifierAsset(array($this->filter));
$this->enabled = $endabled;
}
public function uglify($content) {
if ($this->enabled) {
$this->asset->loadContent($content);
$uglified = $this->asset->dump();
return $uglified;
} else {
return $content;
}
}
}
class UglifierAsset extends BaseAsset {
public function __construct($filters = array()) {
parent::__construct($filters);
}
private $theContent;
public function loadContent($content) {
$this->theContent = $content;
$this->load();
}
public function load(FilterInterface $additionalFilter = null) {
$this->doLoad($this->theContent);
}
public function getLastModified() {
return 0;
}
}
第 3 步: 创建 Uglifier
作为 sService,将其传递给 Twig Extension
并在扩展中实现自定义标记
app.twig_extension:
class: AppBundle\Twig\AppExtension
public: false
arguments: [ "@app.twig_extension.uglifier" ]
tags:
- { name: twig.extension }
app.twig_extension.uglifier:
class: AppBundle\Twig\uglify\Uglifier
arguments: [ "@assetic.filter.uglifyjs2", "%twig_extension.unglifier.enabled%" ]
class AppExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface {
private $uglifier;
public function __construct(Uglifier $uglifier) {
$this->uglifier = $uglifier;
}
public function getGlobals() {
return array(
'_uglifier' => $this->uglifier,
);
}
public function getTokenParsers() {
return array(new UglifyTokenParser());
}
}
第 4 步: 使用新的 `{% uglify %} 标签包装嵌入式脚本
{# before #}
<script type="text/javascript">
// some JS
</script>
{# after #}
<script type="text/javascript">
{% uglify %}
// some JS
{% enduglify %}
</script>
完成
在我的 Symfony 2.8
项目中使用 Assetic
时,压缩链接的 JavaScript
和 CSS
文件没什么大不了的。但是使用 script
标签直接嵌入页面的脚本呢?这些脚本不会以任何方式修改。
是否也可以 compress/modify/uglify 这些脚本?
当然,我可以简单地将这些脚本移动到单独的文件中,从而也对它们应用 Assetic
过滤器。但在某些情况下,将脚本直接放在 HTML / Twig 模板中会很方便。
那么,有没有现有的解决方案可以在不移动它们的情况下过滤这些脚本?
因为我找不到解决这个问题的方法,所以我终于设法创建了自己的:将自定义标签添加到我的(现有)Twig 扩展中,以将 Assetics uglifyJS2
过滤器应用于嵌入式脚本:
第 1 步: 创建一个 Twig TokenParser
和 Node
class UglifyTokenParser extends Twig_TokenParser {
private $enabled;
public function __construct($enabled = true) {
$this->enabled = (bool) $enabled;
}
public function parse(Twig_Token $token) {
$lineNumber = $token->getLine();
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(function (Twig_Token $token) {
return $token->test('enduglify');
}, true);
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
if ($this->enabled) {
$node = new UglifyNode($body, $lineNumber, $this->getTag());
return $node;
}
return $body;
}
public function getTag() {
return 'uglify';
}
}
class UglifyNode extends \Twig_Node {
public function __construct(Twig_Node $body, $lineNumber, $tag = 'uglify') {
parent::__construct(array('body' => $body), array(), $lineNumber, $tag);
}
public function compile(Twig_Compiler $compiler) {
$compiler
->addDebugInfo($this)
->write("ob_start();\n")
->subcompile($this->getNode('body'))
->write("echo $context['_uglifier']->uglify(trim(ob_get_clean()));\n");
}
}
第2步:添加Uglifier
class,Node
使用它来将内容传递给AsseticUglifyJS2Filter
class Uglifier {
private $filter;
private $asset;
private $enabled;
public function __construct(FilterInterface $filter, $endabled) {
$this->filter = $filter;
$this->asset = new UglifierAsset(array($this->filter));
$this->enabled = $endabled;
}
public function uglify($content) {
if ($this->enabled) {
$this->asset->loadContent($content);
$uglified = $this->asset->dump();
return $uglified;
} else {
return $content;
}
}
}
class UglifierAsset extends BaseAsset {
public function __construct($filters = array()) {
parent::__construct($filters);
}
private $theContent;
public function loadContent($content) {
$this->theContent = $content;
$this->load();
}
public function load(FilterInterface $additionalFilter = null) {
$this->doLoad($this->theContent);
}
public function getLastModified() {
return 0;
}
}
第 3 步: 创建 Uglifier
作为 sService,将其传递给 Twig Extension
并在扩展中实现自定义标记
app.twig_extension:
class: AppBundle\Twig\AppExtension
public: false
arguments: [ "@app.twig_extension.uglifier" ]
tags:
- { name: twig.extension }
app.twig_extension.uglifier:
class: AppBundle\Twig\uglify\Uglifier
arguments: [ "@assetic.filter.uglifyjs2", "%twig_extension.unglifier.enabled%" ]
class AppExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface {
private $uglifier;
public function __construct(Uglifier $uglifier) {
$this->uglifier = $uglifier;
}
public function getGlobals() {
return array(
'_uglifier' => $this->uglifier,
);
}
public function getTokenParsers() {
return array(new UglifyTokenParser());
}
}
第 4 步: 使用新的 `{% uglify %} 标签包装嵌入式脚本
{# before #}
<script type="text/javascript">
// some JS
</script>
{# after #}
<script type="text/javascript">
{% uglify %}
// some JS
{% enduglify %}
</script>
完成