SassとGruntとVagrantで考えるWordPressテーマ設計術

Lovelog+*のテーマをリニューアルしました。

がベースになっています。

Sponsored Link


このリニューアルにあたって、
GruntとVagrantを導入し、開発環境を一新しました。

WordPress環境開発は、Varying Vagrant Vagrantsを使用しています。

子育てをしながらだと、
1日で作業する時間は限られてくるので、中々進みませんが…。

閃きだけは降り注ぐので、
それを後は空いた時間に実装するだけ(^^ゞ

今回もまたWordPressのテーマ設計を、
SassとGruntとVagrantを使って、
どこまで効率的に、表示速度も含めて開発できるかを念頭に設計しています。

まぁ、もうちょっとやりようはあると思うのですが、
圧倒的に育児で学習時間が足りてないので、
この内容で勘弁して下さいw

テーマ=開発環境という煩わしさ

今回始めてみて、
サーバーには要らないフォルダが沢山出来るという事が1番煩わしかったです。

なので、まずここに着目しました。

フォルダ構成

恐らくテーマフォルダ直下が開発環境になっていると思います。

 themes
  テーマ名
   bower.json…ファイル
   Gruntfile.js…ファイル
   node_modules…フォルダ
   package.json…ファイル
   config.rb…ファイル
   scss…フォルダ
   必要なテーマファイル一式

私は1つのテーマで、
下記の用に分けて開発したかったので、
3つに切り離して開発することにしました。

  • 配布用のクリーンなテーマ
  • 開発環境ごと渡すテーマ
  • 自分用にカスタマイズしたテーマ

最初は面倒ですけれど、
このテーマをベースに制作していけば良いのでw

フォルダ構成は下記の通り

 wp-content
  themes
   配布用のテーマ…中身は必要なファイルのみ
   自分用にカスタマイズしたテーマ…中身は必要なファイルのみ
   開発用テーマ
    bower.json…ファイル
    Gruntfile.js…ファイル
    node_modules…フォルダ
    package.json…ファイル
    保留フォルダ…Gruntで実行した内容を一旦保存しておくフォルダ
    dev…作業フォルダ
     config.rb…ファイル
     scss…フォルダ
     必要なテーマファイル

でもこれどうやるの?

Gruntで必要なファイルだけ、
テーマにコピーしたり、
不要なフォルダやファイルを削除、
またファイルの結合をします。

フォルダ構成と実行内容

gruntfile.jsはすっごい複雑です(^_^;)

gruntfile.jsでのフォルダの設定

dir: {
    bin:'dev', →開発テーマ=作業フォルダ
    release:'Lovelog_ver36', →自分用のテーマ
    js: 'js', →JSフォルダ
    css: 'css', →CSSフォルダ
    img:'images', →画像フォルダ
    sass:'scss', →SCSSフォルダ
    blog:'Lovelog_ver36_blog', →配布用テーマ
    project:'Lovelog_ver36_dev', →開発環境ごと渡すテーマ
    bower:'bower_components', →Foundation5フォルダ一式
},

フォルダを指定する時、下記のように書くと、
上記で指定したフォルダに置き換えてくれます。
なので、テーマ名を変えたい場合は上記を変えればOK。

<%= dir.release %>

必要なGruntプラグイン

grunt-contribをインストールすればだいたい入っていますが、一応個別にも。

npm install --save-dev grunt-contrib

黒い画面でプロジェクトフォルダに移動して、
上記のコマンドを打てばOK。

gruntfile.jsに記述するソース

grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-csscomb');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-pngmin');

copyプラグイン以外のプラグインは、
プロジェクト範囲内しか動作しません。
copyプラグインは、プロジェクトの1つ上の階層でもコピーが可能でした。
それを逆手に取ります。

なので、開発フォルダ内に一旦保留しておくフォルダを作ります。
そこからGruntで実行された最低限のものだけをthemesフォルダ直下(1つ上の階層)にコピーするようにしました。
この一旦保留しておくフォルダは、最後にGruntで削除します。

Resume

  1. 保留フォルダ内にあるファイルを削除してクリーンな状態にする。
  2. SCSSファイルを結合する。
  3. SCSSファイルをCSSにコンパイルする。
  4. 必要なファイルやフォルダを保留フォルダにコピーする。
  5. png画像ファイルを圧縮して保留フォルダに保存する。
  6. 保留フォルダ内のCSSファイルを圧縮する。
  7. 保留フォルダ内の不要なファイルを削除する。
  8. 保留フォルダ内のJSファイルを圧縮する。
  9. 保留フォルダ内のjQuery関係のファイルを結合する。
  10. 保留フォルダ内のPC用のjQuery関係のファイルを結合する。
  11. 保留フォルダ内の結合元ファイルを全て削除する。
  12. 保留フォルダ内を全てthemesフォルダ直下のテーマにコピーする。
  13. 保留フォルダを削除する。
  14. 実行タスクを記述する。

※ここでは配布用テーマを元に解説していきます。

1.保留フォルダ内にあるファイルを削除してクリーンな状態にする。

開発中、余計なファイルやフォルダを作ることもあるので、
毎回最新の状態になるように、
最初にクリーンな状態にします。

本当は最後のthemesフォルダ直下のコピー先のフォルダも、
コピーする前に一旦クリーンな状態にしたかったのですが、
それが出来なかったので、
定期的に削除すると良いかもです。

ソースは、
clean: {}内に下記のソースを記述します。

    deleteReleaseFolder: {
        src: '<%= dir.release %>/'
    },

2.SCSSファイルを結合する。

最初に述べたように、1つのプロジェクトで、
3つのテーマを作成出来るように切り分ける必要があります。

  • 配布用のクリーンなテーマ
  • 開発環境ごと渡すテーマ
  • 自分用にカスタマイズしたテーマ

SCSSファイルを3つに切り分けます。

  • 基本となるSass…style.scss
  • 自分用にカスタマイズするSass…_home.scss
  • 配布用のSass…_blog.scss

自分用のカスタマイズで背景画像など使う場合があるので、
配布用の汎用性を考えるとどうしても切り離した方が便利なんですよね。

そして、
テーマ名も異なることから、
テーマ情報のみを記述したSCSSファイルを用意します。

  • 開発用テーマ名…_dev-theme-name.scss
  • 自分用にカスタマイズしたテーマ名…_home-theme-name.scss
  • 配布用のテーマ名…_blog-theme-name.scss

と、それぞれテーマ情報を記述したSCSSファイルを用意します。

ポイント:テーマ情報を記述する際の注意点

SCSSファイルをコンパイルしてCSSファイルにした後に、
CSSファイルを圧縮するので、
必ず/*!コメントアウトの場所に!を付けるようにします。
これをしないと、圧縮した時に削除されてしまいます。

/*!
Theme Name: Lovelog_ver36_blog
Theme URI: http://lovelog.eternal-tears.com/
Description:Lovelog+*のテーマです。
Author: Eternal-tears
Author URI: http://lovelog.eternal-tears.com/
*/

テーマごとに必要なSCSSファイルを、
下記の順番になるようにconcat: {}内に記述して結合します。

テーマ名のSCSSファイル+基本となるSCSSファイル+配布用のSCSSファイル

▼実際のソース

    sassblog: {
        src: ['<%= dir.bin %>/<%= dir.sass %>/_blog-theme-name.scss','<%= dir.bin %>/<%= dir.sass %>/style.scss','<%= dir.bin %>/scss/_blog.scss',],
        dest: '<%= dir.bin %>/<%= dir.sass %>/style-blog.scss',
    },

開発用フォルダのSCSSフォルダ内の必要なファイルを結合して、
同じフォルダ階層の中にstyle-blog.scssとして作成します。

ポイント:style.scss以外は_(アンダーバー)を付ける

基本となるstyle.scssファイル以外は、
ファイル名の先頭に_(アンダーバー)を付けておくほうが良いです。

後々にコンパイルする時に、
一々CSSファイルが作成されてしまうので(^_^;)

ポイント:SCSSファイルを結合するときの注意点

style.cssの最初にテーマ情報を記載しないと、
WordPressの管理画面上でテーマ名を認識しません。

また、CSSは優先順位があり、
後に書いたものが優先されるようになっています。

SCSSファイルを結合する順番を間違えると、
上手く表示されない場合があるので注意してください。

3.SCSSファイルをCSSにコンパイルする。

2.で結合したSCSSファイルをCSSにコンパイルします。

sass: {}内に下記のソースを記述します。

    distblog: {
      files: {
        '<%= dir.bin %>/style.css': '<%= dir.bin %>/<%= dir.sass %>/style-blog.scss'
      }
    },

2.で作成したstyle-blog.scssを開発用フォルダ内にコンパイルしたstyle.cssファイルを作成します。

4.必要なファイルやフォルダを保留フォルダにコピーする。

テーマに必要なファイル一式を、
開発用フォルダから保留フォルダにコピーします。

copy: {}内に下記のソースを記述します。

    files: { //開発フォルダ内のbower_componentsフォルダを保留フォルダにコピー
        expand:true,
        cwd:'<%= dir.bin %>/<%= dir.bower %>/',
        src:['**'],
        dest:'<%= dir.release %>/<%= dir.bower %>/'
    },
    files2: { //開発フォルダ内のlanguagesフォルダを保留フォルダにコピー
        expand:true,
        cwd:'<%= dir.bin %>/languages/',
        src:['**'],
        dest:'<%= dir.release %>/languages/'
    },
    php: { //開発フォルダ内の全てのPHPファイルを保留フォルダにコピー
        expand: true,
        cwd: '<%= dir.bin %>/',
        src: ['**/*.php'],
        dest: '<%= dir.release %>/'
    },
    css: { //開発フォルダ内の全てのCSSファイルを保留フォルダにコピー
        expand: true,
        cwd: '<%= dir.bin %>/',
        src: ['**.css'],
        dest: '<%= dir.release %>/'
    },
    images: { //開発フォルダ内のimages内の全ての画像を保留フォルダにコピー
        expand: true,
        cwd: '<%= dir.bin %>/',
        src: ['images/**'],
        dest: '<%= dir.release %>/'
    },
    js: { //開発フォルダ内のjs内の全てのjsファイルを保留フォルダにコピー
        expand: true,
        cwd: '<%= dir.bin %>/',
        src: ['js/**'],
        dest: '<%= dir.release %>/'
    },

5.png画像ファイルを圧縮して保留フォルダに保存する。

私のテーマ内の画像は全てpng画像なので、
grunt-pngminのみ使っています。
gif画像やjpg画像を圧縮したい場合は、下記のようなプラグインもあります。

pngmin: {}内に、下記のソースを記述します。

    compile:{
        options: {
            ext:'.png', //デフォルトでは拡張子の前に、【-fs8】がついて、画像名+-fs8+.pngになるので、デフォルト値を取る。
            force: true //同名の既存の画像ファイルがある場合、上書きするか否か。デフォルト値はfalseなので上書きされない設定になっているので、trueにする。
        },
        files: [{
            expand: true,
            src: ['**/*.png'], //対象ファイル
            cwd: '<%= dir.bin %>/images/', //圧縮する対象の画像フォルダ先
            dest: '<%= dir.release %>/images/' //圧縮した画像の保存先
        }]
    }

▼実際のソース

pngmin: {
    compile:{
        options: {
            ext:'.png',
            force: true
        },
        files: [{
            expand: true,
            src: ['**/*.png'],
            cwd: '<%= dir.bin %>/images/',
            dest: '<%= dir.release %>/images/'
        }]
    }
},

6.保留フォルダ内のCSSファイルを圧縮する。

4.で保留フォルダ先にコピーした全てのCSSファイルを圧縮します。

cssmin: {
    all: { //全てのCSSファイルを圧縮します。
        expand: true,
        cwd: '<%= dir.release %>/', //ファイル元
        src: ['*.css'], //全てのCSSファイル
        dest: '<%= dir.release %>/' //圧縮したファイルの保存先
    }
},

7.保留フォルダ内の不要なファイルを削除する。

4.で保留フォルダ先のFoundation5関係の不要なファイルを削除します。
不要と思われるフォルダやファイルを全て記述しているので、
必要なファイルは//でコメントアウトか,
'(コンマ)の後に!を付けて、
'!のようにすると除外されます。

'!<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.dropdown.js',

私の場合は、コメントアウトした方が分かりやすいので(^^ゞ

clean: {}内に下記のソースを記述します。

    deletebower:{
        src: [
            '<%= dir.release %>/<%= dir.bower %>/fastclick/',
            '<%= dir.release %>/<%= dir.bower %>/jqueryplaceholder/',
            '<%= dir.release %>/<%= dir.bower %>/jquery.cookie/',
            '<%= dir.release %>/<%= dir.bower %>/modernizr/feature-detects/',
            '<%= dir.release %>/<%= dir.bower %>/modernizr/media/',
            '<%= dir.release %>/<%= dir.bower %>/modernizr/test/',
            '<%= dir.release %>/<%= dir.bower %>/modernizr/grunt.js',
            '<%= dir.release %>/<%= dir.bower %>/jquery/bower.json',
            '<%= dir.release %>/<%= dir.bower %>/jquery/dist/jquery.min.map',
            '<%= dir.release %>/<%= dir.bower %>/jquery/dist/jquery.js',
            '<%= dir.release %>/<%= dir.bower %>/jquery/src/',
            '<%= dir.release %>/<%= dir.bower %>/jquery-placeholder/'
        ]
    },
    deletefoundationblog:{
        src: [
            '<%= dir.release %>/<%= dir.bower %>/foundation/css/',
            '<%= dir.release %>/<%= dir.bower %>/foundation/scss/',
            '<%= dir.release %>/<%= dir.bower %>/foundation/bower.json',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation.min.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.abide.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.accordion.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.alert.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.clearing.js',
            //'<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.dropdown.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.equalizer.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.interchange.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.joyride.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.magellan.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.offcanvas.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.orbit.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.reveal.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.slider.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.tab.js',
            '<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.tooltip.js',
            //'<%= dir.release %>/<%= dir.bower %>/foundation/js/foundation/foundation.topbar.js',
        ]
    },

8.保留フォルダ内のJSファイルを圧縮する。

4.で保留フォルダ先にコピーした全てのJSファイルを圧縮します。

uglify: {}に下記のソースを記述します。

    min: { //保留フォルダ/js/全てのJSファイルを圧縮します。
        expand: true,
        cwd: '<%= dir.release %>/<%= dir.js %>/',
        src: ['**/*.js'],
        dest: '<%= dir.release %>/<%= dir.js %>/'
    },
    foundation:{ //保留フォルダ/bower_components/foundationフォルダ内にある全てのJSファイルを圧縮します。
        expand: true,
        cwd: '<%= dir.release %>/<%= dir.bower %>/foundation',
        src: ['**/*.js','!**/foundation.min.js'],
        dest: '<%= dir.release %>/<%= dir.bower %>/foundation'
    },
   modernizr:{ //保留フォルダ/bower_components/modernizrフォルダ内にある全てのJSファイルを圧縮します。
        expand: true,
        cwd: '<%= dir.release %>/<%= dir.bower %>/modernizr',
        src: ['modernizr.js'],
        dest: '<%= dir.release %>/<%= dir.bower %>/modernizr'
    }

9.保留フォルダ内のjQuery関係のファイルを結合する。

    jquery: {
        src: ['<%= dir.release %>/<%= dir.bower %>/jquery/**/*.js','<%= dir.release %>/<%= dir.bower %>/modernizr/*.js','<%= dir.release %>/<%= dir.bower %>/foundation/js/**/*.js'], //結合する対象ファイル
        dest: '<%= dir.release %>/<%= dir.js %>/bower.all.js', //結合してリネームしたファイルの保存先
    },

▼実際のソース

    othersblog: {
        src: ['<%= dir.release %>/<%= dir.js %>/jquery.sticky-kit.min.js','<%= dir.release %>/<%= dir.js %>/jQueryAutoHeight.js','<%= dir.release %>/<%= dir.js %>/in-function-blog.js'],
        dest: '<%= dir.release %>/<%= dir.js %>/others.js',
    },

ポイント:JSファイルを結合する時の注意点

JSファイルの結合時に、
順番を間違えると動作しなくなるので注意しましょう。

例えばjQuery関係は下記のように書きます。
基本結合時は、フォルダ階層に則ってABC順に結合されていきます。
jQueryとModernizrとFundation5の順番は守るように記述します。

    jquery: {
        src: ['<%= dir.release %>/<%= dir.bower %>/jquery/**/*.js','<%= dir.release %>/<%= dir.bower %>/modernizr/*.js','<%= dir.release %>/<%= dir.bower %>/foundation/js/**/*.js'],
        dest: '<%= dir.release %>/<%= dir.js %>/bower.all.js',
    },

10.保留フォルダ内のPC用のjQuery関係のファイルを結合する。

モバイル用とPC用のJSを切り分けるために、
PC用のjQueryのプラグインファイルを結合をします。

私の場合は、モバイルでjQueryのプラグインは使用しないのですが、
もし複数プラグインを使用する場合は、モバイルも結合した方が良いですね。

concat: {}内に下記のソースを記述します。
こちらもjQueryの順序を守るようにしないと、
動作しなくなるので注意します。

    othersblog: {
        src: ['<%= dir.release %>/<%= dir.js %>/jquery.sticky-kit.min.js','<%= dir.release %>/<%= dir.js %>/jQueryAutoHeight.js','<%= dir.release %>/<%= dir.js %>/in-function-blog.js'],
        dest: '<%= dir.release %>/<%= dir.js %>/in-function.js',
    },

ポイント:モバイルで使用しないJavascriptファイルは負荷を避ける為に切り分ける

モバイルで使用しないJSがあるので、
少しでも負荷を減らすために、
PC用とモバイル用で使用するJSを切り分けます。

テーマはwp_is_mobile(){}でfunctions.phpに下記のように書くと切り分けが可能となります。

    wp_register_script('in-function', get_template_directory_uri() . '/js/in-function.js', false, NULL);
    wp_register_script('in-function-mobile', get_template_directory_uri() . '/js/in-function-mobile.js', false, NULL);

    if(wp_is_mobile()){
        wp_enqueue_script( 'in-function-mobile',array('jquery'),true );
    }else{
        wp_enqueue_script( 'in-function',array('jquery'),true );
    }

11.保留フォルダ内の結合元ファイルを全て削除する。

結合元の不要なファイルを削除します。

clean: {}内に下記のソースを記述します。

    deleteallblog:{
        src:[
            '<%= dir.release %>/<%= dir.bower %>/**/',
            '<%= dir.release %>/**/*.js',
            '<%= dir.release %>/home.php',
            '<%= dir.release %>/reference.php',
            '<%= dir.release %>/content-category.php',
            '<%= dir.bin %>/<%= dir.sass %>/style-blog.scss',
            '!<%= dir.release %>/<%= dir.js %>/bower.all.js',
            '!<%= dir.release %>/<%= dir.js %>/in-function.js',
            '!<%= dir.release %>/<%= dir.js %>/in-function-mobile.js',
        ]
    },

私の場合は、自分用のテーマと配布用のテーマと2つ切り分けています。

今回は配布用テーマを元に記述しているので、
自分用のカスタマイズしているテーマを削除しています。

また、全てのJSファイルを削除する指示をしていますが、
必要なファイルは'!のように、
‘(シングルコーテーション)’の後に!を付けて除外しています。

12.保留フォルダ内を全てthemesフォルダ直下のテーマにコピーする。

これで必要なファイルのみ保留フォルダ内にある状態になりました。
copy: {}内に下記のソースを記述します。

< %= dir.blog %>は、ブログ用テーマ先なのですが、
そのままだと、開発フォルダ内にコピーされてしまいます。

なので、../を記述して、1つ上の階層に保存されるようにします。

    allcopyblog: {
        expand:true,
        cwd:'<%= dir.release %>', //コピー元:保留ファイル先を記述
        src:['**'],
        dest:'../<%= dir.blog %>' //コピー先:1つ上の階層のテーマ直下先にコピー
    },

13.保留フォルダを削除する。

これで保留フォルダは不要になったので削除します。

clean: {}内に下記のソースを記述します。
ここで、開発フォルダ内のstyle.cssを記述するのは、
テーマフォルダ内直下でなくても、style.cssのテーマ情報の記述が認識されて、
ややこしくなるので削除しています。

    deletedevtheme:{
        src:[
            '<%= dir.release %>/**/',
            '<%= dir.bin %>/style.css',
        ]
    },

14.実行タスクを記述する。

上記の流れを元に記述して、

  • 配布用テーマ
  • 自分用テーマ
  • 開発環境毎渡すテーマ

別にタスクを作っていきます。

//私用テーマ作成用
grunt.registerTask('home', ['clean:deleteReleaseFolder','concat:sasshome','sass:disthome','copy:files','copy:files2','copy:php','copy:css','copy:js','pngmin','cssmin:all','clean:deletebower','clean:deletefoundationhome','uglify','concat:jquery','concat:othershome','clean:deleteallhome','copy:allcopyhome','clean:deletedevtheme']);

//配布用テーマ作成用
grunt.registerTask('blog', ['clean:deleteReleaseFolderblog','concat:sassblog','sass:distblog','copy:files','copy:files2','copy:php','copy:css','copy:js','pngmin','cssmin:all','clean:deletebower','clean:deletefoundationblog','uglify','pngmin','concat:jquery','concat:othersblog','clean:deleteallblog','copy:allcopyblog','clean:deletedevtheme']);

//開発環境ごと渡す用
grunt.registerTask('dev', ['clean:deleteReleaseFolderdev','copy:allcopydev','clean:deletealldev']);

//ローカル確認用私用テーマ作成用…非圧縮
grunt.registerTask('local-home', ['clean:deleteReleaseFolder','concat:sasshome','sass:disthome','copy:files','copy:files2','copy:php','copy:css','copy:images', 'copy:js','csscomb','clean:deletebower','clean:deletefoundationhome','concat:jquery','concat:othershome','clean:deleteallhome','copy:allcopyhome','clean:deletedevtheme']);

//ローカル確認用配布用テーマ作成用…非圧縮
grunt.registerTask('local-blog', ['clean:deleteReleaseFolderblog','concat:sassblog','sass:distblog','copy:files','copy:files2','copy:php','copy:css','copy:images','copy:js','csscomb','clean:deletebower','clean:deletefoundationblog','concat:jquery','concat:othersblog','clean:deleteallblog','copy:allcopyblog','clean:deletedevtheme']);

まとめ

最初、このファイルを作成する時は本当に面倒くさかったのですが、
作成し終わった時は、何てラクなんだろうと思いました。
実行タスクがちょっと多いので時間かかります(^_^;)

glupにすればもうちょっとマシなのかな…。

LiveReloadもやろうと思ったのですが、
上手く動作しないので、
検証する時間もないし、今回は省きました。

もっと色々やりようはあると思うのですが、
ちょっと作業できる時間が限られているので、
また閃いて実装できたら、
記事を書きたいと思います。

今回のGruntfile.js

Sponsored Link