structure-assets.md 31.5 KB
Newer Older
Qiang Xue committed
1 2
Assets
======
3

Qiang Xue committed
4
An asset in Yii is a file that may be referenced in a Web page. It can be a CSS file, a JavaScript file, an image
5
or video file, etc. Assets are located in Web-accessible directories and are directly served by Web servers.
Qiang Xue committed
6

Qiang Xue committed
7 8 9 10 11
It is often preferable to manage assets programmatically. For example, when you use the [[yii\jui\DatePicker]] widget
in a page, it will automatically include the required CSS and JavaScript files, instead of asking you to manually
find these files and include them. And when you upgrade the widget to a new version, it will automatically use
the new version of the asset files. In this tutorial, we will describe the powerful asset management capability
provided in Yii.
12 13


Qiang Xue committed
14 15
## Asset Bundles <a name="asset-bundles"></a>

Qiang Xue committed
16 17 18
Yii manages assets in the unit of *asset bundle*. An asset bundle is simply a collection of assets located
in a directory. When you register an asset bundle in a [view](structure-views.md), it will include the CSS and
JavaScript files in the bundle in the rendered Web page.
Qiang Xue committed
19

Qiang Xue committed
20

Qiang Xue committed
21
## Defining Asset Bundles <a name="defining-asset-bundles"></a>
Qiang Xue committed
22

Qiang Xue committed
23
Asset bundles are specified as PHP classes extending from [[yii\web\AssetBundle]]. The name of a bundle is simply
24
its corresponding fully qualified PHP class name (without the leading backslash). An asset bundle class should
25
be [autoloadable](concept-autoloading.md). It usually specifies where the assets are located, what CSS and 
26
JavaScript files the bundle contains, and how the bundle depends on other bundles.
Qiang Xue committed
27 28

The following code defines the main asset bundle used by [the basic application template](start-installation.md):
Qiang Xue committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

```php
<?php

namespace app\assets;

use yii\web\AssetBundle;

class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.css',
    ];
    public $js = [
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap\BootstrapAsset',
    ];
}
```

Qiang Xue committed
53
The above `AppAsset` class specifies that the asset files are located under the `@webroot` directory which
54
corresponds to the URL `@web`; the bundle contains a single CSS file `css/site.css` and no JavaScript file;
Qiang Xue committed
55 56
the bundle depends on two other bundles: [[yii\web\YiiAsset]] and [[yii\bootstrap\BootstrapAsset]]. More detailed
explanation about the properties of [[yii\web\AssetBundle]] can be found in the following:
Qiang Xue committed
57 58 59

* [[yii\web\AssetBundle::sourcePath|sourcePath]]: specifies the root directory that contains the asset files in
  this bundle. This property should be set if the root directory is not Web accessible. Otherwise, you should
Qiang Xue committed
60 61
  set the [[yii\web\AssetBundle::basePath|basePath]] property and [[yii\web\AssetBundle::baseUrl|baseUrl]], instead.
  [Path aliases](concept-aliases.md) can be used here.
Qiang Xue committed
62 63 64 65 66
* [[yii\web\AssetBundle::basePath|basePath]]: specifies a Web-accessible directory that contains the asset files in
  this bundle. When you specify the [[yii\web\AssetBundle::sourcePath|sourcePath]] property,
  the [asset manager](#asset-manager) will publish the assets in this bundle to a Web-accessible directory
  and overwrite this property accordingly. You should set this property if your asset files are already in
  a Web-accessible directory and do not need asset publishing. [Path aliases](concept-aliases.md) can be used here.
Qiang Xue committed
67 68
* [[yii\web\AssetBundle::baseUrl|baseUrl]]: specifies the URL corresponding to the directory
  [[yii\web\AssetBundle::basePath|basePath]]. Like [[yii\web\AssetBundle::basePath|basePath]],
Qiang Xue committed
69 70 71 72 73 74
  if you specify the [[yii\web\AssetBundle::sourcePath|sourcePath]] property, the [asset manager](#asset-manager)
  will publish the assets and overwrite this property accordingly. [Path aliases](concept-aliases.md) can be used here.
* [[yii\web\AssetBundle::js|js]]: an array listing the JavaScript files contained in this bundle. Note that only
  forward slash "/" should be used as directory separators. Each JavaScript file can be specified in one of the
  following two formats:
  - a relative path representing a local JavaScript file (e.g. `js/main.js`). The actual path of the file
Qiang Xue committed
75 76
    can be determined by prepending [[yii\web\AssetManager::basePath]] to the relative path, and the actual URL
    of the file can be determined by prepending [[yii\web\AssetManager::baseUrl]] to the relative path.
Qiang Xue committed
77 78 79 80 81
  - an absolute URL representing an external JavaScript file. For example,
    `http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` or
    `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`.
* [[yii\web\AssetBundle::css|css]]: an array listing the CSS files contained in this bundle. The format of this array
  is the same as that of [[yii\web\AssetBundle::js|js]].
Qiang Xue committed
82 83 84 85 86 87 88
* [[yii\web\AssetBundle::depends|depends]]: an array listing the names of the asset bundles that this bundle depends on
  (to be explained shortly).
* [[yii\web\AssetBundle::jsOptions|jsOptions]]: specifies the options that will be passed to the
  [[yii\web\View::registerJsFile()]] method when it is called to register *every* JavaScript file in this bundle.
* [[yii\web\AssetBundle::cssOptions|cssOptions]]: specifies the options that will be passed to the
  [[yii\web\View::registerCssFile()]] method when it is called to register *every* CSS file in this bundle.
* [[yii\web\AssetBundle::publishOptions|publishOptions]]: specifies the options that will be passed to the
89
  [[yii\web\AssetManager::publish()]] method when it is called to publish source asset files to a Web directory.
Qiang Xue committed
90
  This is only used if you specify the [[yii\web\AssetBundle::sourcePath|sourcePath]] property.
Qiang Xue committed
91 92


Qiang Xue committed
93
### Asset Locations <a name="asset-locations"></a>
Qiang Xue committed
94

Qiang Xue committed
95
Assets, based on their location, can be classified as:
Qiang Xue committed
96

Qiang Xue committed
97
* source assets: the asset files are located together with PHP source code which cannot be directly accessed via Web.
98
  In order to use source assets in a page, they should be copied to a Web directory and turned into the so-called
Qiang Xue committed
99
  published assets. This process is called *asset publishing* which will be described in detail shortly.
100
* published assets: the asset files are located in a Web directory and can thus be directly accessed via Web.
Qiang Xue committed
101 102
* external assets: the asset files are located on a Web server that is different from the one hosting your Web
  application.
Qiang Xue committed
103

Qiang Xue committed
104 105 106
When defining an asset bundle class, if you specify the [[yii\web\AssetBundle::sourcePath|sourcePath]] property,
it means any assets listed using relative paths will be considered as source assets. If you do not specify this property,
it means those assets are published assets (you should therefore specify [[yii\web\AssetBundle::basePath|basePath]] and
107
[[yii\web\AssetBundle::baseUrl|baseUrl]] to let Yii know where they are located).
Qiang Xue committed
108

109
It is recommended that you place assets belonging to an application in a Web directory to avoid the unnecessary asset
Qiang Xue committed
110 111
publishing process. This is why `AppAsset` in the prior example specifies [[yii\web\AssetBundle::basePath|basePath]]
instead of [[yii\web\AssetBundle::sourcePath|sourcePath]].
112

Qiang Xue committed
113
For [extensions](structure-extensions.md), because their assets are located together with their source code
114
in directories that are not Web accessible, you have to specify the [[yii\web\AssetBundle::sourcePath|sourcePath]]
Qiang Xue committed
115
property when defining asset bundle classes for them.
116

Qiang Xue committed
117
> Note: Do not use `@webroot/assets` as the [[yii\web\AssetBundle::sourcePath|source path]].
118
  This directory is used by default by the [[yii\web\AssetManager|asset manager]] to save the asset files
119
  published from their source location. Any content in this directory is considered temporarily and may be subject
Qiang Xue committed
120
  to removal.
Carsten Brandt committed
121

122

Qiang Xue committed
123 124
### Asset Dependencies <a name="asset-dependencies"></a>

125
When you include multiple CSS or JavaScript files in a Web page, they have to follow a certain order to avoid
Qiang Xue committed
126 127 128 129 130 131 132 133 134 135 136
overriding issues. For example, if you are using a jQuery UI widget in a Web page, you have to make sure
the jQuery JavaScript file is included before the jQuery UI JavaScript file. We call such ordering the dependencies
among assets.

Asset dependencies are mainly specified through the [[yii\web\AssetBundle::depends]] property.
In the `AppAsset` example, the asset bundle depends on two other asset bundles: [[yii\web\YiiAsset]] and
[[yii\bootstrap\BootstrapAsset]], which means the CSS and JavaScript files in `AppAsset` will be included *after*
those files in the two dependent bundles.

Asset dependencies are transitive. This means if bundle A depends on B which depends on C, A will depend on C, too.

137

Qiang Xue committed
138
### Asset Options <a name="asset-options"></a>
139

Qiang Xue committed
140 141 142 143 144 145 146 147 148 149
You can specify the [[yii\web\AssetBundle::cssOptions|cssOptions]] and [[yii\web\AssetBundle::jsOptions|jsOptions]]
properties to customize the way that CSS and JavaScript files are included in a page. The values of these properties
will be passed to the [[yii\web\View::registerCssFile()]] and [[yii\web\View::registerJsFile()]] methods, respectively, when
they are called by the [view](structure-views.md) to include CSS and JavaScript files.

> Note: The options you set in a bundle class apply to *every* CSS/JavaScript file in the bundle. If you want to
  use different options for different files, you should create separate asset bundles, and use one set of options
  in each bundle.

For example, to conditionally include a CSS file for browsers that are IE9 or above, you can use the following option:
Qiang Xue committed
150 151

```php
Qiang Xue committed
152 153 154 155 156 157 158 159 160 161 162
public $cssOptions = ['condition' => 'lte IE9'];
```

This will cause a CSS file in the bundle to be included using the following HTML tags:

```html
<!--[if lte IE9]>
<link rel="stylesheet" href="path/to/foo.css">
<![endif]-->
```

Qiang Xue committed
163
To wrap the generated CSS link tags within `<noscript>`, you can configure `cssOptions` as follows,
164 165 166 167 168

```php
public $cssOptions = ['noscript' => true];
```

Qiang Xue committed
169 170 171 172 173
To include a JavaScript file in the head section of a page (by default, JavaScript files are included at the end
of the body section), use the following option:

```php
public $jsOptions = ['position' => \yii\web\View::POS_HEAD];
174 175
```

Qiang Xue committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
By default, when an asset bundle is being published, all contents in the directory specified by [[yii\web\AssetBundle::sourcePath]]
will be published. You can customize this behavior by configuring the [[yii\web\AssetBundle::publishOptions|publishOptions]] 
property. For example, to publish only one or a few subdirectories of [[yii\web\AssetBundle::sourcePath]], 
you can do the following in the asset bundle class:

```php
<?php
namespace app\assets;

use yii\web\AssetBundle;

class FontAwesomeAsset extends AssetBundle 
{
    public $sourcePath = '@bower/font-awesome'; 
    public $css = [ 
        'css/font-awesome.min.css', 
    ]; 
    
    public function init()
    {
        parent::init();
        $this->publishOptions['beforeCopy'] = function ($from, $to) {
            $dirname = basename(dirname($from));
            return $dirname === 'fonts' || $dirname === 'css';
        };
    }
}  
```

The above example defines an asset bundle for the ["fontawesome" package](http://fontawesome.io/). By specifying 
the `beforeCopy` publishing option, only the `fonts` and `css` subdirectories will be published.

208

Qiang Xue committed
209 210
### Bower and NPM Assets <a name="bower-npm-assets"></a>

211
Most JavaScript/CSS packages are managed by [Bower](http://bower.io/) and/or [NPM](https://www.npmjs.org/).
Qiang Xue committed
212 213 214 215 216 217 218 219
If your application or extension is using such a package, it is recommended that you follow these steps to manage
the assets in the library:

1. Modify the `composer.json` file of your application or extension and list the package in the `require` entry.
   You should use `bower-asset/PackageName` (for Bower packages) or `npm-asset/PackageName` (for NPM packages)
   to refer to the library.
2. Create an asset bundle class and list the JavaScript/CSS files that you plan to use in your application or extension.
   You should specify the [[yii\web\AssetBundle::sourcePath|sourcePath]] property as `@bower/PackageName` or `@npm/PackageName`.
220
   This is because Composer will install the Bower or NPM package in the directory corresponding to this alias.
Qiang Xue committed
221 222 223 224 225 226

> Note: Some packages may put all their distributed files in a subdirectory. If this is the case, you should specify
  the subdirectory as the value of [[yii\web\AssetBundle::sourcePath|sourcePath]]. For example, [[yii\web\JqueryAsset]]
  uses `@bower/jquery/dist` instead of `@bower/jquery`.


Qiang Xue committed
227
## Using Asset Bundles <a name="using-asset-bundles"></a>
228

Qiang Xue committed
229 230
To use an asset bundle, register it with a [view](structure-views.md) by calling the [[yii\web\AssetBundle::register()]]
method. For example, in a view template you can register an asset bundle like the following:
231

Qiang Xue committed
232 233
```php
use app\assets\AppAsset;
Qiang Xue committed
234
AppAsset::register($this);  // $this represents the view object
Qiang Xue committed
235
```
236

Qiang Xue committed
237 238 239
> Info: The [[yii\web\AssetBundle::register()]] method returns an asset bundle object containing the information
  about the published assets, such as [[yii\web\AssetBundle::basePath|basePath]] or [[yii\web\AssetBundle::baseUrl|baseUrl]].

Qiang Xue committed
240 241 242
If you are registering an asset bundle in other places, you should provide the needed view object. For example,
to register an asset bundle in a [widget](structure-widgets.md) class, you can get the view object by `$this->view`.

243
When an asset bundle is registered with a view, behind the scenes Yii will register all its dependent asset bundles.
244
And if an asset bundle is located in a directory inaccessible through the Web, it will be published to a Web directory.
245
Later, when the view renders a page, it will generate `<link>` and `<script>` tags for the CSS and JavaScript files
Qiang Xue committed
246 247 248
listed in the registered bundles. The order of these tags is determined by the dependencies among
the registered bundles and the order of the assets listed in the [[yii\web\AssetBundle::css]] and [[yii\web\AssetBundle::js]]
properties.
249

250

Qiang Xue committed
251
### Customizing Asset Bundles <a name="customizing-asset-bundles"></a>
252

Qiang Xue committed
253 254 255 256 257
Yii manages asset bundles through an application component named `assetManager` which is implemented by [[yii\web\AssetManager]].
By configuring the [[yii\web\AssetManager::bundles]] property, it is possible to customize the behavior of an asset bundle.
For example, the default [[yii\web\JqueryAsset]] asset bundle uses the `jquery.js` file from the installed
jquery Bower package. To improve the availability and performance, you may want to use a version hosted by Google.
This can be achieved by configuring `assetManager` in the application configuration like the following:
258

Qiang Xue committed
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
```php
return [
    // ...
    'components' => [
        'assetManager' => [
            'bundles' => [
                'yii\web\JqueryAsset' => [
                    'sourcePath' => null,   // do not publish the bundle
                    'js' => [
                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',
                    ]
                ],
            ],
        ],
    ],
];
```

You can configure multiple asset bundles similarly through [[yii\web\AssetManager::bundles]]. The array keys
should be the class names (without the leading backslash) of the asset bundles, and the array values should
be the corresponding [configuration arrays](concept-configurations.md).

> Tip: You can conditionally choose which assets to use in an asset bundle. The following example shows how
> to use `jquery.js` in the development environment and `jquery.min.js` otherwise:
>
284 285 286 287 288 289 290
> ```php
> 'yii\web\JqueryAsset' => [
>     'js' => [
>         YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'
>     ]
> ],
> ```
Qiang Xue committed
291 292 293

You can disable one or multiple asset bundles by associating `false` with the names of the asset bundles
that you want to disable. When you register a disabled asset bundle with a view, none of its dependent bundles
294
will be registered, and the view also will not include any of the assets in the bundle in the page it renders.
Qiang Xue committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
For example, to disable [[yii\web\JqueryAsset]], you can use the following configuration:

```php
return [
    // ...
    'components' => [
        'assetManager' => [
            'bundles' => [
                'yii\web\JqueryAsset' => false,
            ],
        ],
    ],
];
```

You can also disable *all* asset bundles by setting [[yii\web\AssetManager::bundles]] as `false`.


### Asset Mapping <a name="asset-mapping"></a>

315 316
Sometimes you may want to "fix" incorrect/incompatible asset file paths used in multiple asset bundles. For example,
bundle A uses `jquery.min.js` version 1.11.1, and bundle B uses `jquery.js` version 2.1.1. While you can
Qiang Xue committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
fix the problem by customizing each bundle, an easier way is to use the *asset map* feature to map incorrect assets
to the desired ones. To do so, configure the [[yii\web\AssetManager::assetMap]] property like the following:

```php
return [
    // ...
    'components' => [
        'assetManager' => [
            'assetMap' => [
                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',
            ],
        ],
    ],
];
```

The keys of [[yii\web\AssetManager::assetMap|assetMap]] are the asset names that you want to fix, and the values
are the desired asset paths. When you register an asset bundle with a view, each relative asset file in its
[[yii\web\AssetBundle::css|css]] and [[yii\web\AssetBundle::js|js]] arrays will be examined against this map.
336
If any of the keys are found to be the last part of an asset file (which is prefixed with [[yii\web\AssetBundle::sourcePath]]
Qiang Xue committed
337
if available), the corresponding value will replace the asset and be registered with the view.
338
For example, the asset file `my/path/to/jquery.js` matches the key `jquery.js`.
Qiang Xue committed
339

340
> Note: Only assets specified using relative paths are subject to asset mapping. The target asset paths
Qiang Xue committed
341 342 343 344 345
  should be either absolute URLs or paths relative to [[yii\web\AssetManager::basePath]].


### Asset Publishing <a name="asset-publishing"></a>

346 347
As aforementioned, if an asset bundle is located in a directory that is not Web accessible, its assets will be copied
to a Web directory when the bundle is being registered with a view. This process is called *asset publishing*, and is done
Qiang Xue committed
348
automatically by the [[yii\web\AssetManager|asset manager]].
349

350
By default, assets are published to the directory `@webroot/assets` which corresponds to the URL `@web/assets`.
Qiang Xue committed
351
You may customize this location by configuring the [[yii\web\AssetManager::basePath|basePath]] and
352
[[yii\web\AssetManager::baseUrl|baseUrl]] properties.
Carsten Brandt committed
353

Qiang Xue committed
354 355
Instead of publishing assets by file copying, you may consider using symbolic links, if your OS and Web server allow.
This feature can be enabled by setting [[yii\web\AssetManager::linkAssets|linkAssets]] to be true.
Carsten Brandt committed
356

Qiang Xue committed
357 358 359 360 361 362 363 364 365 366 367
```php
return [
    // ...
    'components' => [
        'assetManager' => [
            'linkAssets' => true,
        ],
    ],
];
```

Qiang Xue committed
368 369 370 371 372 373
With the above configuration, the asset manager will create a symbolic link to the source path of an asset bundle
when it is being published. This is faster than file copying and can also ensure that the published assets are
always up-to-date.


## Commonly Used Asset Bundles <a name="common-asset-bundles"></a>
Qiang Xue committed
374

Qiang Xue committed
375 376
The core Yii code has defined many asset bundles. Among them, the following bundles are commonly used and may
be referenced in your application or extension code.
Qiang Xue committed
377

Qiang Xue committed
378 379
- [[yii\web\YiiAsset]]: It mainly includes the `yii.js` file which implements a mechanism of organizing JavaScript code
  in modules. It also provides special support for `data-method` and `data-confirm` attributes and other useful features.
380
- [[yii\web\JqueryAsset]]: It includes the `jquery.js` file from the jQuery Bower package.
Qiang Xue committed
381 382 383 384
- [[yii\bootstrap\BootstrapAsset]]: It includes the CSS file from the Twitter Bootstrap framework.
- [[yii\bootstrap\BootstrapPluginAsset]]: It includes the JavaScript file from the Twitter Bootstrap framework for
  supporting Bootstrap JavaScript plugins.
- [[yii\jui\JuiAsset]]: It includes the CSS and JavaScript files from the jQuery UI library.
Alexander Makarov committed
385

Qiang Xue committed
386 387 388 389 390 391 392 393 394 395 396
If your code depends on jQuery, jQuery UI or Bootstrap, you should use these predefined asset bundles rather than
creating your own versions. If the default setting of these bundles do not satisfy your needs, you may customize them 
as described in the [Customizing Asset Bundle](#customizing-asset-bundles) subsection. 


## Asset Conversion <a name="asset-conversion"></a>

Instead of directly writing CSS and/or JavaScript code, developers often write them in some extended syntax and
use special tools to convert it into CSS/JavaScript. For example, for CSS code you may use [LESS](http://lesscss.org/)
or [SCSS](http://sass-lang.com/); and for JavaScript you may use [TypeScript](http://www.typescriptlang.org/).

397
You can list the asset files in extended syntax in the [[yii\web\AssetBundle::css|css]] and [[yii\web\AssetBundle::js|js]] properties of an asset bundle. For example,
Alexander Makarov committed
398 399

```php
Qiang Xue committed
400
class AppAsset extends AssetBundle
Alexander Makarov committed
401
{
Qiang Xue committed
402 403 404 405 406
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.less',
    ];
407
    public $js = [
Qiang Xue committed
408 409 410 411 412
        'js/site.ts',
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap\BootstrapAsset',
413
    ];
Alexander Makarov committed
414 415 416
}
```

Qiang Xue committed
417 418 419 420 421 422 423 424 425 426 427 428 429 430
When you register such an asset bundle with a view, the [[yii\web\AssetManager|asset manager]] will automatically
run the pre-processor tools to convert assets in recognized extended syntax into CSS/JavaScript. When the view
finally renders a page, it will include the CSS/JavaScript files in the page, instead of the original assets
in extended syntax.

Yii uses the file name extensions to identify which extended syntax an asset is in. By default it recognizes
the following syntax and file name extensions:

- [LESS](http://lesscss.org/): `.less`
- [SCSS](http://sass-lang.com/): `.scss`
- [Stylus](http://learnboost.github.io/stylus/): `.styl`
- [CoffeeScript](http://coffeescript.org/): `.coffee`
- [TypeScript](http://www.typescriptlang.org/): `.ts`

431
Yii relies on the installed pre-processor tools to convert assets. For example, to use [LESS](http://lesscss.org/)
Qiang Xue committed
432 433 434 435
you should install the `lessc` pre-processor command.

You can customize the pre-processor commands and the supported extended syntax by configuring
[[yii\web\AssetManager::converter]] like the following:
436 437

```php
Qiang Xue committed
438 439 440 441 442 443 444 445 446 447 448 449 450
return [
    'components' => [
        'assetManager' => [
            'converter' => [
                'class' => 'yii\web\AssetConverter',
                'commands' => [
                    'less' => ['css', 'lessc {from} {to} --no-color'],
                    'ts' => ['js', 'tsc --out {to} {from}'],
                ],
            ],
        ],
    ],
];
451 452
```

453
In the above, we specify the supported extended syntax via the [[yii\web\AssetConverter::commands]] property.
Qiang Xue committed
454 455 456
The array keys are the file extension names (without leading dot), and the array values are the resulting
asset file extension names and the commands for performing the asset conversion. The tokens `{from}` and `{to}`
in the commands will be replaced with the source asset file paths and the target asset file paths.
Carsten Brandt committed
457

Qiang Xue committed
458 459 460 461
> Info: There are other ways of working with assets in extended syntax, besides the one described above.
  For example, you can use build tools such as [grunt](http://gruntjs.com/) to monitor and automatically
  convert assets in extended syntax. In this case, you should list the resulting CSS/JavaScript files in
  asset bundles rather than the original files.
462 463


Qiang Xue committed
464
## Combining and Compressing Assets <a name="combining-compressing-assets"></a>
465

Qiang Xue committed
466 467 468 469 470
A Web page can include many CSS and/or JavaScript files. To reduce the number of HTTP requests and the overall
download size of these files, a common practice is to combine and compress multiple CSS/JavaScript files into 
one or very few files, and then include these compressed files instead of the original ones in the Web pages.  
 
> Info: Combining and compressing assets is usually needed when an application is in production mode. 
471
  In development mode, using the original CSS/JavaScript files is often more convenient for debugging purposes.
472

473
In the following, we introduce an approach to combine and compress asset files without the need to modify
Qiang Xue committed
474
your existing application code.
475

476
1. Find all the asset bundles in your application that you plan to combine and compress.
Qiang Xue committed
477 478 479 480 481 482 483 484
2. Divide these bundles into one or a few groups. Note that each bundle can only belong to a single group.
3. Combine/compress the CSS files in each group into a single file. Do this similarly for the JavaScript files.
4. Define a new asset bundle for each group:
   * Set the [[yii\web\AssetBundle::css|css]] and [[yii\web\AssetBundle::js|js]] properties to be
     the combined CSS and JavaScript files, respectively.
   * Customize the asset bundles in each group by setting their [[yii\web\AssetBundle::css|css]] and 
     [[yii\web\AssetBundle::js|js]] properties to be empty, and setting their [[yii\web\AssetBundle::depends|depends]]
     property to be the new asset bundle created for the group.
485

Qiang Xue committed
486 487 488
Using this approach, when you register an asset bundle in a view, it causes the automatic registration of
the new asset bundle for the group that the original bundle belongs to. And as a result, the combined/compressed 
asset files are included in the page, instead of the original ones.
489 490


Qiang Xue committed
491
### An Example <a name="example"></a>
492

Qiang Xue committed
493
Let's use an example to further explain the above approach. 
494

495
Assume your application has two pages, X and Y. Page X uses asset bundles A, B and C, while Page Y uses asset bundles B, C and D. 
496

Qiang Xue committed
497
You have two ways to divide these asset bundles. One is to use a single group to include all asset bundles, the
Qiang Xue committed
498
other is to put A in Group X, D in Group Y, and (B, C) in Group S. Which one is better? It depends. The first way
Qiang Xue committed
499 500
has the advantage that both pages share the same combined CSS and JavaScript files, which makes HTTP caching
more effective. On the other hand, because the single group contains all bundles, the size of the combined CSS and 
Qiang Xue committed
501 502
JavaScript files will be bigger and thus increase the initial file transmission time. For simplicity in this example, 
we will use the first way, i.e., use a single group to contain all bundles.
503

Qiang Xue committed
504 505
> Info: Dividing asset bundles into groups is not trivial task. It usually requires analysis about the real world
  traffic data of various assets on different pages. At the beginning, you may start with a single group for simplicity. 
506

Qiang Xue committed
507 508 509 510 511
Use existing tools (e.g. [Closure Compiler](https://developers.google.com/closure/compiler/), 
[YUI Compressor](https://github.com/yui/yuicompressor/)) to combine and compress CSS and JavaScript files in 
all the bundles. Note that the files should be combined in the order that satisfies the dependencies among the bundles. 
For example, if Bundle A depends on B which depends on both C and D, then you should list the asset files starting 
from C and D, followed by B and finally A. 
512

Qiang Xue committed
513 514
After combining and compressing, we get one CSS file and one JavaScript file. Assume they are named as 
`all-xyz.css` and `all-xyz.js`, where `xyz` stands for a timestamp or a hash that is used to make the file name unique
515
to avoid HTTP caching problems.
Qiang Xue committed
516 517 518
 
We are at the last step now. Configure the [[yii\web\AssetManager|asset manager]] as follows in the application
configuration:
519 520 521 522 523 524

```php
return [
    'components' => [
        'assetManager' => [
            'bundles' => [
Qiang Xue committed
525 526 527 528 529 530
                'all' => [
                    'class' => 'yii\web\AssetBundle',
                    'basePath' => '@webroot/assets',
                    'baseUrl' => '@web/assets',
                    'css' => ['all-xyz.css'],
                    'js' => ['all-xyz.js'],
531
                ],
Qiang Xue committed
532 533 534 535
                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],
                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],
                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],
                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],
536 537 538 539 540 541
            ],
        ],
    ],
];
```

Qiang Xue committed
542 543 544 545 546
As explained in the [Customizing Asset Bundles](#customizing-asset-bundles) subsection, the above configuration
changes the default behavior of each bundle. In particular, Bundle A, B, C and D no longer have any asset files.
They now all depend on the `all` bundle which contains the combined `all-xyz.css` and `all-xyz.js` files.
Consequently, for Page X, instead of including the original source files from Bundle A, B and C, only these
two combined files will be included; the same thing happens to Page Y.
547

Qiang Xue committed
548 549 550
There is one final trick to make the above approach work more smoothly. Instead of directly modifying the
application configuration file, you may put the bundle customization array in a separate file and conditionally
include this file in the application configuration. For example,
551

Qiang Xue committed
552 553 554 555 556 557 558 559
```php
return [
    'components' => [
        'assetManager' => [
            'bundles' => require(__DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php')),  
        ],
    ],
];
560
```
Carsten Brandt committed
561

Qiang Xue committed
562 563
That is, the asset bundle configuration array is saved in `assets-prod.php` for production mode, and
`assets-dev.php` for non-production mode.
564

Carsten Brandt committed
565

Qiang Xue committed
566
### Using the `asset` Command <a name="using-asset-command"></a>
567

Qiang Xue committed
568
Yii provides a console command named `asset` to automate the approach that we just described.
569

Qiang Xue committed
570 571 572
To use this command, you should first create a configuration file to describe what asset bundles should
be combined and how they should be grouped. You can use the `asset/template` sub-command to generate
a template first and then modify it to fit for your needs.
573 574

```
Qiang Xue committed
575
yii asset/template assets.php
576 577
```

Qiang Xue committed
578
The command generates a file named `assets.php` in the current directory. The content of this file looks like the following:
579 580 581 582 583 584 585 586 587

```php
<?php
/**
 * Configuration file for the "yii asset" console command.
 * Note that in the console environment, some path aliases like '@webroot' and '@web' may not exist.
 * Please define these missing path aliases.
 */
return [
588 589 590 591
    // Adjust command/callback for JavaScript files compressing:
    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',
    // Adjust command/callback for CSS files compressing:
    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',
592 593 594 595 596 597 598
    // The list of asset bundles to compress:
    'bundles' => [
        // 'yii\web\YiiAsset',
        // 'yii\web\JqueryAsset',
    ],
    // Asset bundle for compression output:
    'targets' => [
599 600 601 602
        'all' => [
            'class' => 'yii\web\AssetBundle',
            'basePath' => '@webroot/assets',
            'baseUrl' => '@web/assets',
603 604
            'js' => 'js/all-{hash}.js',
            'css' => 'css/all-{hash}.css',
605 606 607 608 609
        ],
    ],
    // Asset manager configuration:
    'assetManager' => [
    ],
610 611 612
];
```

Qiang Xue committed
613 614 615
You should modify this file and specify which bundles you plan to combine in the `bundles` option. In the `targets` 
option you should specify how the bundles should be divided into groups. You can specify one or multiple groups, 
as aforementioned.
616

Qiang Xue committed
617 618
> Note: Because the alias `@webroot` and `@web` are not available in the console application, you should
  explicitly define them in the configuration.
619

620 621
JavaScript files are combined, compressed and written to `js/all-{hash}.js` where {hash} is replaced with the hash of
the resulting file.
622

Qiang Xue committed
623
The `jsCompressor` and `cssCompressor` options specify the console commands or PHP callbacks for performing
624
JavaScript and CSS combining/compressing. By default, Yii uses [Closure Compiler](https://developers.google.com/closure/compiler/) 
Qiang Xue committed
625
for combining JavaScript files and [YUI Compressor](https://github.com/yui/yuicompressor/) for combining CSS files. 
626
You should install those tools manually or adjust these options to use your favorite tools.
627 628


Qiang Xue committed
629 630 631
With the configuration file, you can run the `asset` command to combine and compress the asset files
and then generate a new asset bundle configuration file `assets-prod.php`:
 
632
```
Qiang Xue committed
633
yii asset assets.php config/assets-prod.php
634 635
```

Qiang Xue committed
636 637
The generated configuration file can be included in the application configuration, like described in
the last subsection.
638

Qiang Xue committed
639 640 641

> Info: Using the `asset` command is not the only option to automate the asset combining and compressing process.
  You can use the excellent task runner tool [grunt](http://gruntjs.com/) to achieve the same goal.