1、什么是自动化构建?
就是将开发阶段的源代码自动的转化为生产环境可以运行的代码。我们将这一转换过程称为自动化构建工作流。自动化构建的作用就是脱离运行环境兼容带来的问题,使用提高效率的语法、规范和标准。
2、NPM Scripts是自动化构建最简单的方式
在package.json中添加scripts字段,他可以自动去执行node_modules中的命令
{"name": "auto build","version": "0.1.0","main": "index.js","license": "MIT","scripts": {"build": "sass scss/main.scss css/style.css","serve": "browser-sync .",},"dependencies": {}}
安装browser-sync,启动一个服务器
npm i browser-syncnpm run serve
但是这里直接启动服务器,sass文件并没有转化为css文件,所以要先执行npm build,在这里npm 提供了pre和post的钩子,这样preserve会在serve之前自动执行。
{"name": "auto build","version": "0.1.0","main": "index.js","license": "MIT","scripts": {"build": "sass scss/main.scss css/style.css","preserve": "npm run build","serve": "browser-sync .",},"dependencies": {}}
在build命令行添加—watch参数
{"name": "auto build","version": "0.1.0","main": "index.js","license": "MIT","scripts": {"build": "sass scss/main.scss css/style.css --watch","preserve": "npm run build","serve": "browser-sync .",},"dependencies": {}}
添加了“—watch”参数后,再执行npm run serve,程序会阻塞在build命令,不会往下执行。 这就需要借助npm-run-all同时执行多个任务。借助run-p命令,代码如下。这里运行npm run start就会同时执行build和serve命令。 并且修改sass文件,css文件同步自动修改。
{"name": "auto build","version": "0.1.0","main": "index.js","license": "MIT","scripts": {"build": "sass scss/main.scss css/style.css --watch","serve": "browser-sync .","start":"run-p build serve"},"dependencies": {}}
在serve命令行添加—files \”css/*.css\”,这样css文件发生改变,浏览器会便自动更新。
{"name": "auto build","version": "0.1.0","main": "index.js","license": "MIT","scripts": {"build": "sass scss/main.scss css/style.css --watch","serve": "browser-sync . --files \"css/*.css\"","start":"run-p build serve"},"dependencies": {}}
以上,我们通过npm scripts完成了简单的自动化构建工作流,他的工作流程就是在启动过后,同时启动了build和serve两个命令,其中build去监听sass文件的变化,并且编译成css,而browser-sync是启动一个web服务,当文件发生变化,自动刷新浏览器。
npm scripts只能解决简单的自动化任务,对于复杂的构建任务,我们还是需要借助常用的构建工具。webpack是模块化打包工具,不在这个讨论范围之内。
3、常用的构建工具
Grunt
它是最早的构建工具,生态很完善,但是构建过程是基于临时文件的,所以构建速度慢。比如在完成sass的构建时,会先对sass文件做编译操作(从磁盘读取文件,完成后将结果写入一个临时文件),再自动添加一些自由属性的前缀(下一个插件在从磁盘读取文件。。。),最后压缩代码,在这每一步操作中,Grunt都会有磁盘读写操作。
Gulp
Gulp很好的解决了Grunt构建速度慢的缺点,它是基于内存实现的,对于文件的处理都是在内存中完成的。另外默认支持同时执行多个任务,这样大大提高了效率,生态也很完善,所以后来居上,目前是最受欢迎的前端构建工具。
FIS
百度推出的,相比较前两个,FIS将项目中一些典型的需求集成在内部,例如在FIS中可以很轻松的处理资源加载、模块化开发、代码部署,甚至是性能优化。
4、Gulp的基本使用
4.1、安装gulp并创建gulpfile.js文件
yarn add gulp
4.2、创建task,通过exports导出成员的方式
// gulp.js(gulp入口文件)exports.foo = ()=>{console.log('foo')}
执行gulp foo
任务会执行,但会出现报出一个错误—foo任务没有完成,是否忘记标记任务结束?
因为在最新的gulp中,取消了同步代码模式,约定每一个任务都是异步任务,当任务结束后,需要手动执行一个done函数,标识任务结束:
exports.foo = done =>{console.log("foo task")done() //标识任务结束}
当导出的是default
exports.default = done =>{console.log("default task")done() //标识任务结束}
4.3、创建任务还可以通过gulp的task方法(不推荐)
const gulp = require('gulp')gulp.task('build',done=>{console.log('build')done()})
4.4、组合任务
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)// 让多个任务同时执行exports.bar = parallel(task1, task2, task3)
4.5、异步任务
- 回调函数 ```javascript // callback
exports.callback = done =>{ console.log(‘cb task’) done() }
如果执行过程中出现错误,采取错误优先的原则,在回调函数中传入new Error(),这样后续任务就不会继续执行。```javascriptexports.callback_error = done =>{console.log('cb task')done(new Error('task failed'))}
- Promise ```javascript exports.promise = ()=>{ console.log(‘promise task’) return Promise.resolve() }
- **async **```javascriptconst timeout = time=>{return new Promise((resolve,reject) => {setTimeout(resolve,time)})}exports.async = async ()=>{await timeout(1000)console.log('async tasks')}
- stream
exports.stream = done => {const readStream = fs.createReadStream('package.json')const writeStream = fs.createWriteStream('temp.text')readStream.pipe(writeStream)return readStream}
4.6、gulp构建过程核心工作原理
在构建过程中有三个重要概念:const {Transform} = require('stream')exports.default = ()=>{const readStream = fs.createReadStream('style.css')const writeStream = fs.createWriteStream('style.min.css')const transform = new Transform({transform:(chunk, encoding,callback) => {const input = chunk.toString()const output = input.replace(/\s+/g,'').replace(/\/\*.+?\*\//g,'')callback(null,output)}})readStream.pipe(transform).pipe(writeStream)return readStream}
4.7、 gulp文件操作API之src和dest
```javascript const {src, dest} =require(‘gulp’) const cleanCss = require(‘gulp-clean-css’) const rename = require(‘gulp-rename’)
exports.default = ()=>{ return src(‘src/*.css’) .pipe(cleanCss) .pipe(rename({extname: ‘min.css’})) .pipe(dest(‘dist’)) }
- **样式编译**```javascriptconst {src,dest}= require('gulp')const sass =require('gulp-sass')(require('sass'))const style = ()=>{return src('src/assets/styles/*.scss',{base:'src'}).pipe(sass().on('error', sass.logError)).pipe(dest('dist'))}module.exports = {style}
- 脚本编译 ```javascript const {src,dest}= require(‘gulp’) const babel = require(‘gulp-babel’)
const script = ()=>{ return src(‘src/assets/scripts/*.js’,{base:’src’}) .pipe(babel({presets: [‘@babel/preset-env’]})) .pipe(dest(‘dist’)) } module.exports = { script }
注意bable()需要传入presets的参数,bable是一个转换平台,具体的转码规则需要借助插件完成,@babel/presets-env是多个preset的集合 (es2015+),包含了我们在babel6中常用的es2015,es2016, es2017等最新的语法转化插件,可以根据配置的目标浏览器或者运行环境来自动将ES2015+的代码转换为es5。- **页面模板编译**```javascriptconst {src,dest}= require('gulp')const swig = require('gulp-swig')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()}const page = ()=>{return src('src/**/*.html',{base:'src'}).pipe(swig({data})).pipe(dest('dist'))}module.exports = {page}
- 图片和字体压缩 ```javascript const imagemin = require(‘gulp-imagemin’)
const image = ()=>{ return src(‘src/assets/images/.{png,jpg,gif,svg,ico}’,{base:’src’}) .pipe(imagemin()) .pipe(dest(‘dist’)) } const font = ()=>{ return src(‘src/assets/fonts/*‘,{base:’src’}) .pipe(imagemin()) .pipe(dest(‘dist’)) } module.exports = { font, image }
图片压缩是无损压缩,只是删除了一些元数据的信息,svg类型的只是删除了空格。以上任务可以通过parallel将任务组合在一起。```javascriptconst {parallel}= require('gulp')const compile = parallel(style,script,page)module.exports = {compile}
- 其他文件拷贝及文件清除
我们一般将src目录下的文件的编译组合成一个compile任务,将compile任务和其他文件的编译任务组成一个build任务,在build任务执行前先清除dist目录 ```javascript const del = require(‘del’)const extra = ()=>{return src('public/**',{base:'public'}).pipe(dest('dist'))}module.exports = {extra}
const clean = ()=>{ return del([‘dist’]) }
const build = series(clean,parallel(compile,extra))
- 自动加载插件```javascriptconst loadPlugins = require('gulp-load-plugins')const plugins = loadPlugins()// plugins是一个对象,所有插件都是这个对象的属性。// 如plugins.babel
- 开发服务器
为了提高开发体验,browser-sync提供了热更新服务。
const browserSync = require('browser-sync').create();// 静态服务器const serve = ()=>{return browserSync.init({notify: false,port: 3000,open: true, // 是否自动打开浏览器server: {baseDir: "dist",}})}
使用gulp serve就可以启动服务。
对于页面中引入的css不是dist目录下的css,而是node_modules下的css,可以在配置项里面做映射:server 里面的routes参数
const browserSync = require('browser-sync').create();// 静态服务器const serve = ()=>{return browserSync.init({server: {baseDir: "dist",routes: {'/node_modules': 'node_modules'}}})}
监听文件变化,自动刷新浏览器:files参数
const browserSync = require('browser-sync').create();// 静态服务器const serve = ()=>{return browserSync.init({files:'dist/**', // 单个文件server: {baseDir: "dist",routes: {'/node_modules': 'node_modules'}}})}
gulp的watch方法监听文件变化
const browserSync = require('browser-sync').create();// 静态服务器const serve = ()=>{watch('src/assets/styles/*.scss',style)watch('src/assets/scripts/*.js',script)watch('src/**/*.html',page)watch('src/assets/images/*.{png,jpg,gif,svg,ico}',image)watch('src/assets/fonts/**',font)watch('public/**',extra)return browserSync.init({open:true,files: 'dist/**',server: {baseDir: "dist",routes: {'/node_modules': 'node_modules'}}})}
构建优化
在开发阶段,减少对图片和字体以及public目录下文件的压缩,可以提高效率。
const browserSync = require('browser-sync').create();// 静态服务器const serve = ()=>{watch('src/assets/styles/*.scss',style)watch('src/assets/scripts/*.js',script)watch('src/**/*.html',page)// watch('src/assets/images/*.{png,jpg,gif,svg,ico}',image)// watch('src/assets/fonts/**',font)// watch('public/**',extra)return browserSync.init({open:true,files: 'dist/**',server: {baseDir: ["dist","src","public"],routes: {'/node_modules': 'node_modules'}}})}const compile = parallel(style,script,page)// 创建一个开发阶段的组合任务const develop = series(clean,compile,serve)// 上线前的组合任务const build = series(clean,parallel(compile,image,font,extra))module.exports = {develop,build}
文章内容输出来源:拉勾教育大前端高薪训练营;
