自动化构建工作流以及Gulp Grunt FIS相关工具
自动化构建工作流以及Gulp Grunt FIS相关工具
自动化构建
- 自动化 即为机器代替人工
- 构建 转换
自动化构建就是把开发写出来的源代码 自动化构建为生产代码也就是浏览器可以执行的代码。
这个过程一般叫做自动化构建工作流用来帮助我们脱离运行环境兼容带来的问题。
使用提高效率的语法,规范和标准。
我们就可以使用ECMAScript scss 以及模板引擎这些用法在浏览器中没有办法直接被支持,我们通过自动化构建帮助我们把高效的这些用法转为浏览器可执行的代码。
自动化构建初体验
- 创建一个带有package.json的文件
- 创建一个scss文件夹带有main.scss文件夹,文件内带有以scss形式书写的样式
- 安装sass yarn add sass --dev环境安装
- 在文件加下生成了node - modules文件夹 文件夹下有sass文件
- 命令行输入.\node_modules.bin\sass 找到sass文件
- 需要再命令行给出一个scss的输入路径和css输出路径.\node_modules.bin\sass scss / main.scss css / style / css
- 生成了css文件夹 并且生成了css.map
这样需要重复输入这些命令
NPM Script
-
NPM Script 定义与开发过程有关的脚本命令 可以让命令跟着项目一期运行。
-
在package.json 中新增script字段 字段可以接收一个对象用来写脚本命令。
键就是名称 值就是对应执行的命令
- package.json中写入
"scripts": {
"build":"sass scss/main.scss css/style/css"
},
- yarn build yarn可以省略run
同样也可以生成css文件
NPM Script也是实现自动化构建工作流最近的方式
- 给项目安装browser-sync模块 用来启动测试服务器运行项目 yarn add browser-sync --dev
- 在scripts中新增命令serve用来运行browser-sync
"scripts": {
"build":"sass scss/main.scss css/style/css",
"serve":"browser-sync ."
},
- 运行yarn serve命令
- 会自动启动服务器并唤醒当前浏览器打开网页
如果启动serve命令之前没有样式文件,browser-sync工作时候就没有样式文件,那么需要再启动serve命令之前启动build - 此时我们可以利用serve的钩子机制 在serve之前写入一个preserve,这样就可以在serve执行的时候有css文件了
"scripts": {
"build": "sass scss/main.scss css/style/css",
"preserve":"yarn build",
"serve":"browser-sync ."
},
- 此时运行yarn serve会先执行yarn build
yarn run v1.22.10
$ yarn build
$ sass scss/main.scss css/style.css
$ browser-sync .
- 还可以在 build后面加 --watch来监听 文件的改变,一旦文件改变代码会自动进行编译
- 加–watch 之后browser-sync不会执行会等待文件的变化,我们需要多个命令一起执行
9.可以用npm-run-all这个模块来解决 yarn add npm-run-all --dev
10 scripts中加入 “start”: “run-p build serve” 使用run-p命令同事执行build和serve
11 修改scss文件 会自动构建成css
12 还可以给browser-sync 后面添加一些文件 这样可以监听文件的变化如果文件发生变化 browser-sync会自动同步到浏览器。
常用的自动化构架工具
-
npm script确实是很实用的工具但是面对复杂的执行过程的命令就不太适用了
-
目前流行的自动化构建工作流工具是 grunt gulp first webwack是模块打包工具
-
grunt 是最早的很多常用插件 可以帮助完成任何你想做的事情。
是基于临时文件去构建的 grunt每一步都有磁盘的读写操作 会把编译结果写入临时文件, 需要去读临时文件进行下一步
读写文件的次数越多 处理环节越多 文件很多的情况下构建速度会非常慢。 -
gulp 解决了构建速度慢的问题 是基于内存构建的 相对于磁盘读写速度快很多。
默认同时执行多个任务 书写易懂 插件生态完善 目前最流行的构建系统。 -
fis 是百度团队研发的 前面两个系统是微内核的 fis更像是捆绑套餐 把项目中典型的需求尽可能继承在内部了。
初学fis更适合 灵活多变 gulp 和grunt更适合
Grunt的基本使用
一 grunt的基本使用步骤
- yarn init --yes
- 添加grunt模块 yarn add grunt
- 根目录下添加gruntfile文件 code gruntfile.js
- 需要导出一个模块 接收一个形参grunt
- grunt.registerTask 这个方法注册任务运行 yarn grunt 函数名 执行函数
- grunt.registerTask的参数第一个是函数名 第二个是函数描述可以使用yarn grunt --help来获取描述信息 第三个参数是回调函数
Available tasks
foo Custom task.
bar 任务描述
- 如果在注册任务的名称为default 这个任务会成为grunt的默认任务 运行的时候不需要写入任务名会默认执行默认任务
- 如果注册default为grunt.registerTask(‘default’, [‘foo’,‘bar’]) 执行yarn grunt 会生成一个有foo 和bar 的串行任务
Running "foo" task
hello world
Running "bar" task
other
- grunt对异步任务的支持 setTimeout 模拟异步操作
grunt.registerTask('async-task', () => {
setTimeout(() => {
console.log('async tesk working');
}, 1000)
})
-
执行 yarn grunt async-task 发现打印语句没有直接执行 这是grunt的特点
-
grunt默认支持同步 如果想使用异步需要再在回调结束后运行一下this.async()
grunt.registerTask('async-task', function () {
// 想使用this内部就不能使用箭头函数了改为function函数
const done = this.async()
setTimeout(() => {
console.log('async tesk working');
// 异步执行完打印语句调用done方法
done()
}, 1000)
})
- 执行 yarn grunt async-task 后控制台输出 grunt知道是异步任务 会等待done的执行 执行后结束任务
Running “async-task” task
async tesk working
二 grunt标记任务失败
-
普通任务 return false来标记失败
如果在任务中return false 终端会报任务失败 如果在任务执行列表中会影响其他任务下面的任务不会执行
Running “foo” task
foo task work
Warning: Task “foo” failed. Use --force to continue.
使用 yarn grunt --force可以强制执行所有任务 -
异步任务 done给false参数来标记失败
Running “async-task” task
async tesk working
Warning: Task “async-task” failed. Use --force to continue.
Aborted due to warnings.
三 grunt的配置选项方法
grunt.initConfig
- 使用innitConfig来配置属性
grunt.initConfig({
foo: ‘bar’ // 键与任务名称保持一致 值可以是任意类型
})
2. 注册任务
grunt.registerTask('foo', () => {
// 根据grunt.config()方法传入键来获取配置
console.log(grunt.config('foo') //Running "foo" task// bar
);
})
- 如果initconfig配置的值是对象 grunt支持
grunt.initConfig({
foo: {
key:123
} 键与任务名称保持一致 值可以是任意类型
})
可以直接用foo.key来获取
grunt.registerTask('foo', () => {
// 根据grunt.config()方法传入键来获取配置
console.log(grunt.config('foo.key') //Running "foo" task// bar
);
})
四 grunt多目标任务
- 通过initConfig中的build来配置多目标或者说子任务
- 使用grunt.registerMultiTask注册任务 可以用this.target 拿到目标名称 使用this.data拿到目标的值
module.exports = grunt => {
// 使用innitConfig来配置属性
grunt.initConfig({
build: {
options: {
foo: 'bar'
},
css: {
// 也可以在目标中进行配置 options会覆盖外面的options的值
options: {
foo: 'abc'
},
key: '1'
},
js: '2'
}
// build 配置对象形式 每个键都是一个子任务或者说是目标 除了options,options是为了任务配置的选项
})
// 注册任务
grunt.registerMultiTask('build', function () {
// 根据grunt.config()方法传入键来获取配置
console.log(`target:${this.target}, data: ${this.data}`);
console.log(this.options()); //options是个方法 拿到配置选项
// Running "build:css"(build) task
// target: css, data: [object Object] {
// foo: 'abc'
// }
// Running "build:js"(build) task
// target: js, data: 2 {
// foo: 'bar'
// }
})
}
五 grunt插件的使用
- 安装插件 yarn add grunt-contrib-clean // 插件作用是清除grunt执行过程中的临时文件
gruntfile载入插件 - grunt.loadNpmTasks方法加载插件中提供的任务
grunt.loadNpmTasks(‘grunt-contrib-clean’) - 运行 yarn grunt clean报错 原因是clean是多目标任务 需要使用initConfig来配置目标
arning: Task "clean" not found. Use --force to continue.
Aborted due to warnings.
error Command failed with exit code 3.
4.写入initConfig
grunt.initConfig({
clean: {
temp: 'temp/app.js' // 键为目标 值为对应用路径
}
})
- 运行 yarn grunt clean 查看配置路径下的文件是否被删除
Running "clean:temp" (clean) task
>> 1 path cleaned.
- 通配符
6.1 匹配文件类型
grunt.initConfig({
clean: {
// temp: 'temp/app.js', // 键为目标 值为对应用路径
temp: 'temp/*.txt', //除了写具体路径还可以写文件通配符来删除文件
}
})
Running "clean:temp" (clean) task
>> 2 paths cleaned.
6.2 匹配文件夹下所有子文件
temp: 'temp/**',
Running "clean:temp" (clean) task
>> 2 paths cleaned.
六 grunt常用插件
grunt-scss 是npm的scss 通过npm模块依赖sass 使用起来不需要对机器有环境要求
- 安装依赖 yarn add grunt-sass sass --dev
- require引入sass
- 在initConfig中配置sass任务
- 任务中有main 指定输入目录和输出目录的 main是个对象 对象的键为files 值为一个对象对象的键为输出 值为输入
- 执行yarn grunt sass 会报错 报没有设置implementation
- 在main上新增options来配置 implementation ,implementation指的就是使用什么编译sass 指向值为我们安装的sass模块即可
ES6的语法编译器 babel
- 安装 yarn add grunt-babel @bebel/core @bebel/preset-env --dev
插件引用越来越多我们也会写入很多loadNpmTasks ,可以用load - grunt - task来解决这个问题
2. 安装 yarn add load-grunt-task --dev 并引入
3. 在initConfig中写入babel,main中写入输入和输出路径 options中写入配置 presets 的值为 preset-env
4. 运行 yarn grunt babel 生成编译后的js文件
- 安装 grunt-contrib-watch 监听插件
- 写配置
watch: {
js: {
files: ['src/js/*.js'],
tasks: ['babel']
},
css: {
files: ['src/scss/*.scss'],
tasks: ['sass']
},
}
- 运行 yarn grunt watch 运行后会监听 但是不会编译了
- 优化 先编译一次之后再监听
grunt.registerTask('default', ['sass', 'babel', 'watch'])runt.registerTask('default', ['sass', 'babel', 'watch'])
完整的gruntfile.js
1. grunt的基本使用
Grunt的入口文件
用于定义一些需要Grun自动执行的任务
需要导出一个函数, 此函数接收一个grunt形参 ,内部提供一些创建任务时可以用到的API
module.exports = grunt => {
grunt.registerTask('foo', () => {
console.log(
'hello world'
);
})
grunt.registerTask('bar', '任务描述', () => {
console.log(
'other'
);
})
grunt.registerTask('default', ['foo', 'bar'])
grunt.registerTask('async-task', () => {
setTimeout(() => {
console.log('async tesk working');
}, 1000)
})
grunt.registerTask('async-task', function () {
// 想使用this内部就不能使用箭头函数了改为function函数
const done = this.async()
setTimeout(() => {
console.log('async tesk working');
// 异步执行完打印语句调用done方法
done()
}, 1000)
})
}
2. grunt 标记失败任务
module.exports = grunt => {
grunt.registerTask('foo', () => {
console.log(
'foo task work'
);
return false
})
grunt.registerTask('bad', '任务描述', () => {
console.log(
'bad task work'
)
})
grunt.registerTask('bar', () => {
console.log(
'bar task work'
)
})
grunt.registerTask('default', ['foo', 'bad', 'bar'])
grunt.registerTask('async-task', function () {
// 想使用this内部就不能使用箭头函数了改为function函数
const done = this.async()
setTimeout(() => {
console.log('async tesk working');
// 异步执行完打印语句调用done方法
done(false)
}, 1000)
})
}
3. 配置选项方法
module.exports = grunt => {
// 使用innitConfig来配置属性
grunt.initConfig({
foo: 'bar' // 键与任务名称保持一致 值可以是任意类型
})
// 注册任务
grunt.registerTask('foo', () => {
// 根据grunt.config()方法传入键来获取配置
console.log(grunt.config('foo') //Running "foo" task// bar
);
})
// 使用innitConfig来配置属性
grunt.initConfig({
foo: {
key: 123
} // 键与任务名称保持一致 值可以是任意类型
})
// 注册任务
grunt.registerTask('foo', () => {
// 根据grunt.config()方法传入键来获取配置
console.log(grunt.config('foo.key') // Running "foo" task 123
);
})
}
4. 多目标任务
module.exports = grunt => {
// 使用innitConfig来配置属性
grunt.initConfig({
build: {
options: {
foo: 'bar'
},
css: {
// 也可以在目标中进行配置 options会覆盖外面的options的值
options: {
foo: 'abc'
},
key: '1'
},
js: '2'
}
// build 配置对象形式 每个键都是一个子任务或者说是目标 除了options,options是为了任务配置的选项
})
// 注册任务
grunt.registerMultiTask('build', function () {
// 根据grunt.config()方法传入键来获取配置
console.log(`target:${this.target}, data: ${this.data}`);
console.log(this.options()); //options是个方法 拿到配置选项
})
}
5. grunt插件
module.exports = grunt => {
grunt.initConfig({
clean: {
// temp: 'temp/app.js', // 键为目标 值为对应用路径
// temp: 'temp/*.txt', //除了写具体路径还可以写文件通配符来删除文件
temp: 'temp/**', // 删除temp文件夹以及文件夹下所有的子文件
}
})
grunt.loadNpmTasks('grunt-contrib-clean') // 加载插件中提供的任务
}
常用插件
5.1 grunt - sass
5.2 babel
5.3
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')
module.exports = grunt => {
// grunt-sass是多目标任务
grunt.initConfig({
sass: {
options: {
sourceMap: true, //编译过程中生成对应的sourceMap文件
implementation: sass // 没有加报错 指定使用什么来编译 实现的值就是安装的sass模块
},
// main: 用files来执行输入和输出的文件夹路径 键为输出 值为输入
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
},
babel: {
options: {
sourceMap: true, //编译过程中生成对应的sourceMap文件
presets: ['@babel/preset-env'] // 没有加报错 指定使用什么来编译 实现的值就是preset-env模块
},
// main: 用files来执行输入和输出的文件夹路径 键为输出 值为输入
main: {
files: {
'dist/js/app.js': 'src/js/app.js'
}
}
},
watch: {
js: {
files: ['src/js/*.js'],
tasks: ['babel']
},
css: {
files: ['src/scss/*.scss'],
tasks: ['sass']
},
}
})
loadGruntTasks(grunt) //自动加载所有的grunt插件中的任务
// grunt.loadNpmTasks('grunt-sass') // 加载插件中提供的任务
// 优化 先编译一次之后再监听
grunt.registerTask('default', ['sass', 'babel', 'watch'])
}
Gulp使用
- gulp的入口文件 node环境 遵守common.js
- 导出模式是以exports.任务名 = () => { } 导出
一 Gulp基本使用
-
安装 gulp yarn add gulp --dev
-
创建一个基本任务foo
exports.foo = () => {
console.log(‘foo working’);
}
此时运行报错
[18:24:12] The following tasks did not complete: foo
[18:24:12] Did you forget to signal async completion?
error Command failed with exit code 1.
原因: 在新版的gulp中默认是异步操作 任务执行过后要调用回调函数去标记任务执行完毕
3. foo 接收参数可以标识代码任务结束
exports.foo = done => {
console.log(‘foo working’);
done()
}
此时再运行不报错了 有starting 有finished
- 创建default任务 执行yarn gulp会默认去执行default任务
exports.default = done => {
console.log(‘default working’);
done()
} - require的方式使用gulp
const gulp = require(‘gulp’)
gulp.task(‘bar’, done => {
console.log(‘bar working’);
done()
}
)
这种方式仍然可以执行但是不推荐了 推荐导出函数成员的方式。
二 Gulp创建组合任务
可以通过 和parallelAPI组合gulp任务
- 引入API
const {
series,
parallel
} = require('gulp')
// const task1 = done => {
setTimeout(() => {
console.log('task1 working');
done()
}, 1000)
}
const task2 = done => {
setTimeout(() => {
console.log('task2 working');
done()
}, 1000)
}
const task3 = done => {
setTimeout(() => {
console.log('task3 working');
done()
}, 1000)
}
exports.foo = series(task1, task2, task3) // 创建一个串行任务 使用series创建 接收任意个数参数每个参数都是一个任务。 series按照顺序依次执行
[18:49:43] Using gulpfile ~\Desktop\gulp-sample\gulpfile.js
[18:49:43] Starting 'foo'...
[18:49:43] Starting 'task1'...
task1 working
[18:49:44] Starting 'task2'...
task2 working
[18:49:45] Finished 'task2' after 1.02 s
[18:49:45] Starting 'task3'...
task3 working
[18:49:46] Finished 'task3' after 1.02 s
[18:49:46] Finished 'foo' after 3.11 s
Done in 7.71s.
exports.bar = parallel(task1, task2, task3) // 创建并行任务 使用parallel创建 接收任意个数参数每个参数都是一个任务 所有任务同时执行
[18:54:05] Using gulpfile ~\Desktop\gulp-sample\gulpfile.js
[18:54:05] Starting 'bar'...
[18:54:05] Starting 'task1'...
[18:54:05] Starting 'task2'...
[18:54:05] Starting 'task3'...
task1 working
[18:54:06] Finished 'task1' after 1.03 s
task2 working
[18:54:06] Finished 'task2' after 1.05 s
task3 working
[18:54:06] Finished 'task3' after 1.04 s
[18:54:06] Finished 'bar' after 1.06 s
Done in 4.16s.
三 Gulp异步任务的三种方式
调用异步函数时没办法明确异步调用是否完成
函数内部通过回调和事件来通知外部函数完成
通知gulp完成情况的三种办法
- 回调函数 和node的错误函数一致 都是错误优先的 一旦发生错误后面代码不会执行
exports.callback = done => {
console.log('callback working');
done()
}
- done传new Error 构造函数当参数
exports.callback_error = done => {
console.log('callback_error working');
done(new Error('callback_error task wrong'))
}
- promise
exports.promise = () => {
console.log('promise working');
return Promise.resolve()
}
exports.promise_error = () => {
console.log('promise_error working');
return Promise.reject(new Error('promise_error error'))
}
const timeout = time => {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
- async await
exports.async = async () => {
await timeout(1000)
console.log('async working');
}
- stream依靠读写都有end事件来告诉我们异步执行结束
const fs = require('fs')
exports.stream = () => {
const readStream = fs.createReadStream('package.json')
const writeStream = fs.createWriteStream('temp.txt')
readStream.pipe(writeStream)
return readStream
}
exports.stream = done => {
const readStream = fs.createReadStream('package.json')
const writeStream = fs.createWriteStream('temp.txt')
readStream.pipe(writeStream)
readStream.on('end', () => {
done()
})
}
四 gulp 构建过程核心工作原理
读文件和转换和写入另一个位置
- 读写复制的任务
const fs = require('fs')
// 创建一个默认任务
exports.default = () => {
// 文件读取流
const read = fs.createReadStream('normalize.css')
// 文件写入流
const write = fs.createWriteStream('normalize.min.css')
// 通过pipe进行写入读取的文件
read.pipe(write)
return read // 根据end判断任务结束
此时文件只是从一个地方复制到了另一个地方并没有转换
}
- 文件转换流
const fs = require('fs')
2.1 引入 Transform
const {
Transform
} = require('stream')
// 创建一个默认任务
exports.default = () => {
// 文件读取流
const read = fs.createReadStream('normalize.css')
// 文件写入流
const write = fs.createWriteStream('normalize.min.css')
// 2.2 文件转换流
const transform = new Transform({
transform: (chunk, encoding, callback) => {
// 转换核心过程
// chunk 是读取流中读取到的内容
// 2.3拿到读取内容
const input = chunk.toString()
// 2.4 转换
const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')
callback(null, output)
}
})
read
.pipe(transform) // 先把文件pipe到转换流
.pipe(write) // 再把转换好的文件写入
return read // 根据end判断任务结束
}
成功转换了文件
gulp的介绍就是一个基于文件流的方式gulp希望以构建管道的方式进行构建
五 Gulp文件操作API和插件的使用
-
文件的读取流gulp有提供API相对比于node更好用 转换是使用的插件
-
安装转换的插件 gulp-clean-css 用来转换css文件的
-
安装转换的插件 gulp-rename 用来转换css文件名称的
const {
src,
dest
} = require('gulp')
const cleanCss = require('gulp-clean-css')
const rename = require('gulp-rename')
exports.default = () => {
// src的参数可以采用通配符
// return src('src/normalize.css').pipe(dest('dist')) // 读取和写入正常
return src('src/*.css')
.pipe(cleanCss())
.pipe(rename({
extname: '.min.css'
}))
.pipe(dest('dist')) // 读取和写入正常
}
完整gulpfile.js 写入过程
gulp入口文件
一 样式的编译
1. 引入gulpAPI
const {
src,
dest,
parallel,
series
} = require('gulp')
// const sass = require('gulp-sass')
const del = require('del')
const browserSync = require('browser-sync')
const bc = browserSync.create() // 启动测试服务器的方法
const loadPlugins = require('gulp-load-plugins')
const {
watch
} = require('browser-sync')
const plugins = loadPlugins()
2. 使用 src 和 dest正常读写
const style = () => {
return src('src/assets/styles/*.scss')
.pipe(dest('dist'))
}
这样运行后确实生成了dist目录但是丢失了scss之前的目录不是我们想要的
3. 解决生成dist目录丢失内部目录层级问题
src 的参数中设置第二个参数 键是base 就是基准目录 值就是最相对最外层目录
const style = () => {
return src('src/assets/styles/*.scss', {
base: 'src'
})
.pipe(dest('dist'))
}
此时就拷贝正常了
4. css转换需要插件 安装插件 gulp-sass 并引入
const style = () => {
return src('src/assets/styles/*.scss', {
base: 'src'
})
.pipe(plugins.sass({
outputStyle: 'expanded'
}))
.pipe(dest('dist'))
}
重新运行命令 yarn gulp style 下划线文件名的css文件不转换默认的
此时转换的css文件中结尾的} 没有按照单独一行我们平时常用写法,需要给sass添加一个参数 { outputStyle:'expanded'} 以全部展开的形式展示
二 脚本的编译
const babel = require('gulp-babel')
1. 创建一个任务 完成脚本文件复制
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(dest('dist'))
}
2. 安装编译插件 yarn add gulp-babel --dev 并引入
const babel = require('gulp-babel')
3. 使用babel转译 babelx传参 preset-env 最新babel的集合preset
const script = () => {
return src('src/assets/scripts/*.js', {
base: 'src'
})
.pipe(plugins.babel({
presets: ['@babel/preset-env']
}))
.pipe(dest('dist'))
}
// 三 页面模板编译
const data = {
menus: [{
name: 'Home',
icon: 'aperture',
link: 'index.html'
},
{
name: 'Features',
link: 'features.html'
},
{
name: 'About',
link: 'about.html'
},
{
name: 'Contact',
link: '#',
children: [{
name: 'Twitter',
link: 'https://twitter.com/w_zce'
},
{
name: 'About',
link: 'https://weibo.com/zceme'
},
{
name: 'divider'
},
{
name: 'About',
link: 'https://github.com/zce'
}
]
}
],
pkg: require('./package.json'),
date: new Date()
}
1. 安装模板引擎的转换插件 yarn add gulp-swig --dev 并引入
const swig = require('gulp-swig')
2. 创建html的文件复制
const page = () => {
return src('src/*.html', {
base: 'src'
})
.pipe(plugins.swig(data))
.pipe(dest('dist'))
}
3. 写入模板引擎参数
四 创建组合任务
1. 引入 parallel
const complie = parallel(style, script, page)
2. module.exports 导出complie
3. 运行yarn gulp complie
[22:03:00] Starting 'complie'...
[22:03:00] Starting 'style'...
[22:03:00] Starting 'script'...
[22:03:00] Starting 'page'...
[22:03:04] Finished 'script' after 4.69 s
[22:03:04] Finished 'style' after 4.7 s
[22:03:04] Finished 'page' after 4.7 s
[22:03:04] Finished 'complie' after 4.71 s
Done in 11.55s.
五 图片和字体文件转换
1. 创建imgage任务
const imagemin = require('gulp-imagemin')
const image = () => {
return src('src/assets/images/**', {
base: 'src'imagemin
})
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const font = () => {
return src('src/assets/fonts/**', {
base: 'src'
})
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const complie = parallel(style, script, page, image, font) // 完成了src目录的拷贝
2. 安装插件 gulp-imagemin 并引入
六 额外的文件 public
const extra = () => {
return src('public/**', {
base: 'public'
})
.pipe(dest('dist'))
}
const build = parallel(complie, extra)
const del = require('del')
七 自动清除文件夹模块
1. 安装模块不属于gulp但是在gulp中可以用 yarn add del --dev
原因:gulp支持promise方法 del就是一个promise方法且gulp不一定要src 和pipeAPI也支持自己写
2.
const clean = () => {
return del(['dist']) //返回promise
}
3.增加clean 包装build任务
const build = series(clean, parallel(complie, extra)) // 先执行clean再去执行其他任务
八 自动加载插件(解决手动载入依赖)
1. 安装 yarn add gulp-load-plugins --dev
const loadPlugins = require('gulp-laod-plugins')
const plugins = loadPlugins()
九 开发服务器 热加载模块
1. 安装browser-sync模块
2. 创建create方法
3. 把创建服务器的方法放进任务里执行
const serve = () => {
watch('src/assets/styles/*.scss', style)
watch('src/*.html', page)
watch('src/assets/scripts/*.js', script)
// 开发阶段一般不会监听image 和font 只有生产构建才进行无损压缩
// watch('src/assets/images/**', image)
// watch('src/assets/fonts/**', font)
// watch('public/**', extra)
watch(['src/assets/images/**', 'src/assets/fonts/**', 'public/**'], bs.reload)
// 还有一种写法是在init中不写files 会在任务的后面再pipe(bc.reload({stream:true})) 参数是以流的形式推倒浏览器
bc.init({
notify: true, // browser-sync 可能影响
port: '8080', //端口号
open: false, // 运行后自动打开浏览器
files: 'dist/**', // 通配符监听dist下所有文件变化
server: {
baseDir: ['dist', 'src', 'public'],
routes: {
// '/node_modules': 'node_modules' 对于第三方库会映射到项目下node-modules文件中
} // server进来先会找routes 优先于baseDir
}
})
// 这样就可以实现原代码改变 自动进行转换编译然后监听到dist发生变化浏览器自动更新
}
十 useref文件引用处理
useref 自动处理html中的构建注释 注释有开始的build和结束的build中间是资源引用
1. 安装 useref yarn add gulp-useref
const useref = () => {
return src('dist/*.html', {
base: 'dist'
})
.pipe(plugins.useref({
searchPath: ['dist', '.']
}))
.pipe(dest('dist'))
}
十一 分别压缩html css javascript
1. 安装三种压缩插件
yarn add gulp-htmlmin gulp-uglify gulp-clean-css --dev
const useref = () => {
return src('dist/*.html', {
base: 'dist'
})
.pipe(plugins.useref({
searchPath: ['dist', '.']
}))
// html css js 三种文件需要判断
// 安装 gulp-if
.pipe(plugins.if(/\.js/, plugins.uglify()))
.pipe(plugins.if(/\.css/, plugins.cleanCss()))
.pipe(plugins.if(/\.html/, plugins.htmlmin({
collapseWhitespace: true,
minifyCss: true,
minifyJS: true
})))
.pipe(dest('release'))
}
const complie = parallel(style, script, page, )
const build = series(clean, parallel(complie, extra))
const devalop = series(complie, serve)
导出
module.exports = {
// style,
// script,
// page,
// image,
// font
complie,
build,
devalop,
useref,
serve
}
// 十二 重新规划构建过程
// 1. 引入文件
const {
src, // 文件读取
dest, // 文件写入
parallel, // 多任务并行
series // 多任务按顺序串行
} = require('gulp')
const del = require('del') // 删除多余文件
const browserSync = require('browser-sync') // 热服务器插件
const {
watch
} = require('browser-sync') // 热服务器插件的监听方法
const bs = browserSync.create() // 启动测试服务器的方法
const loadPlugins = require('gulp-load-plugins') // 自动加载gulp需要插件
const plugins = loadPlugins()
const data = {
menus: [{
name: 'Home',
icon: 'aperture',
link: 'index.html'
},
{
name: 'Features',
link: 'features.html'
},
{
name: 'About',
link: 'about.html'
},
{
name: 'Contact',
link: '#',
children: [{
name: 'Twitter',
link: 'https://twitter.com/w_zce'
},
{
name: 'About',
link: 'https://weibo.com/zceme'
},
{
name: 'divider'
},
{
name: 'About',
link: 'https://github.com/zce'
}
]
}
],
pkg: require('./package.json'),
date: new Date()
}
// 2. 创建任务
const clean = () => {
return del(['dist']) //返回promise
}
const style = () => {
return src('src/assets/styles/*.scss', {
base: 'src'
})
.pipe(plugins.sass({
outputStyle: 'expanded'
}))
.pipe(dest('temp'))
.pipe(bs.reload({
stream: true
}))
}
const script = () => {
return src('src/assets/scripts/*.js', {
base: 'src'
})
.pipe(plugins.babel({
presets: ['@babel/preset-env']
}))
.pipe(dest('temp'))
.pipe(bs.reload({
stream: true
}))
}
const page = () => {
return src('src/*.html', {
base: 'src'
})
.pipe(plugins.swig({
data,
defaults: {
cache: false
}
})) // 防止模板缓存导致页面不能及时更新
.pipe(dest('temp'))
.pipe(bs.reload({
stream: true
}))
}
const image = () => {
return src('src/assets/images/**', {
base: 'src'
})
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const font = () => {
return src('src/assets/fonts/**', {
base: 'src'
})
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const extra = () => {
return src('public/**', {
base: 'public'
})
.pipe(dest('dist'))
}
const serve = () => {
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
// watch('src/assets/images/**', image)
// watch('src/assets/fonts/**', font)
// watch('public/**', extra)
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload)
bs.init({
notify: false,
port: 2080,
// open: false,
// files: 'dist/**',
server: {
baseDir: ['temp', 'src', 'public'],
routes: {
'/node_modules': 'node_modules'
}
}
})
}
const useref = () => {
return src('temp/*.html', {
base: 'temp'
})
.pipe(plugins.useref({
searchPath: ['temp', '.']
}))
// html js css
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
minifyCSS: true,
minifyJS: true
})))
.pipe(dest('dist'))
}
// 3. 创建组合任务
const compile = parallel(style, script, page)
// 上线之前执行的任务
const build = series(
clean,
parallel(
series(compile, useref),
image,
font,
extra
)
)
const develop = series(compile, serve)
// 4. 导出任务
module.exports = {
clean,
build,
develop
}
// 可以把三个导出的任务写入package.json文件 之后直接yarn 后面运行任务就可以
封装自动化构建工作流
准备
新建一个项目(含远程仓库)
安装zce-cli
提取gulpfile
-
将gulp-demo中的gulpfile.js 文件内容复制到gxw-pages项目下的lib/index.js(入口文件)中
-
将gulp-demo中的package.json中安装的依赖复制到gxw-pages的package.json的dependencies
-
删除gulp-demo项目中的依赖、清空gulpfile.js
-
gxw-pages项目通过yarn link 链接到本地全局
-
在gulp-demo项目中通过yarn link “gxw-pages” 链接到本项目
gulp-demo项目中gulpfile.js添加代码module.exports = require(‘gxw-pages’) -
gulp-demo项目中安装一下依赖(原本项目依赖)
-
安装一下gulp-cl、gulp
-
运行脚本yarn gulp clean
-
解决模块中的问题
-
gulp-demo项目中创建page.config.js文件(目的是抽离出一些配置信息)
-
在gxw-pages项目中lib/index.js加载配置文件
const cwd = process.cwd();// 返回当前命令行工作目录
let config = {
// default config
}
try {
const loadConfig = require(`${cwd}/pages.config.js`)
config = Object.assign({},config,loadConfig)
} catch (error) {
}
将gxw-pages项目中lib/index.js用到的相关配置改成加载过来的数据
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(swig({ data: config.data }))
.pipe(dest('dist'))
}
抽象路径配置
把写死的路径改成可配置的
let config = {
// default config
build:{
src:'src',
dist:'dist',
temp:'temp',
public:'public',
paths:{
styles:'assets/style/*.scss',
scripts:'assets/scripts/*.js',
pages:'*.html',
images:'assets/images/**',
fonts:'assets/font/**'
}
}
}
const style = () => {
return src(config.build.paths.styles, { base: config.build.src,cwd:config.build.src})
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
-
包装gulp cli
-
gulp-demo项目中gulpfile.js删除
-
yarn gulp build --gulpfile .\node_modules\gxw-- – ------pages\lib\index.js
yarn gulp build --gulpfile .\node_modules\gxw-pages\lib\index.js --cwd . (指定当前目录为工作目录)
上面的方法传参太多
解决:在gxw-pages项目中提供一个cli
在gxw-pages项目新建bin/gxw-pages.js
在package.json中配置"bin":“bin/gxw-pages.js”
#!/usr/bin/env node
process.argv.push('--cwd')
process.argv.push(process.cwd())
process.argv.push('--gulpfile')
process.argv.push(require.resolve('..'))
require('gulp/bin/gulp')
gxw-pages clean·
发布使用 gwx-pages
package.json文件files增加
"files": [
"lib",
"bin"
],
npm publish (要先登录)
npm i gwx-pages 在其他项目中使用
6、Fis (高度集成、内置webserver)
- 基本使用
- 安装fis3
- yarn fis3 release (默认构建任务)
- yarn fis3 release -d dist (指定输出目录)
- 配置文件fis-conf.js
- 编译与压缩
- yarn fis3 inspect 查看编译过程
// 安装 fis-parser-node-sass
fis.match('**/*.scss',{
rExt:'.css',// 修改扩展名
parser:fis.plugin('node-sass'),
optimizer:fis.plugin('clean-css')//压缩
})
// 安装
fis.match('**/*.js',{
parser:fis.plugin('babel-6.x'),
optimizer:fis.plugin('uglify-js')
})