原文地址:https://css-tricks.com/gulp-for-beginners/
原文代码:https://github.com/zellwk/gulp-starter-csstricks
Gulp是一个在你开发web时,帮助你完成几个任务的工具。它经常用来进行一些前端任务,比如:
生成一个Web服务器
当一个文件保存时,浏览器自动刷新
编译像Sass或者LESS一样的预处理器
优化资源文件,像CSS,JavaScript和图片等
这些并不是Gulp的全部功能。如果你足够疯狂,你甚至可以使用Gulp创造一个静态页面生成器(我已经做到了!)。所以,Gulp是非常强大的,但是如果你想创建你自己的构建流程,你就要去学习如何使用Gulp。
所以这就是这篇文章所要做的。它将帮助你入门Gulp,你就可以自己探索其他任何东西。
我们要配置什么
在看完这篇文章后,你将拥有一个工作流,来进行我们文章开始所说的一些任务:
生成一个Web服务器
当一个文件保存时,浏览器自动刷新
编译像Sass或者LESS一样的预处理器
优化资源文件,像CSS,JavaScript和图片等
你也将学习如何使用容易理解和执行的命令行,将不同的任务捆绑在一起。
安装Gulp
在你安装Gulp
之前,需要安装Node.js(Node)
环境。
如果你没有安装,你可以在这个网页来获取安装包。
当你安装好Node后,你可以通过使用下列命令行来安装Gulp
(全局安装)
$sudo npm install gulp -g
提示:只有Mac用户需要
sudo
-g
标志代表这个安装时全局安装到你的电脑上,这就运行你在电脑的任何地方都能使用gulp
。
Mac用户需要额外的sudo
,因为他们需要管理员权限来全局安装Gulp
。
现在你已经安装好了Gulp
,让我们使用Gulp
来创建一个项目吧。
创建一个Gulp项目
1.我们要创建一个叫project
文件夹作为我们的根目录。
2.在目录里运行npm init
命令行来初始化项目。
npm init
命令行创建一个package.json
文件,用来保存关于项目的信息,比如一些在项目中使用的依赖(Gulp
就是一个依赖)。
npm init
将提示你:
3.一旦package.json
文件创建后,我们可以使用下面命令行,在项目中安装Gulp
$npm install gulp --save-dev
这时候,我们就将
Gulp
安装到项目里了,而不是全局安装,这就是为什么命令行有些不一样。
你将会看到在这里不需要sudo
,因为我们没有全局安装Gulp
,所以-g
也是不需要的。我们增加--save-dev,来告诉计算机增加gulp
到package.json
到dev
依赖
如果你查看项目文件夹,在命令执行结束,你应该能看到Gulp
在node_modules
文件夹里。
决定项目文件夹结构
Gulp
对于很多文件夹结构都可以足够灵活的使用。在对项目结构进行调整之前,你只需要理解内部工作原理。
对于这篇文章,我们将使用以下结构来构建项目:
|- app/
-----|- css/
-----|- fonts/
-----|- images/
-----|- index.html
-----|- js/
-----|- scss/
|- dist/
|- gulpfile.js
|- node_modules/
|- package.json
在这个结构里,我们将使用app
文件夹用于开发目的,当dist
文件夹内包括了优化后的文件,用于生产时候的页面。
自从app被用来开发目的后,我们所有的代码都要放到app
文件夹中。
我们将不得不保持目录结构当我们运行我们的Glup
配置。现在,让我们开始在gulpfile.js
中,创建我们第一个Gulp
任务。
编写你的第一个Gulp任务
第一步是在gulpfile中require
Gulp
var gulp = require('gulp');
这个require
声明告诉Node在node_modules中寻找名为gulp
的包。一旦包被找到,我们就将它里面内容赋值到变量gulp
中。
我们现在可以开始使用gulp
变量写一个gulp
任务。一个gulp
任务的基本语法是:
gulp.task('task-name',function(){
//stuff here
})
task-name
指的是任务的名称,当你在Gulp
中运行这个任务时,将会使用这个名称。你也可以在命令行中运行相同的任务,通过gulp task-name
为了测试它,让我们创建一个hello
任务,来说Hello Zell!
gulp.task('hello', function(){
console.log('Hello Zell!');
});
我们可以在命令行中通过gulp hello
来运行这个任务
$gulp hello
命令行将会返回一个日志,打印出Hello Zell!
Gulp
任务通常要比这个复杂一点。它通常包括两个附加的Gulp
时间,加上各种各样的Gulp
插件。这儿是一个真实任务的样子
gulp.task('task-name',function(){
return gulp.src('source-files')
.pipe(aGulpPlugin())
.pipe(gulp.dest('destination'))
});
正如你能看到的,一个真实的任务有两个额外的事件gulp.src
和gulp.dest
。gulp.src
告诉Gulp
任务,所要使用的文件。gulp.dest
告知当任务完成后,Gulp
输出文件的地址。
让我们来尝试构造一个真实的任务,将Sass
文件编译成CSS
文件。
Gulp预处理
在Gulp
中,我们可以将Sass
编译成CSS
,使用一个叫做
gulp-sass的插件。你可以安装gulp-sass
到你的项目中,通过使用以下命令
$ npm install gulp-sass --save-dev
在我们使用插件之前,我们需要从node_moudles
文件夹中require gulp-sass
,就像我们引入gulp
一样
var gulp = require('gulp');
// Requires the gulp-sass plugin
var sass = require('gulp-sass');
我们可以使用gulp-sass
通过将aGulpPlugin()
替换成sass()
,因为任务用来将Sass
编译成CSS
,所以让我们将他命名为'sass'
gulp.task('sass', function(){
return gulp.src('source-files')
.pipe(sass())
.pipe(gulp.dest('destination'));
});
我们想测试sass
任务是否能像我们预想的工作。我们可以在styules.scss中增加一个Sass
函数
.testing{
width:percentage(5/7);
}
如果你在命令行中运行gulp sass
,你应该就能看到app/css
中会有一个styles.css
文件被创建。此外,它代码中的percentage(5/7)
被计算出来了71.42857%
/* styles.css */
.testing{
width: 71.42857%;
}
我们知道了sass
任务是如何工作。
有时我们需要能够编译多个.scss
文件成CSS
文件。我们可以在Node globs
的帮助下完成(globs参数是文件匹配模式,类似正则表达式,用来匹配文件路径包括文件名)。
供参考:Gulp-sass使用LibSass来将Sass转换成CSS。这比基于Ruby的方法要快。如果你希望Ruby方法,你也可以使用gulp-ruby-sass或者
gulp-compass插件来代替。
Node的Globbing
Globs是匹配文件模式,允许你在gulp.src
中增加多个文件。它就像正则表达式一样,但是只用来表示文件路径。
当你使用glob
,计算机检查文件名和路径以特定的特征。如果特征存在,文件就会被匹配。
大部分Gulp
工作流倾向于只要求4个不同的匹配模式。
*.scss
:*
特征是一个通配符,用来匹配当前路径中的一些特征文件。倘若这样,我们将匹配根路径下,所有以.scss
为后缀名的文件
2.**/*.scss
:这是一个更极端版本的*
特征,匹配在根路径和一些子路径的以.scss
结尾的文件
3.!not-me.scss
:!
表明,Gulp
应该排除这个匹配的特征,当你要在匹配的文件中,排除一个文件,是非常有用的。倘若这样,not-me.scss
将被排除出匹配。
4.*.+(scss|sass)
: 加号+
和括号()
允许Gulp匹配大量的特征,不同的特征使用|
分隔开。倘若这样,Gulp
将匹配根目录下所有以.scss
或者.sass
结尾的文件。
在我们现在知道glob
之后,我们可以将app/scss/styles.scss
替换成scss/**/*.scss
,这样可以匹配app/scss
或者子路径下的.scss
文件。
gulp.task('sass', function(){
return gulp.src('app/scss/**/*.scss)
.pipe(sass())
.pipe(gulp.dest('app/css'))
})
其他在app/scss
文件夹下找到的Sass
文件,将自动被包括到sass
任务中。如果你增加一个print.scss
文件到项目中,你将看到print.css
被创建到app/css
。
我们现在可以通过一个命令,管理所有Sass
文件编译成CSS
文件。但是问题是,有什么可以让我们不用每次都手动运行gulp sass
,将Sass
编译成CSS
?
监视Sass文件更改
Gulp
提供我们一个watch
方法,监视是否有文件更改。watch
文件的语法是:
//Gulp watch syntax
gulp.watch('files-to-watch',['tasks','to','run']);
如果我们希望监视多个Sass
文件以及运行sass
任务,当一个Sass
文件被保存,我们只要将files-to-watch
替换成app/scss/**/*.scss
,将['task','to','run']
替换成[sass]
:
//Gulp watch syntax
gulp.watch('app/scss/**/*.scss',['sass']);
虽然更多时候,我们希望同时监视多种类型文件。为了实现,我们可以将多个监视进程加入到一个组里,放到一个watch
任务:
gulp.task('watch',function(){
gulp.watch('app/scss/**/*.scss',['sass']);
//other watchers
});
如果你在命令行中运行gulp watch
,你将立即看到看Gulp
的监视。
它将自动运行
sass
任务,当你保存一个.scss
文件。让我们来进行下一步,以及让Gulp重新加载浏览器,当我们保存一个
.scss
文件,通过Browser Sync。
Browser Sync的实时加载
Browser Sync使开发Web更加容易,通过创建一个Web服务器,帮助我们更容易的实时加载。它有其他的特性,比如跨多设备同步操作。
我们首先要安装Browser Sync:
$ npm install browser-sync --save-dev
你或许会注意到,当我们安装Browser Sync
时,没有gulp-
前缀。这是因为Browser Sync
与Gulp
兼容,所以我们不需要用到插件。
要使用Browser Sync
,我们要requireBrowser Sync
var browserSync = require('browser-sync').create();
我们需要创建一个browserSync
任务,让Gulp
生成一个服务器用于Browser Sync
。因此我们运行一个服务器,我们需要让Browser Sync
知道服务器的根目录。在我们的例子中,就是app
文件夹:
gulp.task('browserSync',function(){
browserSync.init({
server: {
baseDir:'app'
},
})
})
我们也需要稍微改变我们的sass
任务,让Browser Sync
能够注入新的CSS
样式(更新CSS
)到浏览器,当sass
任务运行时。
gulp.task('sass',function(){
return gulp.src('app/scss/**/*.scss')
.pipe(sass())
.pipe(gulp.dest('app/css'))
.pipe(browserSync.reload({
stream: true
}))
});
我们已经配置好了Browser Sync
。现在,我们需要同时运行watch
和browserSync
任务来进行实时加载。
这个将要笨重的打开两个命令行窗口以及独立运行gulp browserSync
和gulp watch
,所以,让我们使用Gulp
来让他们一起运行,通过告知watch
任务,browserSync
必须在watch
之前完成,watch
才能运行。
我们能做到,通过给watch
任务添加第二参数。语法如下:
gulp.task('watch', ['array', 'of', 'tasks', 'to', 'complete','before', 'watch'], function (){
// ...
})
在这种情况下,我们添加了browserSync
任务。
gulp.task('watch',['browserSync'],function(){
gulp.watch('apjp/scss/**/*.scss',['sass']);
//other watchers
}
现在,如果你在命令行中运行gulp watch
,Gulp
会同时开启sass
和browserSync
任务。当两个任务都完成后,watch
将会运行
同时,一个显示app/index.html文件的浏览器窗口也将突然弹出。如果你改变了styles.scss文件,你将会看到浏览器自动刷新效果。
在我们结束这个实时更新小节之前,还有一个件事情。既然我们已经监视了.scss
文件,并重新加载,为什么不更进一步,当HTML
文件和JavaScript
文件保存后,重新加载浏览器呢?
我们可以通过增加两个监视进程,以及当一个文件保存后,唤起browserSync.reload
函数。
gulp.task('watch',['browserSync','sass'], function(){
gulp.watch('app/scss/**/*.scss',['sass']);
//Reloads the browser whenever HTML or JS files changes
gulp.watch('app/*.html',browserSync.reload);
gulp.watch('app/js/**/*.js', browserSync.reload);
});
到目前为止,在这个教程中,我们已经处理了三件事:
1.创建一个开发用的WEB服务器
2.使用Sass编译器
3.当文件改变后,自动重新加载浏览器
让我们进入下一届,讨论优化资源文件的部分。我们将从优化CSS和JavaScript文件开始。
优化CSS和JavaScript文件
当我们尝试优化CSS和JavaScript文件给生产使用,开发者有两个任务来执行:压缩和串联。
开发者面对一个问题是,当自动化运行这个进程时,很难将你的脚本串联成正确的顺序。
比如说我们在index.html
有三个脚本标签
<body>
<script src="js/lib/a-library.js"></script>
<script src="js/lib/another-library.js"></script>
<script src="js/main.js"></script>
</body>
这些脚本在两个不同的路径。这将很难使用传统的插件(比如gulp-concatenate)来链接他们。
很感激,这儿有一个有用的Gulp插件,gulp-useref解决了这个问题。
Gulp-useref
连接一定数量的CSS
和JavaScript
文件在一个单独的文件里,通过寻找一个注释,以“”.他的语法是:
<!-- build:<type> <path> -->
<!-- endbuild -->
<type>
可以为js
,css
或者remove
。最好设置type
为你试图连接的文件的类型。如果你设置type
为remove
,Gulp
将移除整个构件块,而不生成文件。
<path>
指的是生成文件的目标地址。
我们希望最终生成的js
文件在js
文件夹,名为main.min.js
因此标记应为:
<!-- 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
中配置gulp-useref
插件。我们将安装这个插件,然后在gulpfile
中引入它。
$ npm install gulp-useref --save-dev
var useref = require('gulp-useref');
设置useref
任务和我们到目前为止设置的其他任务相似。这儿是代码:
gulp.task('useref',function(){
return gulp.src('app/*.html')
.pipe(useref())
.pipe(gulp.dest('dist'))
});
现在如果你运行这个useref
任务,Gulp
将贯穿三个脚本标签,以及连接他们到dist/js/main.min.js
但是这个文件现在并没有被压缩。我们将使用gulp-uglify插件,来压缩JavaScript文件。
我们也需要另一个插件,叫gulp-if,但是我们只能试图去压缩JavaScript文件。
$ npm install gulp-uglify --save-dev
var uglify = require('gulp-uglify');
var gulpIf = require('gulp-if');
gulp.task('useref', function(){
return gulp.src('app/*.html')
.pipe(useref())
.pipe(gulpIf('*.js',uglify()))
.pipe(gulp.dest('dist')
});
Gulp
现在应该自动压缩main.min.js
文件,当你运行useref
任务。
还有一件有关Gulp-useref
的使没有透露,就是它自动改变所有在 ""
中的标签成'js/main.min.js'
.
很棒是不是!
我们可以使用相同的方法来连接
CSS
文件(如果你打算增加多个)。我们将遵循相同的进程以及增加一个build
注释。
<!--build:css css/styles.min.css-->
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/another-stylesheet.css">
<!--endbuild-->
我们也要压缩连接后的CSS文件。我们需要使用一个叫做gulp-cssnano的插件来帮助我们压缩。
$ npm install gulp-cssnano --save-dev
var cssnano = require('gulp-cssnano');
gulp.task('useref',function(){
return gulp.src('app/*.html')
.pipe(useref())
.pipe(gulpIf('*.js',uglify()))
.pipe(gulpIf('*.css',cssnano()))
.pipe(gulp.dest('dist'))
});
现在当你运行useref
任务,你将得到一个优化后的CSS
文件以及一个优化后的JavaScript
文件。
让我们继续来优化图片。
优化图片
你可能已经猜到了;我们需要使用gulp-imagemin来帮助我们压缩图片
$ npm install gulp-imagemin --save-dev
var imagemin = require('gulp-imagemin');
通过gulp-imagemin
的帮助,我们能压缩png,jpg,gif
甚至使svg
。让我们创建一个images
任务来完成这个压缩进程。
gulp.task('images',function(){
return gulp.src('app/images/**/*.+(png|jpg|gif|svg)')
.pipe(imagemin())
.pipe(gulp.dest('dist/iamges'))
}
由于不同文件类型要使用不同的方式压缩,你或许要在imagemin
中增加选项,来自定义如何压缩文件。
比如你可以通过设置interlaced
选项为true,来创建 interlacedGIFs
gulp.task('images', function(){
return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)')
.pipe(imagemin({
// Setting interlaced to true
interlaced: true
}))
.pipe(gulp.dest('dist/images'))
});
你可以玩玩其他选项,如果你希望的话。
压缩图片,是一个极其缓慢的进程,除非必要,你是不会想重复的。为了做好这个,我们可以使用gulp-cache插件。
$ npm install gulp-cache --save-dev
var cache = require('gulp-cache');
gulp.task('images', function(){
return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)')
// Caching images that ran through imagemin
.pipe(cache(imagemin({
interlaced: true
})))
.pipe(gulp.dest('dist/images'))
});
我们几乎完成了优化进程。这儿有多个文件夹,我们需要从app文件夹编译到dist文件夹中,比如字体文件夹。让我们来做这件事。
将字体文件夹赋值到Dist文件夹
由于字体文件已经压缩了,所以我们不需要做额外的事。我们需要做的就是将字体复制到dist。
我们可以使用Gulp
复制文件通过gulp.src
和gulp.dest
,不需要其他插件。
gulp.task('fonts', function() {
return gulp.src('app/fonts/**/*')
.pipe(gulp.dest('dist/fonts'))
})
现在当你运行gulp fonts
,Gulp
将会复制字体文件到dist
中。
现在我们有六个不同的任务在gulpfile
中,以及他们每个都需要单独调用一个命令行,这是有点麻烦的,所以我们希望把所有都放到一个命令中。
在我们做那个之前,让我们来看看如何自动清理生成的文件。
自动清理生成的文件
由于我们自动生成文件,我们希望确定那些不再使用的文件不保留在我们不知道的地方
这个进程被叫做清理(或者用更简单的术语来说,删除文件)
我们将使用del帮助我们完成清理。
$ npm install del --save-dev
var del = require('del');
del
函数接受一组node globs
数组,告知那些文件夹需要删除。
在Gulp
任务中设置它就和我们第一个“hello”
的示例一样
gulp.task('clean:dist',function(){
return del.sync('dist');
})
现在当你运行gulp clean:dist
时,Gulp
将删除dist
文件夹
提示:我们不必担心删除dist/images
文件夹。因为gulp-cache
已经存储了图片的缓存在你本地系统里。
要在你本地系统中删除缓存,你要创建一个单独的任务,叫做cache:clear
gulp.task('cache:clear',function(callback){
return cache.clearAll(callback)
})
Phew, that's a mouthful(真绕口?)。现在让我们把所有任务都组合到一起吧。
组合Gulp任务
让我们总结一下我们做的吧。到目前为止,我们创建了两个不同Gulp任务集。
第一个任务集是一个开发进程,我们可以用它编译Sass到CSS,监视文件的修改,从而重新加载浏览器。
第二个任务集是压缩进程,我们为生产网站准备了所有文件。我们压缩资源文件,像CSS
,JavaScript
和图片在这个进程以及从app
文件夹复制字体文件到dist
文件夹。
我们已经将第一个任务集集合到一个简单的工作流,通过gulp watch
命令:
gulp.task('watch',['browserSync','sass'], function(){
//...watchers
})
第二个任务集包括我们需要运行来生成生产用的网页的任务。这包括了
clean:dist
,sass
,useref
,images
和fonts
。
如果我们有同样的思路,我们能创建一个build
任务来联系所有。
gulp.task('build', ['clean:dist','sass','useref','images','fonts'], function(){
console.log('building files');
})
不幸的是,我们这样构建build
任务,因为这样会导致Gulp
将第二个参数全部同时运行。
这可能会使useref
,images
或者甚至fonts
在clean
之前运行完成,这就意味着,最后整个dist
文件夹被删除。
所以要确定删除任务要在所有任务之前完成,我们需要使用一个额外的插件,叫做Run Sequence。
$ npm install run-sequence --save-dev
这是一个使用run-sequence
的任务队列的语法:
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-one
结束后,Gulp
将自动启动task-two
.最后当task-two
完成后,Gulp
将运行task-three
Run Requence
也允许你同时运行任务,如果你把他们放在一个数组里:
gulp.task('task-name',function(callback){
runSequence('task-one',['tasks','two','run','in','parallel'],'task-three',callback);
})
在这种情况下,Gulp
第一个运行task-one
.当task-one
完成后,Gulp
同时运行第二个参数里每个任务。第二个参数中所有任务运行完成后,task-three
才能运行。
所以我们现在能创建一个任务,确定clean:dist
第一个运行,然后其他任务运行:
gulp.task('build',function(callback){
runSequence('clean:dist',['sass','useref','images','fonts'],
callback
);
});
为了保持事情的一致性,我们也构建相同的队列在第一组。让我们使用default
作为任务名:
gulp.task('default',function(callback){
runSequence(['sass','browserSync','watch'],
callback
);
})
为什么用default
?因为当你有个任务叫default
,你就可以很简单的使用
gulp
命令运行,会省去键盘的敲击次数。。。
最后这里是一个Github repo,里面都是我们所做的工作。
结束
我们已经经过了Gulp
的基础以及创建了一个工作流,可以将Sass
编译成CSS
,同时监测HTML
和JS
文件发生改变。我们可以在命令行通过gulp
命令运行这个任务。
我们也构建了第二个任务,build
,创建一个dist
文件夹给生产用网页。我们编译Sass
为CSS
,压缩我们所有的资源文件以及复制必要的文件夹到dist
文件夹。我们可以在命令行中运行gulp build
来运行这个任务。
最后,我们有一个clean
任务,用来清理生成的dist
文件夹和一些创建的图片缓存,允许我们移除一些没注意的留在dist
文件夹里的旧的文件。
我们已经创建了一个强健的工作流,到目前为止,这个已经有足够能力来进行大多数WEB
开发。这儿有一些Gulp
和工作流,你可以探索来是这个进程更加完美。这里有一些资源:
For development:
- Using Autoprefixer to write vendor-free CSS code
- Adding Sourcemaps for easier debugging
- Creating Sprites with sprity
- Compiling only files that have changed with gulp-changed
- Writing ES6 with Babel or Traceur
- Modularizing Javascript files with Browserify,webpack, or jspm
- Modularizing HTML with template engines like Handlebars or Swig
- Splitting the gulpfile into smaller files with require-dir
- Generating a Modernizr script automatically with gulp-modernizr
For optimization:
- Removing unused CSS with unCSS
- Further optimizing CSS with CSSO
- Generating inline CSS for performance with Critical
转自简书治电小白