自上次发表了 Grunt:初次使用及前端构建经验 后,前端同学 cobish 对笔者说,grunt 不太好用,现在我们项目中已经不用了,现在用 glup。什么?那你赶紧给笔者我介绍一下。cobish 抠着鼻屎对我说,你去看我的笔记就好啦!好吧,看完后,笔者又整理了一篇关于我们在项目中,使用 glup 的前端文章分享给大家。
gulp 初试用
在用了 Grunt 的一段时间内,越来越觉得自己离不开构建工具。但是,Grunt 的构建速度让我有点苦恼,即使是编译 sass 也需要花上一段时间。于是,我开始试用 gulp,结果意外地让我惊喜。
下面代码是使用 gulp 初次来编译 sass,由于一直都有点习惯了 Grunt 那编译速度单位为秒级别的速度,刚输入命令还没反应过来,就已经以毫秒级的速度编译完了。
var gulp = require('gulp'); var sass = require('gulp-sass'); var sourcemaps = require('gulp-sourcemaps');
// sass 编译
gulp.task('sass:dev', function() {
return gulp.src('dev/sass/**/*.scss')
.pipe(sourcemaps.init())
.pipe(sass({outputStyle: 'expanded'}).on('error', sass.logError))
.pipe(sourcemaps.write())
.pipe(gulp.dest('dev/css'));
});
比较一下 Grunt 与 gulp 编译同一套 sass 代码下所花费的时间:
并不是说 Grunt 就比 gulp 不好,也有 gulp 办不到 Grunt 办得到的事情。但是就运行速度相比,gulp 的速度确实是完胜。
gulp 打包 requirejs
目前我的项目是一个页面一个 js 入口,比如登录页面的入口是 login.js,而主页面的入口是 home.js,它们都是在同一个目录下。
├─ src/
├─ js/
├─ lib
├─ require.min.js
└─ jquery-1.11.1.min.js
├─ mod
├─ home.js
└─ login.js
└─ config.js
├─ gulpfile.js
└─ package.js
html 每个页面的引入是这样子的:
<script type="text/javascript" src="__JSPATH__/config.js"></script>
<script type="text/javascript" src="__JSPATH__/lib/requirejs.min.js" data-main="__JSPATH__/mod/login.js"></script>
requirejs 的 config 文件如下:
var requirejs = {
paths: {
jquery: '../lib/jquery-1.11.1.min',
widget: '../widget'
}
};
接下来就是使用 gulp 对 js 文件进行打包,用到的是「gulp-requirejs-optimize」,由于项目是多入口文件,所以需要批量打包,打包的代码如下:
var gulp = require('gulp'); var requirejsOptimize = require('gulp-requirejs-optimize');
gulp.task('rjs', function () {
return gulp.src('src/js/mod/*.js')
.pipe(requirejsOptimize({
mainConfigFile: 'src/js/config.js',
exclude: [
'jquery'
]
}))
.pipe(gulp.dest('dist/js/mod'));
});
运行相应命令,即可完成打包:
$ gulp rjs
gulp 自动刷新浏览器
项目本地后端开发语言是是基于 apache 的 php,域名为 cloud.xxx.com。刚开始想实现浏览器 F5 自动刷新使用到的是 grunt 和 livereload 插件,gulp 也有对应的方法,参考 gulp 教程之 gulp-livereload。但是,它需要浏览器安装 livereload 插件才能使用,chrome 的插件需要访问外国网站下载,firefox 的插件不起作用,其它的浏览器也无法实现自动刷新。
后来,我发现了 Browsersync。简直就像是找到了宝藏一样,同样支持 grunt 和 gulp。以下代码是使用代理去实现:
var gulp = require('gulp'); var browserSync = require('browser-sync').create();
gulp.task('serve', function () {
browserSync.init({
proxy: "http://cloud.xxx.com"
});gulp.watch('*.html').on('change', browserSync.reload);
});
运行命令,默认的浏览器会自动打开 127.0.0.1:3000 链接
gulp serve
如果想多个浏览器都可以自动刷新,只需要打开其它浏览器,把刚刚的链接输入即可。还有,由于项目原因,开发的时候不能使用到 127.0.0.1 ,想换成 php 配置的域名怎么做?直接把 127.0.0.1:3000 链接换成 cloud.xxx.com:3000 即可,Browsersync 实际就是监听 3000 端口来实现刷新浏览器。
基于 gulp 的前端构建
之前使用 Grunt 时总结了一篇「基于 Grunt 的前端构建」。在使用了 Grunt 的一段时间后,我发现了 gulp 的运行速度比 Grunt 快很多,于是便从 Grunt 转移阵地到了 gulp。以下的构建思路跟 Grunt 的构建很类似。如果你也使用过 Grunt,那我相信你一定能够很快地切换到 gulp。当然如果你没有接触过 Grunt,我相信你一定也能够很快上手 gulp。
目录结构
├─ gulp/ # gulp 配置目录
├─ tasks # 任务配置目录
├─ image.js # 图片配置
├─ other.js # 其它配置
├─ script.js # 脚本配置
├─ style.js # 样式配置
└─ view.js # 页面配置
└─ config # gulp 配置文件
├─ src/ # 开发目录
├─ html/ # 存放 html 的目录
├─ app/ # 可提取复用的页面模块
└─ page/ # 各页面入口文件
├─ images/ # 存放图片的目录
├─ single/ # 不需要合并的图片
└─ sprite/ # 需要合并的图片
├─ js/ # 存放 js 的目录
├─ app/ # 可提取复用的脚步模块
├─ lib/ # 第三方 js 库
├─ page/ # 各页面入口脚本文件
└─ config.js # RequireJs 的配置文件
└─ sass/ # 存放 sass 的目录
├─ app/ # 可提取复用的样式模块
└─ page/ # 各页面入口样式文件
├─ .jshintrc # jshint 参数配置文件
├─ gulpfile.js # gulp 入口配置文件
└─ package.json # npm 包管理文件
gulp 目录
参考: 前端 | 重构 gulpfile.js。其中专门创建一个 gulp 目录用来存放 gulp 代码,为了能够将任务更加细化,职责单一,方便日后的维护更新。
└─ gulp/ # gulp 配置目录
├─ tasks # 任务配置目录
├─ image.js # 图片配置
├─ other.js # 其它配置
├─ script.js # 脚本配置
├─ style.js # 样式配置
└─ view.js # 页面配置
└─ config # gulp 配置文件
命令
$ gulp help # 说明帮助
$ gulp sass # sass 本地编译
$ gulp jshint # js 语法检测
$ gulp include # html 包含依赖编译
$ gulp dev # 开发监控,浏览器不自动刷新
$ gulp serve # 开发监控,浏览器自动刷新
$ gulp build # 打包上线
开发阶段
执行 gulp dev 命令,gulp 会进行一系列构建操作,最后在 dist 目录下生成可运行文件,并实时监听源文件,一旦源文件改动会执行相应的操作。
// 开发监控,浏览器不自动刷新
gulp.task('dev', function(cb) {
runSequence(
'clean:dist',
'clean:tmp',
['copy:img', 'sass', 'jshint', 'copy:js', 'include'],
'watch',
cb
);
});
执行 gulp serve 命令,gulp 会执行跟 gulp dev 一样的操作并监听源文件,唯一不同的是它在执行后会监听某个端口,一旦有文件改动它会帮你自动刷新浏览器,帮你省下了按 F5 的力气。当然在同时开上多个浏览器测试页面时它将会很有帮助。
// 开发监控,浏览器自动刷新
gulp.task('serve', function(cb) {
runSequence(
'clean:dist',
'clean:tmp',
['copy:img', 'sass', 'jshint', 'copy:js', 'include'],
'reload',
cb
);
});
上线打包阶段
参考:张云龙的 大公司里怎样开发和部署前端代码?。通过以下代码一个大体知道,上线打包主要是对图片样式脚本进行打包处理。所以接下来的工作就是职责分工,独立完成各自的构建工作。
gulp.task('build', function(cb) {
runSequence(
'clean:dist',
'clean:tmp',
'build:img',
'build:css',
'build:js',
'build:html',
'clean:tmp',
cb
);
});
第一个步骤主要是对图片进行处理,包括图片合并压缩 hash 戳等。其中对 css 代码处理是为了替换合并后的图片路径。
// 打包图片
gulp.task('build:img', function(cb) {
runSequence(
'sass:tmp',
'copy:tmpImg',
'autoSprite',
'imagemin',
'rev:img',
cb
)
});
第二个步骤主要是对 css 文件进行处理,其中还包括替换已经 hash 的图片资源,并生成 hash 戳。
// 打包 css 文件
gulp.task('build:css', function(cb) {
runSequence(
'usemin:css',
'sass:dist',
'rev:css',
cb
);
});
第三个步骤是 js 文件的打包,打包 RequireJs 代码可以根据依赖进行 js 文件的合并压缩,最终每个页面都打包一个 js 文件为单入口。
// 打包 js 文件
gulp.task('build:js', function(cb) {
runSequence(
'requirejs',
'uglify:config',
'rev:js',
'copy:js',
cb
);
});
第四个步骤是 html 文件的打包,替换掉前面已经 hash 的静态资源即可。
// 打包 html 文件
gulp.task('build:html', function(cb) {
runSequence(
'include',
'usemin:html',
cb
);
});
最终生成的代码依然在 dist 目录下,也就是说在开发阶段与上线打包阶段构建生成的代码都在同一个目录下,只不过在开发阶段代码是未进行合并压缩,上线打包阶段代码是经过合并压缩打上 hash 戳的。所以建议该目录下的代码不需要添加到版本控制中。
未解决的问题
开发阶段:对 RequireJs 的路径配置(config.js 与 gulp 中的配置)感到困惑迷糊,多创建一个目录就路径不匹配打包不成功。
上线打包阶段:RequireJS 若添加第三方库,需要手动修改 gulp 代码。