前回の記事 Yii2のAssetsでNPMやBowerパッケージを読み込むには でYii2のAssetシステムの使い方についてまとめましたが、今回は、Yii2のAssetを使わずに、JavascriptやCSSをGruntで管理する方法について調べた内容をまとめてみました。
前半は、Yii2 CookbookのAsset processing with Gruntの内容を試したメモで、その後簡単なサンプルを作成してみます。
解説の前提として、Yiiプロジェクトが作成されているところからになります。
Gruntのセットアップ
ますは、Node.jsの環境を準備します。
Ubuntuの場合は以下のコマンドでインストールできます。
$ curl -sL https://deb.nodesource.com/setup_5.x | sudo -E bash -
$ sudo apt-get install -y nodejs
他のプラットフォームのパッケージインストールについては Installing Node.js via package manager を参照してください。
OS Xの場合は、nodebrewがおすすめです。
Gruntをグローバルインストールします。
$ sudo npm install -g grunt-cli
プロジェクトディレクトリに移動して、npm init
してpackage.xml
を作成した後、必要なGruntプラグインをインストールしていきます
$ cd project_dir
$ npm init
$ npm install grunt --save-dev
$ npm install grunt-contrib-copy --save-dev
$ npm install grunt-contrib-less --save-dev
$ npm install grunt-contrib-uglify --save-dev
$ npm install grunt-contrib-watch --save-dev
$ npm install grunt-concat-sourcemap --save-dev
$ npm install grunt-typescript --save-dev
デフォルトのassetManagerを無効にするようにconfig/web.php
を設定します。
// config/web.php
$config = [
...
'components' => [
...
'assetManager' => [
'bundles' => false,
],
],
...
GruntでビルドしたCSSとJSを読み込むように、レイアウトviews/layouts/main.php
に追加していきます。
CSSは、<?= Html::csrfMetaTags() ?>
の後ろにHtml::cssFile
の呼び出しを追加します。
// views/layouts/main.php
...
<head>
<meta charset="<?= Yii::$app->charset ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?= Html::csrfMetaTags() ?>
<?= Html::cssFile(YII_DEBUG ? '@web/css/all.css' : '@web/css/all.min.css?v=' . filemtime(Yii::getAlias('@webroot/css/all.min.css'))) ?>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head() ?>
</head>
...
http://example.com/css/all.css
があれば開発用のall.css
を、なければプロダクションの圧縮したall.min.css
のリンクを追加するようにしています。
JSの読み込みは、endBody() ?>
の前に追加します。
// views/layoutes/main.php
...
<body>
<?php $this->beginBody() ?>
...
<?= Html::jsFile(YII_DEBUG ? '@web/js/lib.js' : '@web/js/lib.min.js?v=' . filemtime(Yii::getAlias('@webroot/js/lib.min.js'))) ?>
<?= Html::jsFile(YII_DEBUG ? '@web/js/all.js' : '@web/js/all.min.js?v=' . filemtime(Yii::getAlias('@webroot/js/all.min.js'))) ?>
<?php $this->endBody() ?>
</body>
...
JSもCSSと同じく開発用とプロダクション用のJSの読み込みを切り替えています。
Gruntfile.jsは以下のようになります。
// Gruntfile.js
module.exports = function (grunt) {
grunt.initConfig({
less: {
dev: {
options: {
compress: false,
sourceMap: true,
outputSourceFiles: true
},
files: {
"web/css/all.css": "assets/less/all.less"
}
},
prod: {
options: {
compress: true
},
files: {
"web/css/all.min.css": "assets/less/all.less"
}
}
},
typescript: {
base: {
src: ['assets/ts/*.ts'],
dest: 'web/js/all.js',
options: {
module: 'amd',
sourceMap: true,
target: 'es5'
}
}
},
concat_sourcemap: {
options: {
sourcesContent: true
},
all: {
files: {
'web/js/all.js': grunt.file.readJSON('assets/js/all.json')
}
}
},
copy: {
main: {
files: [
{expand: true, flatten: true, src: ['vendor/bower/bootstrap/fonts/*'], dest: 'web/fonts/', filter: 'isFile'}
]
}
},
uglify: {
options: {
mangle: false
},
lib: {
files: {
'web/js/lib.min.js': 'web/js/lib.js'
}
},
all: {
files: {
'web/js/all.min.js': 'web/js/all.js'
}
}
},
watch: {
typescript: {
files: ['assets/ts/*.ts'],
tasks: ['typescript', 'uglify:all'],
options: {
livereload: true
}
},
js: {
files: ['assets/js/**/*.js', 'assets/js/all.json'],
tasks: ['concat_sourcemap', 'uglify:lib'],
options: {
livereload: true
}
},
less: {
files: ['assets/less/**/*.less'],
tasks: ['less'],
options: {
livereload: true
}
},
fonts: {
files: [
'vendor/bower/bootstrap/fonts/*'
],
tasks: ['copy'],
options: {
livereload: true
}
}
}
});
// Plugin loading
grunt.loadNpmTasks('grunt-typescript');
grunt.loadNpmTasks('grunt-concat-sourcemap');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');
// Task definition
grunt.registerTask('build', ['less', 'typescript', 'copy', 'concat_sourcemap', 'uglify']);
grunt.registerTask('default', ['watch']);
};
lib.jsに含めるYii標準のassetを指定するassets/js/all.json
を作成します。
// assets/js/all.json
[
"vendor/bower/jquery/dist/jquery.js",
"vendor/bower/bootstrap/dist/js/bootstrap.js",
"vendor/yiisoft/yii2/assets/yii.js",
"vendor/yiisoft/yii2/assets/yii.validation.js",
"vendor/yiisoft/yii2/assets/yii.activeForm.js"
]
次にassets/less/all.less
を作成します。
/* assets/less/all.less */
@import "../../vendor/bower/bootstrap/less/bootstrap.less";
@import "site.less";
site.less
は、web/css/site.css
の内容をコピーします。
$ cat web/css/site.css > assets/less/site.less
ここまでで、Gruntでアセットをビルドする環境が整いました。
grunt buildでビルドします。
$ grunt build
トップページにアクセスすると以下のように、デフォルトのAssetシステムを使っている場合と同じ表示になります。
AngularJSを利用したサンプル
GruntでAssetをビルドする環境が整いましたので、サンプルとしてAngularJSのアプリを作成してみます。
サンプルは、Yeoman AngularJS Generatorで作成したプロジェクト利用してみます。
実装の流れは、typescriptを指定してGeneratorでAngularJSのプロジェクトを作成して、必要なコードをYii2プロジェクトにコピーしていきます。
まずは、以下のコマンドでAngularJSプロジェクトを作成します。
$ sudo npm install -g yo generator-angular
$ yo angular testApp --typescript
Angularのモジュールは、angular-routeのみ追加してみました。
プロジェクトの動作を確認する場合は、以下のコマンドを実行します。
$ npm install && bower install
$ grunt serve
ブラウザで動作を確認するとトップページが以下のように表示されます。
生成された、AngularJSプロジェクトのファイルをYii2プロジェクトに組み込んで行きます。
以下のディレクトリ・ファイルをコピーしてください。
app/scripts -> assets/ts
app/images -> web
app/views -> web
typings -> プロジェクトルート
コピーするファイルは以上になります。
次に、angularとangular-routeをComposertでインストールします。
$ composer require bower-asset/angular
$ composer require bower-asset/angular-route
all.jsonにangular.jsを追加します。
// assets/js/all.json
[
"vendor/bower/jquery/dist/jquery.js",
"vendor/bower/bootstrap/dist/js/bootstrap.js",
"vendor/bower/angular/angular.js",
"vendor/bower/angular-route/angular-route.js",
"vendor/yiisoft/yii2/assets/yii.js",
"vendor/bower/bootstrap/dist/js/bootstrap.js",
"vendor/yiisoft/yii2/assets/yii.validation.js",
"vendor/yiisoft/yii2/assets/yii.activeForm.js"
]
トップページのindexをAngularJS用に書き換えます。
// views/site/index.php
<?php
/* @var $this yii\web\View */
$this->title = 'My Yii Application';
?>
<div ng-app="testAppApp">
<div ng-view=""></div>
</div>
再度、grunt build
して、ブラウザでアクセスすると以下のように、Yiiのレイアウトの中に、AnguarJSでレンダリングしたコンテンツが表示されます。