前端工程化之gulp
Posted 前端小菜的进阶之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端工程化之gulp相关的知识,希望对你有一定的参考价值。
前端工程化是依据业务特点,将前端开发的规范、流程、技术、工具、经验等形成规范并建立成一种标准的体系。
构建工具的主要功能就是实现自动化处理,例如对代码进行检查、预编译、合并、压缩;生成雪碧图、sourceMap、版本管理;运行单元测试、监控等,当然有的工具还提供模块化、组件化的开发流程功能。
今天我们就来熟悉一款相对简单的构建工具——gulp。
一、gulp介绍
gulp
的前身是grunt
,它们都是基于任务运行的构建工具。与grunt
相比,gulp
显得更为高效更为简单了:
更为高效:
gulp
利用Node.js
流的威力,可以快速构建项目并减少频繁的IO
操作;更为简单:通过代码优于配置的策略,
Gulp
让简单的任务简单,复杂的任务可管理;易于上手:通过最少的
API
,掌握Gulp
毫不费力,构建工作尽在掌握:如同一系列流管道。
gulp
比较简单,入门容易,适合小的工程项目的构建。
二、gulp的安装
我们使用任何软件的第一步就是安装。
第一步:gulp
是基于node
,所以我们第一步进行node
的监检测:
node -v
如果你没有安装node
,请移步到,先进行node
的安装;
第二步:正确安装node
后,全局安装gulp
,并检测是否安装成功
npm install -g gulp
....
....
gulp -v
第三步:切换到项目目录,局部安装gulp
npm init -y
...
npm install gulp -D
...
第四步:gulp
初体验
// 执行gulp命令
gulp
No gulpfile found
...
然后我们在项目根目录创建一个配置文件:gulpfile.js
// 执行gulp命令
var gulp = require('gulp');
gulp.task('default', function() {
// 将你的默认的任务代码放在这
console.log('我是默认任务')
});
至此,项目目录结构如下:
├── project_dir(项目根目录)
├── gulpfile.js(gulp配置文件)
├── node_modules
│ └── gulp
└── package.json
gulp
的安装就结束了,这里为什么要全局和局部分别安装一次gulp
呢?有人说是为了版本灵活,其实这不是主要的原因:
全局安装:作为命令行工具,作为启动器,执行gulpfile.js中的任务
本地安装:作为
gulp
的核心库,提供各种功能api,在gulpfile.js
中使用
三、gulp的api
gulp
简单易学,入门容易的一个原因得益于它的api够简单。满打满算它的api就那么十一二个,常用的api也就4个:
gulp.task():
创建任务;gulp.src():
根据参数选择要处理的文件,并返回一个可以被pipe接收的虚拟文件对象流;gulp.dest():
能够接收pipe传出的流,并把流中的内容写入到文件中;gulp.watch():
监测文件,文件修改后,执行相关的任务操作;
在前面提到,gulp
比grunt
更高效,其中它们一个重要的因素在任务执行的时候的媒介不同:grunt
是以文件为媒介进行任务的,它每进行一步就会把结果存入到一个临时文件中,来作为下一步的输入;gulp
是以流为媒介进行额任务,它匹配到要处理的文件之后,生成相应的虚拟文件对象流,这个"流"流入到下一步任务,处理完之后继续生成相应的"流",直到任务结束,然后再输出到文件。
1、gulp.src():输入文件
用来匹配要处理的文件,并根据参数生成相应的流,其语法如下:
gulp.src(globs[, options])
globs
:用来配文要处理的文件路径,包含文件名,采用的是node-glob语法,或者直接输入文件路径,语法有些类似正则,又有别于正则。可以是string或arrayoptions
:生成文件流时的可选配置,通常我们不需要关注
下面我们来看几个例子:
// 匹配src/js文件夹下的所有直接包含的js
gulp.src('src/js/*.js')
// 匹配src/js文件夹下的所有js,包含js后代目录中的js文件
gulp.src('src/js/**/*.js')
/**
*匹配src/js文件夹下的所有js,包含js后代目录中的js文件,
*以及lib/js下的直接包含的js
*/
gulp.src(['src/js/**/*.js','lib/js/*.js'])
更多相关信息,请阅读gulp官网api。
2、gulp.dest():输出文件
gulp.dest()
方法是用来写文件的,其语法为:
gulp.dest(path[,options])
path
:文件将被写入的路径(输出目录)。也可以传入一个函数,在函数中返回相应路径;options
:可选输出配置参数,通常不需要关注;
这里要注意:path
是路径,不包含文件名哦,生成的最终文件名,由gulp.src匹配到的文件决定,或者通过gulp-rename
修改
例如:
var gulp = require('gulp');
gulp.src('lib/js/jquery.js')
.pipe(gulp.dest('dist/foo.js'));
//最终生成的文件路径为 dist/foo.js/js/jquery.js,而不是dist/foo.js
3、gulp.task()
我们说gulp是任务型的工程化工具,那么如何创建任务呢?
语法如下:
gulp.task(name[, deps], fn)
name
:当前要定义的任务名称deps
:可选参数,当前任务所依赖的其他任务列表fn
:当前任务要执行的操作
在我们编译一个项目的过程中,我们可能创建了多个任务:build
、build_js
、build_css
、build_lib
等,它们直接可能存在依赖,也能不存在依赖。
当build
依赖其他任务时:
//定义一个有依赖的任务
gulp.task('build',['build_js','build_css','build_lib'],function(){
// build任务内容
});
当我们所依赖的任务是个异步任务时,我们需要异步操作完成才进行下一步,该怎么操作呢?有三种方式:
回调的方式:给被依赖任务传入回调函数
cb
,异步操作完成后,调用cb
//被依赖任务:cb为任务函数提供的回调,用来通知任务已经
gulp.task('one',function(cb){ 完成
//one是一个异步执行的任务
setTimeout(function(){
console.log('one is done');
cb(); //执行回调,表示这个异步任务已经完成
},5000);
});
//这时two任务会在one任务中的异步操作完成后再执行
gulp.task('two',['one'],function(){
console.log('two is done');
});
返回一个流:
gulp.task('somename', function() {
var stream = gulp.src('client/**/*.js')
.pipe(minify())
.pipe(gulp.dest('build'));
return stream;
});
返回一个promise
var Q = require('q');
gulp.task('somename', function() {
var deferred = Q.defer();
// 执行异步的操作
setTimeout(function() {
deferred.resolve();
}, 1);
return deferred.promise;
});
4、gulp.watch()
用来监视文件的变化,当文件发生变化后,我们可以利用它来执行相应的任务,例如文件压缩等。其语法为:
gulp.watch(glob[, opts], tasks)
glob
:匹配待监测的文件,同gulp.src
参数opts
:可选的配置参数tasks
:任务列表,当文件变化时,需要执行的任务
四、常用插件
终于到了插件部分,插件才是王道,去gulp的官网扫了一眼,它的插件多达3772个,能满足你各种姿势各种需求。好了,我们先来上一个清单(package.json
):
{
"name": "16",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.1.6",
"@babel/preset-env": "^7.1.6",
"gulp": "^3.9.1",
"gulp-babel": "^8.0.0",
"gulp-concat": "^2.6.1",
"gulp-cssmin": "^0.2.0",
"gulp-imagemin": "^5.0.3",
"gulp-less": "^4.0.1",
"gulp-livereload": "^4.0.0",
"gulp-rename": "^1.4.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-uglify": "^3.0.1"
}
}
看到了吗,一部分金黄色的字,那就是我们常用的插件,它们清一色的以"gulp-
"开头,并且各司其职。
1、JS压缩插件:gulp-uglify
安装:
npm install gulp-uglify -D
使用:
const gulp = require('gulp');
const uglify = require('gulp-uglify'); //js压缩
const rename = require('gulp-rename'); // 重命名
// JS压缩重命名
gulp.task('js/*.js',() => {
return gulp.src(js_path)
.pipe(uglify())
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./build/'))
});
压缩js目录下的js文件,并保存到build
目录下。这里使用了rename
插件,重命名了文件名(给压缩后的文件加了后缀.min
),更多rename
的使用请查阅官网
没使用es6语法?不可能的,所以当说使用了es6
,gulp-uglify
就显得不够用了,就要需要babel
转换了。
2、ES6语法转换:gulp-babel
安装:
npm i gulp-babel @babel/core @babel/preset-env -D
使用:
// 编译es6
gulp.task('es6/*.js',() => {
return gulp.src(js_path)
.pipe(babel({
presets:['@babel/env']
}))
.pipe(uglify())
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./build/es6'))
});
插件gulp-uglify
和gulp-babel
满足了我们对js的压缩,可是在开发的时候,如果js
报错了就麻烦了,因为压缩后的代码谁也不认识,变得不可追溯了。怎么办,用gulp-sourcemaps
。
3、资源图:gulp-sourcemaps
安装:
npm i gulp-sourcemaps -D
使用:
// 编译es6并记录map可调式追溯
// 注意sourcemap的位置
gulp.task('es6map',() => {
return gulp
.src('es6/*.js')
.pipe(sourcemaps.init())
.pipe(babel({
presets:['@babel/env']
}))
.pipe(uglify())
.pipe(rename({suffix: '.min'}))
.pipe(sourcemaps.write())
.pipe(gulp.dest('./build/es6map'))
});
有兴趣的话,可以去看看生成的sourcemap
位置,看看如何生成独立的sourcemap
文件。
4、处理图片文件:gulp-imagemin
适用范围:只能处理三种文件jpg
/
png
/
gif
安装:
npm i gulp-imagemin -D
使用:
// 处理图片
gulp.task('image',()=>{
return gulp
.src(['./src/img/**/*.jpg', './src/img/**/*.png', './src/img/**/*.gif'])
.pipe(imgmin([
imgmin.gifsicle({interlacd:true}),
imgmin.jpegtran({progressive:true}),
imgmin.optipng({optimizationLevel:5})
]))
.pipe(gulp.dest('./build/img'))
})
我这里src写的比较繁琐,大家可以尝试着用node-glob写法
5、压缩css:gulp-cssmin
安装:
npm i gulp-cssmin -D
使用:
// 处理css
gulp.task('style',() => {
return gulp
.src(['./src/css/**/*.css'])
.pipe(concat('style.min.css'))
.pipe(cssmin())
.pipe(gulp.dest('./build/css'))
})
6、处理less:gulp-less
安装:
npm i gulp-less -D
使用:
gulp.task('less', ()=>{
return gulp
.src(['./src/less/**/*.less'])
.pipe(sourcemaps.init())
.pipe(concat('main.less.min.css'))
.pipe(less())
.pipe(cssmin())
.pipe(sourcemaps.write())
.pipe(gulp.dest('./build/css/'));
});
sass
的处理与less
类似,只是插件不同而已,有需要可以去看看gulp-sass
的使用。
你会发现在处理less的时候,使用了sourcemap
。其实像这类插件gulp-rename
、gulp-concat
、gulp-sourcemaps
,在很多地方都可以使用,它们跟语言没有多大关系。
7、html文件压缩gulp-minify-html
安装:
npm i gulp-minify-html -D
使用:
gulp.task('minify-html',()=> {
return gulp.src('src/page/*.html')
.pipe(minifyHtml()) //压缩
.pipe(gulp.dest('build/page'));
});
当然gulp-minify-html
有很多配置参数:
const htmlConfig = {
removeComments: true, //清除HTML注释
collapseWhitespace: true, //压缩HTML
collapseBooleanAttributes: true, //省略布尔属性的值 <input checked="true"/> ==> <input />
removeEmptyAttributes: true, //删除所有空格作属性值 <input id="" /> ==> <input />
removeScriptTypeAttributes: true, //删除<script>的type="text/javascript"
removeStyleLinkTypeAttributes: true, //删除<style>和<link>的type="text/css"
minifyJS: false, //压缩页面JS
minifyCSS: false //压缩页面CSS
};
以上是我以前使用过的,其他的大家去官网查阅
最后我们来上一个高大上的插件:自动刷新
8、自动刷新:gulp-livereload
这个插件是用来自动刷新的,代码修改保存后,浏览器自动刷新。当然该插件最好配合谷歌浏览器使用,需要安装一个浏览器端的插件:livereload_2_1_0_0.crx
。
安装:
npm i gulp-livereload -D
使用:
const js_path=['./src/js/**/*.js'];
gulp.task('js', ()=>{
return gulp
.src(js_path)
.pipe(babel({
presets: ['@babel/env']
}))
.pipe(concat('bundle.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./build/js'))
.pipe(livereload());
});
gulp.task('watch', ()=>{
//开启livereload的服务
livereload.listen();
//重新编译JS
gulp.watch(js_path, ['js']);
//监听html;标记文件修改
gulp.watch([
'./1.html',
...js_path
], file=>{
livereload.changed(file.path);
});
});
gulp.task('default', ['js', 'watch']);
你会发现default
任务里面先执行了一遍js
任务,是因为watch
任务只有在文件变化的时候,它才会执行。
别忘记了在浏览器里面安装插件,并开启相应功能哦。
到此为止,我们也就生成了开始时候的清单了。
五、兼容性
当你在执行gulp
默认任务的时候:
gulp
有可能你会发现,没法执行,还报了错,别急,这是版本之间的兼容性导致的。
进入官方文档查看,官方的意思是,4.0.0版本的,“default
”右边两个参数放在gulp.series()
的参数中,否则会报错。所以自己去改改吧
以上是我几次gulp使用后的一些总结,有不对的地方,请斧正,欢迎关注交流。
以上是关于前端工程化之gulp的主要内容,如果未能解决你的问题,请参考以下文章