笔记来源:拉勾教育 - 大前端就业集训营
文章内容:学习过程中的笔记、感悟、和经验
Gulp - 自动化构建工具
中文官网:www.gulpjs.com.cn/
英文官网:www.gulpjs.com
常用自动化构建工具
- grunt:最早,基于临时文件,较慢
- Glup:基于内存,速度快,简单
- FIS:百度发布,百度维护,不更新了
Gulp和npm scripts
- 都能实现自动化构建
- gulp语法更简单,使用的是js语法,npm使用的语法更接近shell脚本
- gulp生态完善,构建效率更高
Gulp基础
步骤
- 全局安装gulp(
npm i gulp-cli -g),这里安装的只是gulp的命令行工具 - 初始化项目(
npm init --yes) - 项目中安装gulp包(
npm i gulp -D),这里在本地安装项目依赖 - 根目录创建
gulpfile.js文件 - gulpfile.js中,创建gulp任务,类似之前的脚本
- 执行gulp任务(
gulp 任务名)
// gulpfile.js
// 注意,新版gulp要求所有任务必须是异步的,所以不可以写同步任务,只需要加上一个回调函数即可(cb)
// 直接引入gulp
// const GulpClient = require("gulp");
// 旧版写法
// 调用task方法添加任务
// 参数1为任务名,参数2是一个异步函数
// GulpClient.task('task1', (cb) => {
// console.log('task1')
// cb()
// })
// 新版写法,直接创建常量,值为一个异步函数
const task2 = (cb) => {
console.log('task2')
cb()
}
const task3 = (cb) => {
console.log('task3')
cb()
}
// 最后把所有新创建的函数导出出来即可
module.exports = {
task2,
// default表示默认执行,调用的时候不需要写任务名,直接gulp回车即可执行task3
default: task3
}
组合任务
类似于npm scripts中的并行和串行执行的功能
任务执行
并行执行:gulp.parallel(任务,任务.....)
串行执行:gulp.series(任务,任务......)
案例:先执行任务1,之后任务二和任务三并行执行,之后执行任务4 => gulp.series(任务1,gulp.parallel(任务2,任务3),任务4)
// 导入gulp
const GulpClient = require("gulp")
// 添加四个任务
const task1 = (cb) => {
console.log('task1')
cb()
}
const task2 = (cb) => {
console.log('task2')
cb()
}
const task3 = (cb) => {
console.log('task3')
cb()
}
const task4 = (cb) => {
console.log('task4')
cb()
}
// 常规写法,注意module可以省略,直接写exports即可
// exports = {
// p: GulpClient.series(task1, GulpClient.parallel(task3, task2), task4)
// }
// exports实际上是一个对象,直接添加方法也可以,和上面的写法效果相同
exports.p = GulpClient.series(task1, GulpClient.parallel(task3, task2), task4)
// 任务2和任务3并行执行,和任务1、任务4串行执行
文件操作
gulp是基于流的构建系统,文件操作可以使用gulp的三个函数
输入-读取流-src() => 加工-转换流-pipe() => 输出-写入流-dest()
// 引入gulp
// 常规方法
// const gulp = require('gulp')
// 可以使用es6解构的方式获得两个方法,不过还是建议不要这么做,可能要用到其他方法,这样做反倒麻烦
const {
src,
dest
} = require('gulp')
// 创建任务,因为gulp的方法都是异步的,所以不需要在创建回调函数了
const go = () => {
// 常规方法调用
// return gulp.src('./src/a/index.js', {base: 'src'}).pipe(gulp.dest('./dist'))
// 使用ES6就不需要写gulp了
return src('./src/a/index.js', {base: 'src'}).pipe(dest('./dist'))
}
// 这里建议写全module.exports,因为刚刚我没写module导致报错了
module.exports = {
go
}
案例:构建样式文件
需求:和之前npm scripts脚本一样需要把less文件转换成压缩后的css文件
思路:less转css再进行压缩再更改名字后缀为min.css
我们需要在gulp官网查找所需的插件
- less转css插件:gulp-less
- css压缩插件:gulp-clean-css
- 重命名插件:gulp-rename
// 引入gulp
const gulp = require('gulp'),
// 本地安装三个插件,按照插件文档要求引入3个插件
less = require('gulp-less'),
cleanCSS = require('gulp-clean-css'),
rename = require("gulp-rename")
// 创建任务
const go = () => {
// 读取流
return gulp.src('./src/style/index.less', {
// 参考src创建文档结构
base: 'src'
})
// 之前说过,管子函数pipe可以反复调用多次
// 按照顺序依次使用插件
// 注意其实这些插件都可以配置参数,这里不需要所以没有配置参数,具体参数可以查询插件文档
// 转css
.pipe(less())
// 压缩
.pipe(cleanCSS())
// 重命名,这里使用对象的方式,只更改后缀名
.pipe(rename({
extname: ".min.css"
}))
// 写入流
.pipe(gulp.dest('./dist'))
}
// 导出
module.exports = {
go
}
Css hack和autoprefixer兼容性
css hack
css代码也存在兼容性问题
针对不同浏览器写不同的css代码,这过程叫做css hack
属性前缀法:
给我们所写的css代码加上特定的前缀名,可以在不同浏览器调用指定的属性
/* 例如:user-select属性可以控制用户是否可以选中文本(存在兼容性问题),如果使用前缀法就要写成 */
.code {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
| 前缀 | 浏览器 |
|---|---|
| -ms- | IE |
| -webkit- | Chrome、safari |
| -moz- | 火狐 |
| -o- | opera |
autoprefixer自动前缀
自动为css样式添加兼容性前缀
Autoprefixer使用caniuse.com的数据来决定哪些属性需要加前缀
// 引入gulp
const gulp = require('gulp'),
//引入自动前缀插件
gulpAutoprefixer = require('gulp-autoprefixer')
// 创建任务
const go = () => {
// 读取流
return gulp.src('./src/style/index.less', {
// 参考src创建文档结构
base: 'src'
})
// 转css
.pipe(less())
// 调用管子方法调用自动添加前缀
.pipe(gulpAutoprefixer())
// 写入流
.pipe(gulp.dest('./dist'))
}
// 导出
module.exports = {
go
}
/* 转换前,less代码 */
@cl : red;
// 我是注释
body {
width: 100px;
height: 100px;
background-color: @cl;
user-select: none;
}
/* 转换后,发现前缀自动添加了 */
body {
width: 100px;
height: 100px;
background-color: red;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
构建脚本(js)文件
使用插件
- 转ES5插件:gulp-babel,注意查看自己本地babel的版本安装对应的版本,另外文档里面的代码使用的是版本7的代码,如果是6版本不要用那个,使用
'babel-preset-env' - 压缩js代码:gulp-uglify
- 重命名:gulp-rename
构建HTML文件
使用插件:
- 压缩HTML文件:gulp-htmlmin
构建图片文件
使用插件:
- 压缩图片: gulp-imagemin - 默认无损压缩,有损压缩需要配置参数
文件清除
使用插件
- 删除文件和目录:del -
npm i dev -D - 在gulp没有发现这款插件,可能不属于gulp,后面调用也不需要写gulp前缀
Gulp服务发布 - 服务器
使用插件
- 发布web服务:browser-sync
- 官网:browsersync.io/
npm i browser-sync gulp -D - gulp可省略
注意:
- 发布的服务是默认指定目录下的index.html文件
- 文件如果需要引入js或者css文件要参考目录结构,如果输出结构和构建之前的结构相同,可以在构建之前就更改引入文件名
- 比如构建之前使用的是index.css
- 构建后css文件为index.min.css
- 那么就可以在构建之前更改css文件名为index.min.css,这样构建之后就会直接链接到构建后的index.min.css文件
组合案例:构建css+自动前缀、js、HTML、图片、文件清除、发布服务
这里展示完整的一整套css、js、html、图片构建、文件清除、发布服务
这里会演示上面所有构建服务的代码
// 引入gulp
const gulp = require('gulp'),
// 引入gulp插件
// less转css
less = require('gulp-less'),
// css自动前缀
autoprefixer = require('gulp-autoprefixer'),
// 压缩css文件
cleanCss = require('gulp-clean-css'),
// 重命名
rename = require('gulp-rename'),
// 压缩js代码
uglify = require('gulp-uglify'),
// ES6+转SES5
babel = require('gulp-babel'),
// 压缩html代码
htmlmin = require('gulp-htmlmin'),
// 压缩图片
imagemin = require('gulp-imagemin'),
// 文件清除插件
del = require('del'),
// 引入并创建服务器,.create()为创建服务器的方法
browserSync = require('browser-sync').create()
// css文件构建
const css = () => {
// 读取
return gulp.src('./src/css/index.less', {
// 以src文件结构为模版
base: 'src'
})
// 转css
.pipe(less())
// 自动前缀
.pipe(autoprefixer())
//压缩
.pipe(cleanCss())
// 重命名
.pipe(rename({
// 重命名使用对象格式,设置后缀
extname: '.min.css'
}))
// 写入
.pipe(gulp.dest('./dist'))
}
// js构建
const js = () => {
return gulp.src('./src/js/index.js', {
base: 'src'
})
// 转ES5
.pipe(babel({
// babel文档的代码是babel7的代码,如果是babel6改成'babel-preset-env'即可,也可直接去
presets: ['babel-preset-env']
}))
// 压缩
.pipe(uglify())
// 重命名
.pipe(rename({
extname: '.min.js'
}))
.pipe(gulp.dest('./dist'))
}
// html构建
const html = () => {
return gulp.src('./src/index.html', {
base: 'src'
})
// html压缩,这里设置参数
.pipe(htmlmin({
// 下面三个默认属性都是flase,所以需要改为true
// 空白折叠 - 是
collapseWhitespace: true,
// 压缩css代码 - 是
minifyCSS: true,
// 压缩js代码 - 是
minifyJS: true
}))
.pipe(gulp.dest('./dist'))
}
// 图片构建
const image = () => {
// 这里使用通配符**通配怒文件夹下面的所有文件
return gulp.src('./src/img/**', {
base: 'src'
})
// 执行压缩,这里可以配置参数,可以查看文档
.pipe(imagemin())
.pipe(gulp.dest('./dist'))
}
// 清除文件任务
const clear = () => {
// 直接调用del即可,del不属于gulp
return del(['./dist'])
}
// 发布服务
const serve = () => {
// init()为服务器初始化方法
return browserSync.init({
// 禁用每次刷新后出现在右上角提示框
notify: false,
server: {
// basedir为参考的基础目录,会自动在目录下寻找index.html文件进行发布
baseDir: "./dist"
}
});
}
// 创建串行执行任务先执行清除文件,再并行构建css、js、html、图片,最后直接发布服务
const go = gulp.series(clear, gulp.parallel(css, js, html, image), serve)
// 导出所有任务
module.exports = {
// 这里只需要把go暴露出来即可,其他的都不需要单独执行
go
}
使用bootstrap
使用插件
- bootstrap:提供常用页面效果
- jquery:bootstrap依赖
npm i jquery bootstrap@3.4.1 -S
注意:
- 如果使用这两个插件是后面上线版本中需要保留的,所以使用的时候不要使用-D而是使用-S安装插件
- 如果要使用指定版本包,可以在包后面加
@版本号安装指定版本的包,例如bootstrap@3.4.1
引入路径和发布路由
- 引入路径:这里的css和js文件都要去node_modules文件夹中去找,因为依赖安装在这里
jquery:
<script src="/node_modules/jquery/dist/jquery.min.js"></script>
bootstrap:
<script src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
- 发布路由:
// 发布服务
const serve = () => {
// init()为服务器初始化方法
return browserSync.init({
// 禁用每次刷新后出现在右上角提示框
notify: false,
server: {
// basedir为参考的基础目录,会自动在目录下寻找index.html文件进行发布
baseDir: "./dist",
// 在这里可以设置路由映射!!!!!!!
routes: {
// 把 /node_modules 映射到 node_modules 文件夹,实际上只要属性名对应上引入的链接前缀即可
'/node_modules': 'node_modules'
}
}
});
}
// 路由上只是一个映射地址,假如前面写的src地址是 /abc/.../..../.../... 映射只需要写上 '/abc': 'node_modules' 即可
browser-sync热更新 - 服务器监视文件变化
方法
-
监视src目录变化,一旦变化自动构建 -
- 使用gulp的watch()方法监视文件
- 语法:
gulp.watch('src/css/*.less'(监听文件,星号表示通配,两个星号表示监视全部文件), css(文件变化后执行任务))
-
监视dist目录变化,一旦变化重新发布 - 使用browser-sync的files监视目录
-
// 发布服务 const serve = () => { // 监视文件变化!!!!!!! gulp.watch('src/index.html', html) gulp.watch('src/css/*.less', css) gulp.watch('src/js/*.js', js) gulp.watch('src/img/**', image) // init()为服务器初始化方法 browserSync.init({ // files表示文件,这里表示一旦'./dist/**'下面的任何文件发生变化都要更新!!!!!!!! files: './dist/**', // 禁用每次刷新后出现在右上角提示框 notify: false, server: { // basedir为参考的基础目录,会自动在目录下寻找index.html文件进行发布 baseDir: "./dist", // 在这里可以设置路由映射 routes: { // 把 /node_modules 映射到 node_modules 文件夹,实际上只要属性名对应上引入的链接前缀即可 '/node_modules': 'node_modules' } } }); }
gulp和yeoman
以gulp在yeoman中的使用为例,运行过程
- 命令行执行
npm run start=> - 执行npm scripts中的脚本
start=> - 脚本内容为
gulp serve=> - 执行
gulp serve=> - 执行构建和服务器发布
根据以上分析,我们可以发现,gulp在脚手架中使用起来没有任何问题,因为脚手架中继承了gulp,我们也学习了gulp,在日后工作中我们就可以在自行定制脚手架中gulp的js代码
安装依赖报错解决方案
报错:无法加载默认插件xxxxx / Couldn't load default plugin xxxxx
解决方案:配置hosts
- 通过vscode打开hosts文件(C:\Windows\System32\Drivers\etc)
- 添加Github hosts内容(请查看课堂笔记)
- 保存(如无权限,以管理员身份新打开文件)
- 然后重新安装插件
如果上面以来问题仍然没有解决
删除所有依赖文件,命令行输入npm i会自动安装所有这个项目的依赖(package.json下面的所有依赖),可以解决大部分问题
淘宝cnpm包下载
npm install -g cnpm --registry=https://registry.npm.taobao.org
使用cnpm安装插件(更快,有一些问题可以解决,就比如我安装gulp-imagemin运行之后总是报错,使用cnpm解决了)