使用 Gulp 来实现 Laravel 4 的 Assets 管理
The Problem
需要有一个自动化工具来管理 Assets (css,js,img etc..) , 具备以下功能
- 支持编译 scss, coffee script, less 等语言;
- 合并文件的能力, 把页面上所有的 css 和 js 各自合并为一个文件, 将极大提高网页的加载速度;
- 压缩 css 和 js 的能力, 减少文件大小, 注意这里的压缩不只是去除空格和换行;
- 支持 CDN 加速, 需要对每次修改的文件加版本号, 这是最有效的, 也是最合理的客户端样式更新方法;
- 开发要足够简单;
- 部署要足够简单;
有两个工具作为候选, 一个是 Grunt
, 一个是 Gulp
, 最后选择 Gulp
, 因为其易用性, 并且是个新奇玩意, 此项目在 web 开发的开源世界中非常受欢迎, 虽然出现的时间只有短短的一年 (第一个提交 contra authored on Jul 4, 2013).
安装和配置 gulp.js
gulp.js 依赖于 nodejs, 如果你使用 Homestead
作为开发环境的话, 这些东西已经都装好了, 如果你还没有安装 Homestead
请看这里 Laravel 4 的 Homestead 开发环境部署 .
配置 package.json
在项目根目录下, 创建 package.json
并填入以下内容:
{
"devDependencies": {
"del": "^0.1.2",
"gulp": "^3.8.7",
"gulp-asset-manifest": "0.0.4",
"gulp-autoprefixer": "0.0.10",
"gulp-concat": "^2.3.4",
"gulp-minify-css": "^0.3.7",
"gulp-rev": "^1.1.0",
"gulp-sass": "^0.7.3",
"gulp-uglify": "^1.0.0"
}
}
关于里面每一个 package
的说明见以下注释:
{
"devDependencies": {
"del": "^0.1.2", // 删除文件
"gulp": "^3.8.7", // 主程序
"gulp-asset-manifest": "0.0.4", // 压缩 css
"gulp-autoprefixer": "0.0.10", // 自动给 css3 属性加浏览器前缀, 如: `-webkit-`
"gulp-concat": "^2.3.4", // 文件合并
"gulp-minify-css": "^0.3.7", // // 压缩 css
"gulp-rev": "^1.1.0", // 给文件加版本号, 如 `script-$version$.js`
"gulp-sass": "^0.7.3", // 编译 scss
"gulp-uglify": "^1.0.0" // 压缩 js
}
}
保存后在根目录下运行以下命令下载 package
npm install
Gulpfile.js
在根目录下创建文件 Gulpfile.js
, 填入以下内容:
var gulp = require('gulp');
var sass = require('gulp-sass');
var autoprefixer = require('gulp-autoprefixer');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var rev = require('gulp-rev');
var del = require('del');
var filename = require('gulp-asset-manifest');
var minifycss = require('gulp-minify-css');
// Paths to your asset files
var paths = {
frontend: {
scripts: [
'app/assets/js/jquery.min.js',
'app/assets/js/bootstrap.min.js',
'app/assets/js/moment.min.js',
'app/assets/js/zh-cn.min.js',
'app/assets/js/emojify.min.js',
'app/assets/js/jquery.scrollUp.js',
'app/assets/js/jquery.pjax.js',
'app/assets/js/nprogress.js',
'app/assets/js/jquery.autosize.min.js',
'app/assets/js/prism.js',
'app/assets/js/jquery.textcomplete.js',
'app/assets/js/emoji.js',
'app/assets/js/ekko-lightbox.js',
'app/assets/js/main.js',
],
styles: [
'app/assets/css/bootstrap.min.css',
'app/assets/css/font-awesome.min.css',
'app/assets/css/main.css',
'app/assets/css/markdown.css',
'app/assets/css/nprogress.css',
'app/assets/css/prism.css',
]
}
}
// CSS task
gulp.task('css', function() {
// Convert scss first
gulp.src('app/assets/sass/**/*.scss')
.pipe(sass())
.pipe(autoprefixer('last 10 version'))
.pipe(gulp.dest('app/assets/css'));
// Cleanup old assets
del(['public/assets/css/styles-*.css'], function (err) {});
// Prefix, compress and concat the CSS assets
// Afterwards add the MD5 hash to the filename
gulp.src(paths.frontend.styles)
.pipe(concat('styles.css'))
.pipe(rev())
.pipe(filename({ bundleName: 'frontend.styles' })) // This will create/update the assets.json file
.pipe(minifycss())
.pipe(gulp.dest('public/assets/css'));
});
// JavaScript task
gulp.task('js', function() {
// Cleanup old assets
del(['public/assets/js/scripts-*.js'], function (err) {});
// Concat and uglify the JavaScript assets
// Afterwards add the MD5 hash to the filename
gulp.src(paths.frontend.scripts)
.pipe(concat('scripts.js'))
.pipe(uglify())
.pipe(rev())
.pipe(filename({ bundleName: 'frontend.scripts' })) // This will create/update the assets.json file
.pipe(gulp.dest('public/assets/js'));
});
gulp.task('build', ['css', 'js']);
gulp.task('watch', function(){
gulp.watch('app/assets/sass/**/*.scss', ['css']);
gulp.watch('app/assets/css/**/*.css', ['css']);
gulp.watch('app/assets/js/**/*.js', ['js']);
});
// The default task (called when you run `gulp` from cli)
gulp.task('default', ['build', 'watch']);
关于上面代码的一些解释:
// 第一部分是引入 package
var gulp = require('gulp');
... 中间省略一堆
var minifycss = require('gulp-minify-css');
// 手动来设置这些文件, 这样的话会我们可以控制其合并文件时候
// 的顺序, 以后要加入某个 js 或者 css 的时候都在此添加
var paths = {
frontend: {
scripts: [
'app/assets/js/jquery.min.js',
... 中间省略一堆
'app/assets/js/main.js',
],
styles: [
'app/assets/css/bootstrap.min.css',
... 中间省略一堆
'app/assets/css/prism.css',
]
}
}
// CSS task
gulp.task('css', function() {
// 先编译 scss
gulp.src('app/assets/sass/**/*.scss')
.pipe(sass())
.pipe(autoprefixer('last 10 version'))
.pipe(gulp.dest('app/assets/css'));
// 清除之前的文件
del(['public/assets/css/styles-*.css'], function (err) {});
gulp.src(paths.frontend.styles) // 处理scc 文件
.pipe(concat('styles.css')) // 合并
.pipe(rev()) // 加版本号
.pipe(filename({ bundleName: 'frontend.styles' })) // 生成 asset_manifest.json 文件
.pipe(minifycss()) // 压缩
.pipe(gulp.dest('public/assets/css')); // 存放到 `public/assets/css` 文件夹
});
// JavaScript task
gulp.task('js', function() {
// 清除之前的文件
del(['public/assets/js/scripts-*.js'], function (err) {});
gulp.src(paths.frontend.scripts) // 处理 js 文件列表
.pipe(concat('scripts.js')) // 合并
.pipe(uglify()) // 压缩
.pipe(rev()) // 加版本号
.pipe(filename({ bundleName: 'frontend.scripts' })) // 生成 asset_manifest.json 文件
.pipe(gulp.dest('public/assets/js')); // 存放到 `public/assets/js` 文件夹
});
// 设置 build 任务, 此任务调用上面定义的 css 和 js 任务
gulp.task('build', ['css', 'js']);
// 设置 build 任务, 方便开发, css 和 js 文件一修改, 立刻进行重新编译
gulp.task('watch', function(){
gulp.watch('app/assets/sass/**/*.scss', ['css']);
gulp.watch('app/assets/css/**/*.css', ['css']);
gulp.watch('app/assets/js/**/*.js', ['js']);
});
// 默认命令行运行 `gulp` 的时候开始执行 build 和 watch 任务
gulp.task('default', ['build', 'watch']);
asset_manifest.json 文件
保存完上面文件后, 当我们在命令行下运行
gulp build
后, 就会在 public/assets/js
和 public/assets/css
生成最终会在模版里面使用的文件, 文件名:
- styles-$version$.css
- scripts-$version$.js
其中 $version$
是变量, 每一次文件修改的时候, 都会不一样, 类似于 scripts-39eb8a9a.js
和 styles-7c717f38.css
.
问题: $version$
会随着文件修改而变化, 模版里该怎么来引用他们?
asset_manifest.json
文件的作用就是为了解决这个问题, 每一次文件一修改, 作为各种操作后, gulp-asset-manifest
插件会把修改后的文件名存放到此文件里面, 类似于这样:
{
"frontend.scripts":[
"scripts-39eb8a9a.js"
],
"frontend.styles":[
"styles-7c717f38.css"
]
}
那么接下需要做的事情就是写一个 php 脚本来读取并解析此文件, 获取最终处理好的 css 和 js 文件名, 并在模版里面引用.
Laravel4 的 asset-manager Package
asset-manager
正是做了上面最后谈到的 一个 php 脚本
所做的事, 原始的项目在这里 modbase/asset-manager , 可惜只支持 Laravel4.1, 原作者已经不再维护, 只能自己 folk 一份, 并做了些许修改 summerblue/asset-manager .
Composer 安装 asset-manager
使用 repository 来安装, 在 composer.json 里面加入:
"repositories": [
{
"type": "vcs",
"url": "https://github.com/summerblue/asset-manager"
}
],
在 require 节点下添加
"summerblue/asset-manager": "0.2.*"
修改后如下图:
然后
composer update
修改 app/config/app.php
, 在 providers
数组里面添加:
'Modbase\AssetManager\AssetManagerServiceProvider'
至此部署完成, 接下来讲讲怎么使用.
一般使用
在修改 css 和 js 之前, 先在项目根目录下命令行执行 gulp
:
➜ phphub git:(master) ✗ gulp
[07:45:45] Using gulpfile ~/Projects/phphub.org/phphub/Gulpfile.js
[07:45:45] Starting 'css'...
[07:45:45] Finished 'css' after 12 ms
[07:45:45] Starting 'js'...
[07:45:45] Finished 'js' after 10 ms
[07:45:45] Starting 'build'...
[07:45:45] Finished 'build' after 5.17 μs
[07:45:45] Starting 'watch'...
[07:45:45] Finished 'watch' after 14 ms
[07:45:45] Starting 'default'...
[07:45:45] Finished 'default' after 5 μs
[07:45:47] Starting 'css'...
[07:45:47] Finished 'css' after 4.12 ms
[07:45:47] Starting 'css'...
[07:45:47] Finished 'css' after 4.13 ms
请让此命令行一直保持执行着, 因为 TA 正在监控着你的文件修改.
需要添加 css 和 js 文件的话, 请到 Gulpfile.js
里面的 paths
选项下添加, 并重启 gulp
的监控.
添加 .gitignore 文件
修改根目录下的 . gitignore
文件, 添加这几行:
public/assets/css/*
public/assets/js/*
asset_manifest.json
这些是 gulp 的产生物, 不需要入版本.
生产环境下的部署
生产环境下, 每一次修改 assets 的话, 都需要执行此命令:
gulp build
如果是使用 envoy 进行远程部署的话, 只需要添加多一个 task :
@task('assets:publish')
cd /var/www/phphub
git pull origin master
gulp build
@endtask
允许本地命令行下运行一条命令进行部署:
envoy run assets:publish
关于 Envoy 请见这里 Laravel Envoy 优雅的 SSH 远程任务执行工具.
:sun_with_face: :sun_with_face: :sun_with_face: :sunflower: :sunglasses:
-- EOF --
欢迎关注 LaravelTips
, 这是一个专注于为 Laravel 开发者服务, 致力于帮助开发者更好的掌握 Laravel 框架, 提升开发效率的微信公众号.
推荐文章: