如何使用Assetic进行资源管理
安装和开启Assetic ¶
从Symfony 2.8开始,Assetic不再是Symofny标准版框架的自带内容。在使用其功能之前,请先在你的项目中执行以下命令,安装AcceticBundle:
1 |
$ composer require symfony/assetic-bundle |
然后,在你Symfony程序的 AppKernel.php
文件中开启bundle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
// ...
public function registerBundles()
{
$bundles = array(
// ...
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
);
// ...
}
} |
最后,添加下列最小化配置以开启程序对Assetic的支持:
1 2 3 4 5 6 7 8 |
# app/config/config.yml
assetic:
debug: '%kernel.debug%'
use_controller: '%kernel.debug%'
filters:
cssrewrite: ~
# ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="Http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:assetic="http://symfony.com/schema/dic/assetic"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/assetic
http://symfony.com/schema/dic/assetic/assetic-1.0.xsd">
<assetic:config debug="%kernel.debug%" use-controller="%kernel.debug%">
<assetic:filters cssrewrite="null" />
</assetic:config>
<!-- ... -->
</container> |
1 2 3 4 5 6 7 8 9 10 11 |
// app/config/config.php
$container->loadFromExtension('assetic', array(
'debug' => '%kernel.debug%',
'use_controller' => '%kernel.debug%',
'filters' => array(
'cssrewrite' => null,
),
// ...
));
// ... |
Assetic简介 ¶
Assetic包括两项主要功能: assets 和 filters。asset(资源)是指CSS, JavaScript 和 image 文件。调节器(filters)则是在这些文件被送到浏览器之前,可以作用于它们的工具。这能达成一种 “存于程序中(某处)的资源文件” 和 “真实呈现给用户的文件” 之间的分离。
如果没有Assetic,你在程序中只能提供“直接存储在程序中(的web目录)”的文件:
1 |
<script src="{{ asset('js/script.js') }}"></script> |
1 |
<script src="<?php echo $view['assets']->getUrl('js/script.js') ?>"></script> |
但如果 使用 Assetic,在它们被提供(给浏览器)之前,你可以随需操作这些资源(或从任意位置加载它们)。这意味着你可以:
最小化与合并全部CSS和JS文件
通过某些compiler,例如LESS,SASS或CoffeeScript,运行全部(或一部分)CSS和JS文件
对图片进行优化
Assets ¶
使用Assetic,相比直接提供文件,有多项优势。文件不需要存放在显示它们的地方,而是可以从各种源头(比如一个bundle)中拖出来(使用)。
你可以使用Assetic来处理 css样式表、JavaScript文件 和 图片。它们的背后思想大致相同,但语法细节略有不同。
包容JavaScript文件 ¶
要包容JavaScript文件,可在任意模板中使用 javascripts
标签:
1 2 3 |
{% javascripts '@AppBundle/Resources/public/js/*' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %} |
1 2 3 4 5 |
<?php foreach ($view['assetic']->javascripts(
array('@AppBundle/Resources/public/js/*')
) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach ?> |
如果你的程序模板使用的是Symfony标准版框架中的默认块名(block names),javascripts
标签则最常被置于 javascripts
block之中:
1 2 3 4 5 6 7 |
{# ... #}
{% block javascripts %}
{% javascripts '@AppBundle/Resources/public/js/*' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
{% endblock %}
{# ... #} |
你也可以包容CSS样式:参考包容CSS样式表。
本例中,AppBundle的 Resources/public/js/
目录下的所有文件将被加载,然后从另外一个地方来提供。真实渲染后的标签可能是下面这样:
1 |
<script src="/app_dev.php/js/abcd123.js"></script> |
这里有个关键点:一旦你让Assetic处理资源,文件就会从另外一个地方被提供出来。这 将 引发那些内含相对路径的CSS文件的问题。参考 使用cssrewrite调节器修复CSS路径。
包容CSS样式表 ¶
要引入CSS样式,你可以使用和上例相同的技巧,除了使用 stylesheets
标签:
1 2 3 |
{% stylesheets 'bundles/app/css/*' filter='cssrewrite' %}
<link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %} |
1 2 3 4 5 6 |
<?php foreach ($view['assetic']->stylesheets(
array('bundles/app/css/*'),
array('cssrewrite')
) as $url): ?>
<link rel="stylesheet" href="<?php echo $view->escape($url) ?>" />
<?php endforeach ?> |
如果你的程序模板使用的是Symfony标准版框架中的默认块名(block names),stylesheets
标签则最常被置于 stylesheets
block之中:
1 2 3 4 5 6 7 |
{# ... #}
{% block stylesheets %}
{% stylesheets 'bundles/app/css/*' filter='cssrewrite' %}
<link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}
{% endblock %}
{# ... #} |
但由于Assetic改变了你的资源路径,这 将 破坏使用了“相对路径”的背景图片(或其他路径),除非你 使用cssrewrite调节器修复CSS路径。
注意原来那个包容了JavaScript文件,你引用的文件使用了类似 @AppBundle/Resources/public/file.js
的路径,但那种情况在本例中,你所引用的CSS文件用是它们自己的真实的、可公开访问的路径:bundles/app/css
。你用哪种都行,除了当你对CSS样式表使用 @AppBundle
语法时,存在已知的问题会导致 cssrewrite
调节器失效。
包容图片 ¶
要包容图片你应使用 image
标签:
1 2 3 |
{% image '@AppBundle/Resources/public/images/example.jpg' %}
<img src="{{ asset_url }}" alt="Example" />
{% endimage %} |
1 2 3 4 5 |
<?php foreach ($view['assetic']->image(
array('@AppBundle/Resources/public/images/example.jpg')
) as $url): ?>
<img src="<?php echo $view->escape($url) ?>" alt="Example" />
<?php endforeach ?> |
你也可以使用Assetic来优化图片。更多信息参考如何配合Twig函数使用Assetic来对图片进行优化
不同于用Assetic来包容图片,你也可以考虑使用 LiipImagineBundle 社区bundle,它可以让你在提供图片之前对其进行压缩和操作(翻转、缩小、加水印,等等)。
使用cssrewrite调节器修复CSS路径 ¶
由于Assetic为你的资源生成了新的URLs,CSS文件中的任何相对路径都将失效。要修复此点,确保在 stylesheets
中使用了 cssrewrite
调节器。这会解析你的CSS文件并在内部修正路径,以反射到全新位置。
在前面小节中的你可以看到一个示例。
当使用 cssrewrite
filter时,不要通过 @AppBundle
语法引用你的CSS文件。 参考上面小节的 “note” 区域内容。
合并资源 ¶
Assetic的一个功能,是把多个文件合并成一个。这可以减少HTTP请求次数,提高了前端性能。它还能让你更轻松地维护那些”被分割成可管理的若干部分”的文件。这可以提高可复用性,因为你可以轻松地把“特定项目”的文件分割之后“用在其他程序中”,但还是会以一个单一文件提供出来:
1 2 3 4 5 6 |
{% javascripts
'@AppBundle/Resources/public/js/*'
'@AcmeBarBundle/Resources/public/js/form.js'
'@AcmeBarBundle/Resources/public/js/calendar.js' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %} |
1 2 3 4 5 6 7 8 9 |
<?php foreach ($view['assetic']->javascripts(
array(
'@AppBundle/Resources/public/js/*',
'@AcmeBarBundle/Resources/public/js/form.js',
'@AcmeBarBundle/Resources/public/js/calendar.js',
)
) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach ?> |
在 dev
环境下,每个文件仍然会被独立提供,以方便调试。但是在 prod
环境
(或者更特殊的, debug
旗标被设为 false
时),这会以一个单一的 script
标签进行渲染,里面有全部JavaScript文件的内容。
如果你刚接触Assetic,并且程序是 prod
环境(使用的是 app.php
控制器),你可能会看到所有CSS和JS无法执行。别急,这是有意为之的。在 prod
环境使用Assetic的细节,参考 剥离Asset文件。
合并文件时不只是适用于 你的 文件。还可以使用Assetic来合并第三方bundle的资源,例如jquery,连同你自己的资源,合并为单一文件:
1 2 3 4 5 |
{% javascripts
'@AppBundle/Resources/public/js/thirdparty/jquery.js'
'@AppBundle/Resources/public/js/*' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %} |
1 2 3 4 5 6 7 8 |
<?php foreach ($view['assetic']->javascripts(
array(
'@AppBundle/Resources/public/js/thirdparty/jquery.js',
'@AppBundle/Resources/public/js/*',
)
) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach ?> |
使用命名资源 ¶
AsseticBundle的配置指令,允许你定义命名资源集。你可以通过在配置信息中 assetic
根键下定义输入文件,调节器和输出文件来实现。参考 AsseticBundle配置信息。
1 2 3 4 5 6 7 |
# app/config/config.yml
assetic:
assets:
jquery_and_ui:
inputs:
- '@AppBundle/Resources/public/js/thirdparty/jquery.js'
- '@AppBundle/Resources/public/js/thirdparty/jquery.ui.js' |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:assetic="http://symfony.com/schema/dic/assetic"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/assetic
http://symfony.com/schema/dic/assetic/assetic-1.0.xsd">
<assetic:config>
<assetic:asset name="jquery_and_ui">
<assetic:input>@AppBundle/Resources/public/js/thirdparty/jquery.js</assetic:input>
<assetic:input>@AppBundle/Resources/public/js/thirdparty/jquery.ui.js</assetic:input>
</assetic:asset>
</assetic:config>
</container> |
1 2 3 4 5 6 7 8 9 10 11 |
// app/config/config.php
$container->loadFromExtension('assetic', array(
'assets' => array(
'jquery_and_ui' => array(
'inputs' => array(
'@AppBundle/Resources/public/js/thirdparty/jquery.js',
'@AppBundle/Resources/public/js/thirdparty/jquery.ui.js',
),
),
),
); |
在你定义了命名资源之后,可以在模板中使用 @named_asset
注释来引用它们:
1 2 3 4 5 |
{% javascripts
'@jquery_and_ui'
'@AppBundle/Resources/public/js/*' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %} |
1 2 3 4 5 6 7 8 |
<?php foreach ($view['assetic']->javascripts(
array(
'@jquery_and_ui',
'@AppBundle/Resources/public/js/*',
)
) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach ?> |
Filters(调节器) ¶
一旦(assets资源)被Assetic管理,你就可以在它们被供给(到客户端)之前对其使用调节器。包括为了获得更小容量而对资源的输出进行压缩的调节器(更好的前端优化)。其他调节器,可以编译CoffeeScript文件至JavaScript,以及处理SASS至CSS。实际上,Assetic有很多可用的调节器。
很多调节器并不直接参与工作,而是利用即存的三方类库来处理重载。这意味着你经常需要安装第三方类库才能使用一个调节器。引入这些类库(因为反对直接使用)而使用Assetic最大的好处是,避免在操作资源文件时被迫地手动让它们运行,Assetic将帮你打点这些,并从开发环境和部署进程中一并移除此步骤。
要使用一个调节器,你先要在Assetic的配置区块指定它。这里添加的调节器并不意味着它会被使用——只表明它是可以使用的(你将在后面例子中使用它)。
例如,要使用UglifyJS这个JS最小化工具,应对配置作如下定义:
1 2 3 4 5 |
# app/config/config.yml
assetic:
filters:
uglifyjs2:
bin: /usr/local/bin/uglifyjs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:assetic="http://symfony.com/schema/dic/assetic"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/assetic
http://symfony.com/schema/dic/assetic/assetic-1.0.xsd">
<assetic:config>
<assetic:filter
name="uglifyjs2"
bin="/usr/local/bin/uglifyjs" />
</assetic:config>
</container> |
1 2 3 4 5 6 7 8 |
// app/config/config.php
$container->loadFromExtension('assetic', array(
'filters' => array(
'uglifyjs2' => array(
'bin' => '/usr/local/bin/uglifyjs',
),
),
)); |
现在,为了对一组JavaScript文件真正 使用 这个调节器,把它添加到你的模板中:
1 2 3 |
{% javascripts '@AppBundle/Resources/public/js/*' filter='uglifyjs2' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %} |
1 2 3 4 5 6 |
<?php foreach ($view['assetic']->javascripts(
array('@AppBundle/Resources/public/js/*'),
array('uglifyjs2')
) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach ?> |
关于配置和使用Assetic过滤器以及Assetic调试模式的细节内容,可以参考 如何最小化CSS/JS文件(使用UglifyJS和UglifyCSS)。
控制所使用的URL ¶
如果你愿意,可以控制Assetic生成的URL。这可在模板中完成,并且(输出内容)是相对于公开的web根目录。
1 2 3 |
{% javascripts '@AppBundle/Resources/public/js/*' output='js/compiled/main.js' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %} |
1 2 3 4 5 6 7 |
<?php foreach ($view['assetic']->javascripts(
array('@AppBundle/Resources/public/js/*'),
array(),
array('output' => 'js/compiled/main.js')
) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach ?> |
Symfony也包含了一个用于cache busting(缓存击破)的方法,其被Assetic生成的最终URL中带有一个query参数,可以在每次部署时随着配置信息而递增。更多信息,参考 version 配置选项。
剥离Asset文件 ¶
在 dev
环境下,Assetic所生成的CSS和JavaScript文件路径并非是电脑上的“物理存在”。而是渲染出来,因为Symfony内部的控制器会打开这些文件并送回给内容(在运行任何调节器之后)。
这一类处理assets时的“动态伺服”是非常好的,因为这意味着你可以立即看到任何一个资源在发生改变后的新状态。同时其缺点是运行极慢。如果你使用了多个filters,有可能彻底崩溃掉。
幸运的是,Assetic提供了一种方式来剥离出你的资源到“真实文件”,而不是被即时动态生成。
在生产环境下剥离资源文件 ¶
在 prod
环境下,你的JS和CSS调节器是通过各自的独立标签来呈现的。换言之,不能看到你包容进来的每个JavaScript文件,你可能只看到类似下面的东西:
1 |
<script src="/js/abcd123.js"></script> |
再者,那个文件 并不 真实存在,也不被Symfony动态渲染(因为资源文件是在 dev
环境中)。这是设计本意——让Symfony在生产环境中动态生成这些文件实在太慢了。
取而代之的是,每一次当你在 prod
环境使用程序时(也就是,每一次在你部署时),你应该运行如下命令:
1 |
$ php bin/console assetic:dump --env=prod --no-debug |
这会物理生成并写入每一个你需要的文件(如 /js/abcd123.js
)。如果你更新了资源中的任何一个,需要重新运行此命令以再次生成文件。
在开发环境下剥离资源文件 ¶
默认时,dev
环境下被生成的asset路径,由Symfony负责动态处理。这并无欠点(你可以立即看到你做出的改变),除了资源加载时显著变慢。如果你觉得资源的加载太过缓慢,遵循以下指南。
首先,告之Symfony停止尝试动态处理这些文件。在 config_dev.yml
做出如下改变:
1 2 3 |
# app/config/config_dev.yml
assetic:
use_controller: false |
1 2 3 4 5 6 7 8 9 10 11 12 |
<!-- app/config/config_dev.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:assetic="http://symfony.com/schema/dic/assetic"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/assetic
http://symfony.com/schema/dic/assetic/assetic-1.0.xsd">
<assetic:config use-controller="false" />
</container> |
1 2 3 4 |
// app/config/config_dev.php
$container->loadFromExtension('assetic', array(
'use_controller' => false,
)); |
接下来,由于Symfony不再为你生成这些assets,你需要手动剥离之。要这么做,运行以下命令:
1 |
$ php bin/console assetic:dump |
这会为你的 dev
环境物理写入全部所需的资源文件。最大欠点是,你需要在每一次的资源更新时运行命令。幸运的是,使用 assetic:watch
命令,资源会在 它们改变时 自动重新生成:
1 |
$ php bin/console assetic:watch |
assetic:watch
命令自AsseticBundle 2.4起被引入。之前的版本,你要在 assetic:dump
命令中使用 --watch
选项来实现相同的目的。
由于在 dev
环境下运行此命令会生成一堆文件,通常,把这些资源文件映射到一个单独的目录是个好办法(如 /js/compiled
),以令事情井井有条:
1 2 3 |
{% javascripts '@AppBundle/Resources/public/js/*' output='js/compiled/main.js' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %} |
1 2 3 4 5 6 7 |
<?php foreach ($view['assetic']->javascripts(
array('@AppBundle/Resources/public/js/*'),
array(),
array('output' => 'js/compiled/main.js')
) as $url): ?>
<script src="<?php echo $view->escape($url) ?>"></script>
<?php endforeach ?> |