自动化构建
开发行业中的自动化构建 就是把我们开发中写的源代码自动转换成可以在生产环境中运行的代码。
一般我们会把这个过程称为自动化构建工作流,作用是让我们脱离运行环境兼容带来的种种问题,在开发阶段使用一些提高效率的语法规格和标准。
- ECMAScript新语法提高编码效率和代码质量
- Sass增强css的可编程性
- 模板引擎抽象页面中重复的html
通过自动化构建可以将上述不被浏览器支持的特性转换成能够直接运行的代码。
浏览器使用sass基本使用
- 安装sass模块
yarn add sass --dev
- 执行命令
.\node_modules\.bin\sass.cmd sass/style.sass css/style.css
这样使用的话,每次转换都要执行一边代码,过于繁琐
所以要解决在项目开发阶段重复去执行的命令,可以使用NPM Scripts
NPM Scripts
可以在NPM Scripts中定义一些与这个项目开发过程中有关的脚本命令,让这些命令跟在项目一起去维护。包装构建命令的方式就是在package.json中添加一个scripts字段:
"scripts": {
"build-sass": "sass sass/style.sass css/style.css"
}
键是script命令,值是需要去执行的命令。注意:scripts可以自动去发现node_modules中的命令,这里就不需要写完整的命令,直接写名称可以。
执行 yarn sass-build就可以了。
另外,NPM Scripts也是实现自动化构建最简单的方式。
browser-sync(启动测试服务器运行项目)
yarn add browser-sync --dev
"scripts": {
"sass-build": "sass sass/style.sass css/style.css",
"serve":"browser-sync ."
}
执行 yarn serve启动测试服务器。
NPM Scripts钩子机制
想要实现项目启动前让sass-build 工作,定义一个preserve命令,它会在serve命令执行前去执行。
"scripts": {
"sass-build": "sass scss/style.scss css/style.css",
"preserve":"yarn sass-build"
"serve":"browser-sync ."
}
这样再去执行serve 命令后就会自动化先执行 yarn sass-build 命令,然后执行serve
监听sass文件并同时执行多个任务
安装 npm-run-all:
yarn add npm-run-all --dev
"scripts": {
"sass-build": "sass sass/style.sass css/style.css --watch",
"serve": "browser-sync . --files \"css/*.css\"",
"start": "run-p sass-build serve"
}
- 为sass命令添加一个–watch的参数,sass就会监听文件的变化
- 借助npm-run-all模块同时执行多个任务
- 为browser-sync添加–files参数,可以让browser-sync启动后监听一些文件的变化,然后自动同步到浏览器,自动更新页面,可以不用手动刷新;浏览器了。
这样我们就借助NPM Scripts 完成了一个简单的自动化构建的工作流。(启动后同时运行 serve 和 sass-build 这两个命令)
常用的自动化构建工具
NPM Scripts可以解决一些自动化构建的任务,但对于一些相对复杂的构建过程它就显得非常吃力。这时候就需要更为专业的构建工具。目前市面上使用最多的自动化构建工具主要就是gulp,grunt和FIS。webpack严格意义上来讲是一个模块打包工具。
它们的用法大体相同,都是通过一些简单的代码组织一些插件使用,然后就可以使用工具帮我们执行重复的工作了。
Grunt:
- 是最早的构建工具了
- 插件生态比较完善,几乎可以帮我们完成任何想要做的事情
- 工作过程基于临时文件,每一步都有磁盘读写的操作,所以构建速度相对较慢
Gulp:
- 文件处理环节都是在内存中完成的;
- 可以同时执行多个任务,速度比较快;
- 插件生态完善,现在最主流的构建工具;
FIS
- 百度前端团队使用,项目中典型的需求尽可能都集成在内部
- 捆绑套餐:资源加载、模块化开发、性能优化
Grunt
基本使用:
- 创建新项目,init一个package.json:yarn init --yes
- 安装grunt:yarn add grunt
- 项目根目录下添加一个gruntfile.js文件。(grunt的入口文件)
注册任务:
// Grunt的入口文件
// 用于定义一些需要Grunt 自动执行的任务
// 需要导出一个函数,接收一个grunt的形参,grunt是一个对象,里面是一些grunt的api,这些api可以快速创建一些构建任务
module.exports = (grunt) => {
// 借助grunt的registerTask方法注册一个任务
// registerTask方法第一个参数指定任务名字,第二个参数可以指定任务函数,任务发生时自动执行的函数
grunt.registerTask('foo',()=>{
console.log("hello grunt")
})
// registerTask方法第二个参数是字符串时,这个字符串会成为这个任务的描述,会出现在grunt的帮助信息中。
grunt.registerTask('bar','任务描述',()=>{
console.log(" grunt 任务描述")
})
// 任务名字是default时,这个任务是默认任务,运行时就不需要指定名称,grunt自动执行default
// grunt.registerTask('default',()=>{
// console.log("default task")
// })
// 一般会用default 映射其他的任务,具体做法是第二个参数传入一个数组,数组中指定一些任务的名字。
// 这个时候执行default时,grunt会依次执行数组中的任务。
grunt.registerTask('default', ['foo' , 'bar'] )
// 注册异步任务
// grunt默认支持同步模式,若要支持异步模式,
// 必须使用this.async()得到一个回调函数,异步操作完成后调用诸葛异步函数
grunt.registerTask('async-task', function () {
const done = this.async()
setTimeout(() => {
console.log('async-task')
done()
}, 1000)
})
}
执行 foo 任务:yarn grunt foo
执行 默认任务:yarn grunt
标记失败任务:
如果在构建任务的逻辑代码中发生错误,例如需要的文件找不到了,那就可以把这个任务标记为一个失败任务。
可以在函数体中通过return false来实现。
module.exports = (grunt) => {
grunt.registerTask('foo',()=>{
console.log("hello grunt")
return false;
})
grunt.registerTask('bar','任务描述',()=>{
console.log(" grunt 任务描述")
})
grunt.registerTask('default', ['foo' , 'bar'] )
}
某个任务出错后其他任务继续执行 :yarn grunt --force
异步任务标记失败:
// 标记异步失败任务
grunt.registerTask('bad-async-task', function () {
const done = this.async()
setTimeout(() => {
console.log('bad-async-task')
done(false)
}, 1000)
})
Grunt的配置方法
grunt提供的initConfig 方法去添加一些配置选项,例如使用grunt帮我们压缩文件时可以通过这种方式去配置需要压缩的文件路径。
module.exports = grunt => {
// 配置
grunt.initConfig({
foo: {
name: 'foo'
}
})
grunt.registerTask('foo', () => {
console.log(grunt.config('foo'))//{ name: 'foo' }
})
grunt.registerTask('default', ['foo'])
}
Grunt多目标任务
除了普通的任务形式以外,Grunt中还支持叫多目标模式的任务。可以理解为子任务概念,后续通过Grunt实现各种构建任务时非常有用。
module.exports = grunt => {
grunt.initConfig({
build:{
// 会作为任务的配置(不会作为task )
options:{
foo:'aaa'
},
css:{
name:'css',
// 会合并任务配置中的options,如果存在相同属性,会覆盖
options:{
foo:'css'
}
},
js:'js'
}
})
// 多目标模式
grunt.registerMultiTask('build',function() {
console.log('build')
console.log('target:',this.target)
console.log('data:',this.data)
console.log('options:',this.options())
})
}
- 多目标模式的任务需要通过Grunt的 registerMultiTask 方法去定义,参数1:任务名称;参数2:函数,仍然是任务执行中所需要做的事情;
- 需要为多目标任务配置不同的目标,通过grunt.initConfig方法 去配置。这个方法的参数对象中需要指定一个与任务名称同名的属性“build” ,属性值也是对象,对象中每一个属性名就是 目标 ‘’build‘’ 的目标名称。
- yarn grunt build 后发现它执行了两个目标;运行其中一个的方式:yarn grunt build:css
- 任务函数中可以通过this 拿到当前任务目标的名称和配置数据 (this.target 和 this.data)
- options中的信息会作为任务的配置选项,它不会成为目标。( 任务函数中可以通过this.option() 拿到 )
- 在目标中也可以配置options,会覆盖父对象的options
Grunt插件的使用
插件是 Grunt 的核心,因为很多构建任务都是通用的。社区中出现了很多插件,当中封装了一些通用的构建任务,一般我们的构建过程都是由这些通用的构建任务组成的。
npm 安装这个插件,再到gruntfile.js中去载入这个插件提供的任务,最后 根据插件文档完成相关的配置选项。
- grunt-contrib-clean
比如使用一下grunt-contrib-clean 插件来尝试一下(清除我们在项目开发过程中产生的临时文件):
- 安装:yarn add grunt-contrib-clean
- 使用 grunt .loadNpmTask方法去加载一下这个插件中提供的一些任务
- 绝大多下情况下,grunt插件的命名规范都是grunt-contrib- < name>,所以grunt-contrib-clean插件提供的任务名称就是clean
- 直接运行yarn grunt clean会报出错误,因为clean是一个多目标任务,需要通过initConfig添加配置选项去配置不同的目标。
module.exports = (grunt) => {
grunt.initConfig({
'clean':{
'temp':'temp/**'
}
})
grunt.loadNpmTasks('grunt-contrib-clean')
}
运行:yarn grunt clean
- grunt-sass插件
grunt-sass是一个npm模块,它在内部通过npm形式依赖sass。所以需要sass模块支持。
yarn add grunt-sass sass --dev
const sass = require('sass')
module.exports = (grunt) => {
grunt.initConfig({
sass:{
main:{
options:{
implementation:sass
},
//通过files属性指定输入输出的方式
files:{
'dist/css/main.css':'src/sass/main.scss'
}
}
}
})
grunt.loadNpmTasks('grunt-sass')
}
运行:yarn grunt sass
- grunt-babel
grunt也需要依赖babel的核心模块和预设,有了这三个模块后就可以在gruntfile.js中使用babel的一些任务了。
yarn add grunt-babel @babel-core @babel-preset-env --dev
module.exports = (grunt) => {
grunt.initConfig({
babel:{
main:{
//设置babel转换时候的preset
//babel作为ECMAScript最新特性的转换,支持转换部分特性,preset意思是我们需要转换哪些特性,它把一系列的特性打包形成preset
//env 默认根据最新的es特性做转换
options:{
sourceMap:true,
presets:['@babel/preset-env']
},
files:{
'dist/js/app.js':'src/js/app.js'
}
}
}
})
grunt.loadNpmTasks('grunt-babel')
}
- grunt-contrib-watch 当文件修改成后自动编译
const loadGruntTasks = require('load-grunt-tasks');
const sass = require('sass')
module.exports = (grunt) => {
grunt.initConfig({
babel:{
main:{
//设置babel转换时候的preset
//babel作为ECMAScript最新特性的转换,支持转换部分特性,preset意思是我们需要转换哪些特性,它把一系列的特性打包形成preset
//env 默认根据最新的es特性做转换
options:{
sourceMap:true,
presets:['@babel/preset-env']
},
files:{
'dist/js/app.js':'src/js/app.js'
}
}
},
sass:{
main:{
options:{
implementation:sass
},
files:{
'dist/css/main.css':'src/sass/main.scss'
}
}
},
watch:{
js:{
files:['src/js/*.js'],// 监控哪些文件的变化
tasks:['babel'] //文件的变化后需要执行什么任务
},
css:{
files:['src/sass/*.scss'],// 监控哪些文件的变化
tasks:['sass'] //文件的变化后需要执行什么任务
}
}
})
loadGruntTasks(grunt)
// watch启动后并不会直接执行sass、babel任务,只是会监视文件,当文件发生变化后才会去执行对应的任务
grunt.registerTask('default', ['sass' , 'babel' , 'watch'] )
}