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-sync
npm 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(),这样后续任务就不会继续执行。
```javascript
exports.callback_error = done =>{
console.log('cb task')
done(new Error('task failed'))
}
- Promise ```javascript exports.promise = ()=>{ console.log(‘promise task’) return Promise.resolve() }
- **async **
```javascript
const 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’)) }
- **样式编译**
```javascript
const {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。
- **页面模板编译**
```javascript
const {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将任务组合在一起。
```javascript
const {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))
- 自动加载插件
```javascript
const 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
}
文章内容输出来源:拉勾教育大前端高薪训练营;