Gulp 入门

年后,前端林宥嘉走了,一个满口广普的93年小孩儿。有点伤感,曾经在下班之后聊过几次,通常都是他教我写代码,帮我找bug。有几个瞬间,我突然有了无数动力学前端,因为想变得像他一样厉害。同样刚刚毕业,这是我心里学霸的模样,可又是个很有意思的人。

最近因为项目重新翻出了gulp,发现好久不用又忘记了。总是这样,学了忘忘了学,脑容量让人堪忧。

之前从“林宥嘉”的主页里看到一篇很好的用于gulp入门的翻译,以下内容完全是用代码重现了整个过程,聊表怀念:Gulp新手入门教程

安装gulp

安装gulp之前,先安装node.js,点这里。然后使用 npm install 命令安装gulp

1
sudo npm install gulp -g

npm install从字面意义上来看就是安装,安装什么呢?你可以想象远程有个地方存着好多js库,npm install的意义就是从这个库里下载你需要的js代码。而在gulp当中,你需要的js库被称作“插件”或“包”。

只要知道插件的名称,就可以通过npm install下载到本地,如果你不知道插件的名称,可以在npmjs里进行查找。例如,你想下载编译scss文件的插件,就可以在终端输入

1
$ npm install sass

sass就是插件的名称。

因为npm install安装插件是从国外服务器下载,常常会受到网络限制。阿里就提供了一个npm镜像库,把库从国外同步到了国内,10分钟从npmjs.org同步一次。安装后将命令改为cnpm install即可。

被下载的插件被存放在node_modules文件夹对应的模块名文件夹中。引用方式由<script src="node_modules/gulp"></script>变成了在gulpfile.js文件的require,默认路径是node_modules/

1
var gulp = require('gulp');

创建gulp项目

1
$ npm init

npm init 执行后,创建package.json文件,package.json文件包含项目相关信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
}
}

执行命令,局部安装依赖包。

1
npm install gulp --save-dev

使用--save-dev会在package.json文件中添加依赖信息。查看package.json文件,会发现devDependencies字段多了一行信息,这就是我们刚刚添加的依赖。

1
2
3
"devDependencies": {
"gulp": "^3.9.1"
}

依赖的作用在于,当执行npm install时,会安装devDependencies下的所有插件。这一功能在多人协作开发当中是非常有用的。

目录结构

除了node_modulespackage.json可以通过命令生成,其他目录结构是需要手动创建的。通常情况下,目录结构如下

1
2
3
4
5
6
7
8
9
10
11
├── app/
├── css/
├── fonts/
├── images/
├── index.html
├── js/
| └── scss/
├── dist/
├── gulpfile.js
├── node_modules/
└── package.json

在这个结构当中,app文件夹为开发目录,源文件都在这个文件夹下。dist文件夹用来存放生产环境的文件。gulpfile.js是最重要的一个文件,它是gulp项目的配置文件,下文讲到的所有代码都写在这个文件中。

第一个gulp项目

在创建好的gulpfile.js文件中写入以下内容

1
2
3
4
5
var gulp = require('gulp');

gulp.task('hello', function() {
console.log('Hello World!');
});

第一行代码引用了需要的gulp包。gulp.task('hello',...)自定义了一个名叫“hello”的任务,任务的内容就是输出Hello World!

在命令行中执行

1
$ gulp hello

控制台输出Hello World!,这就是我们写的第一个gulp项目。

通过使用不同的插件,可以对文件进行操作,比如编译scss文件。

1
2
3
4
5
gulp.task('task-name', function () {
return gulp.src('source-files') // Get source files with gulp.src
.pipe(aGulpPlugin()) // Sends it through a gulp plugin
.pipe(gulp.dest('destination')) // Outputs the file in the destination folder
})

gulp 执行预处理

首先,安装 gulp-sass 插件

1
$ npm install gulp-sass --save-dev

gulpfile.js文件:

1
2
3
4
5
6
7
8
9
var sass = require('gulp');
// 添加 gulp-sass 插件
var sass = require('gulp-sass');

gulp.task('sass', function(){
return gulp.src('app/scss/style.scss') // Get source files
.pipe(sass()) // Converts Sass to CSS with gulp-sass
.pipe(gulp.dest('app/css')) // Outputs the file
});

这样,当我们编辑并保存style.scss文件以后,在控制台执行

1
$ gulp sass

就会在app/css目录下生成编译好的style.css文件。

Node中的通配符

通配符,用于对文件名和文件路径进行匹配,一种匹配模式可以匹配多个文件。

常用的几种如下:

  1. *.scss:*号匹配当前目录任意文件,所以这里*.scss匹配当前目录下所有scss文件
  2. **/*.scss:匹配当前目录及其子目录下的所有scss文件。
  3. !not-me.scss:!号移除匹配的文件,这里将移除not-me.scss
  4. *.+(scss|sass):+号后面会跟着圆括号,里面的元素用|分割,匹配多个选项。这里将匹配scss和sass文件。

如果写了多个scss文件,可以利用通配符实现多个文件同时编译。例如:

1
2
3
4
5
gulp.task('sass', function() {
return gulp.src('app/scss/**/*.scss')
.pipe(sass())
.pipe(gulp.dest('app/css'))
})

这样,app/scss目录及其子目录下的所有scss文件都会编译到app/css目录下。语法如下:

1
2
// Gulp watch syntax  
gulp.watch('files-to-watch', ['tasks', 'to', 'run']);

监听Sass文件

改造一下上面的例子:

1
2
3
4
gulp.task('watch', function(){
gulp.watch('app/scss/**/*.scss', ['sass']);
// Other watchers
})

如果我们修改了app/scss文件夹下的scss文件,就会执行名叫’sass’的任务。

Browser Sync 自动刷新

参考文档:Browsersync + Gulp.js 说明文档

在刚刚完成的任务中,我们实现了自动编译scss文件的功能。可是编译之后要刷新浏览器才能看到效果,我想保存一下就自动刷新浏览器,这样开发起来就更方便了。这时我们就用到了 Browser Sync。

安装插件

1
$ npm install browser-sync --save-dev

创建 Browser Sync 任务,并设定根目录的位置。

1
2
3
4
5
6
7
8
// 静态服务器
gulp.task('browserSync', function() {
browserSync({
server: {
baseDir: 'app'
},
})
})

Browser Sync 需要一个应用场景,对,就是编译sass。我们希望编译scss文件后刷新浏览器,所以在任务完成后我们调用 BrowserSync 的reload()方法。

1
2
3
4
5
6
7
8
9
// scss编译后的css将注入到浏览器里实现更新
gulp.task('sass', function() {
return gulp.src('app/scss/**/*.scss')
.pipe(sass())
.pipe(gulp.dest('app/css'))
.pipe(browserSync.reload({
stream: true
}))
});

要实现同步刷新就要通过 gulp watch 来调用 BrowserSync 的 reload 方法,由于我们已经在 sass 任务中调用了 reload 方法,所以只需要在 watch 之前执行 browserSync 和 sass 任务即可。

1
2
3
gulp.task('watch', ['array', 'of', 'tasks', 'to', 'complete','before', 'watch'], function (){
// ...
})

根据上述语法,更新代码。

1
2
3
4
gulp.task('watch', ['browserSync', 'sass'], function (){
gulp.watch('app/scss/**/*.scss', ['sass']);
// Other watchers
})

当在命令行中执行 $ gulp watch 以后,会发现浏览器自动打开 localhost:3000 页面,也就是起了本地服务。之前一直搞不懂怎么用 gulp 起本地服务,原来利用 gulp 起服务需要引入服务器插件,有关服务器的插件有很多个,Browser Sync 就是其中之一。

当我们修改背景色的颜色时,只要保存一下,浏览器自动就会生效。是不是很神奇?

不仅如此,它还能在不同的浏览器不同的设备上同步所有页面上的操作。假如我同时打开两个窗口,我在其中一个进行鼠标滚动时,另一个浏览器窗口也会跟着一起滚动。开始不知道这个功能的时候,我把本地地址发给设计走查,出现了页面自己动的灵异现象,活活吓了我一跳。

除了监测 scss 文件的修改,我们还可以加入 HTML 文件和 js 文件的监测,一旦有任何修改都会立即更新。

1
2
3
4
5
gulp.task('watch', ['browserSync', 'sass'], function (){
gulp.watch('app/scss/**/*.scss', ['sass']);
gulp.watch('app/*.html', browserSync.reload);
gulp.watch('app/js/**/*.js', browserSync.reload);
});

组合 Gulp 任务

上面我们是这样定义任务的

1
2
3
gulp.task('watch', ['browserSync', 'sass'], function() {
watchers
})

但是 Gulp 会同时触发[]中的事件。而通常情况下,我们希望 [] 中是按我们书写的顺序执行任务的,所以就要用到 RunSequence。

1
$ npm install run-sequence --save-dev

用法如下:

1
2
3
4
5
var runSequence = require('run-sequence');

gulp.task('task-name', function(callback) {
runSequence('task-one', 'task-two', 'task-three', callback);
});

执行 task-name 时,Gulp 会按照顺序执行 task-one, task-two, task-thre。RunSequence也允许你同时执行多个任务。

1
2
3
4
5
gulp.task('default', function (callback) {
runSequence(['sass', 'browserSync', 'watch'],
callback
)
})

如果你的任务名字叫做 default,那么只需要输入 gulp 命令即可执行。

优化CSS和JavaScript文件

通过压缩,拼接文件,减少 HTTP 请求。gulp-useref 插件可以很好地实现这个功能。

1
$ npm install gulp-useref --save-dev

假如我们有以下 js 文件需要合并

1
2
3
4
5
6
<body>
<!-- other stuff -->
<script src="js/lib/a-library.js"></script>
<script src="js/lib/another-library.js"></script>
<script src="js/main.js"></script>
</body>

合并的语法如下

1
2
3
<!-- build:<type> <path> -->
... HTML Markup, list of script / link tags.
<!-- endbuild -->

在我们这个例子里,只要将需要打包的文件外层加上两行代码,就可以输出到 js 文件夹下的 main.min.js 文件中了。

1
2
3
4
5
<!--build:js js/main.min.js -->  
<script src="js/lib/a-library.js"></script>
<script src="js/lib/another-library.js"></script>
<script src="js/main.js"></script>
<!-- endbuild -->

gulpfile.js 通过一个 useref 任务,把生成的文件保存到 dist/js/main.min.js 里。这样,在新生成的 dist/.html文件中,引用也将变成一行 <script src="js/main.min.js"></script>

1
2
3
4
5
gulp.task('useref', function(){
return gulp.src('app/*.html')
.pipe(useref())
.pipe(gulp.dest('dist'));
});

合并以后用 gulp-uglify 插件进行压缩。

安装

1
$ npm install gulp-uglify --save-dev

使用

1
2
3
4
5
6
7
8
var uglify = require('gulp-uglify');

gulp.task('useref', function(){
return gulp.src('app/*.html')
.pipe(uglify())
.pipe(useref())
.pipe(gulp.dest('dist'))
});

gulp-useref 同样可以应用在 css 上,只不过压缩插件要变为 gulp-minify-css。我们还要使用 gulp-if 来做不同处理。

1
2
3
4
5
6
7
8
9
10
11
12
var gulpIf = require('gulp-if');
var minifyCSS = require('gulp-minify-css');

gulp.task('useref', function(){
return gulp.src('app/*.html')
// Minifies only if it's a CSS file
.pipe(gulpIf('*.css', minifyCSS()))
// Uglifies only if it's a Javascript file
.pipe(gulpIf('*.js', uglify()))
.pipe(useref())
.pipe(gulp.dest('dist'))
});

总结

上面的的内容搭建了一个基本的 Gulp 工作流。另外还有一些常用的插件,同样摘自Gulp新手入门教程

开发过程:

使用 Autoprefixer,你不再需要写CSS浏览器内核前缀
增加 Sourcemaps,让你更方便的调试 Sass, coffeescript
使用 sprity 创建精灵图
gulp-changed 只允许通过修改的文件
BabelTraceur 写ES6
Browserify , webpack , jspm 模块化 JavaScript
Handlebars ,Swing 模块化 HTML
require-dir 分割 gulpfile 成多个文件
gulp-moderinizr 自动生成Modernizr脚本

优化:
unCSS 移除多余的CSS
CSSO 更深入地优化CSS
Critical 生成行内CSS

第一次录屏,觉得挺有意思的。
软件是 licecap,下载链接:http://www.cockos.com/licecap/