基於流的構建系統
gulp基本使用
# 初始化項目
yarn init -y
# 安裝gulp
yarn add gulp -D
項目根目錄創建 gulpfile.js 文件 (gulp入口文件)
定義一個基本任務
// 定義一個構件任務
exports.foo = done => {
console.log('hello foo')
// 標識任務完成
done()
}
執行 yarn gulp foo
定義一個默認任務
// 定義一個默認構建任務
exports.default = done => {
console.log('hello default')
// 標識任務完成
done()
}
執行 yarn gulp
組合任務
const {
series,
parallel
} = require('gulp')
const task1 = done => {
setTimeout(() => {
console.log('task1 working')
done()
}, 300)
}
const task2 = done => {
setTimeout(() => {
console.log('task2 working')
done()
}, 200)
}
const task3 = done => {
setTimeout(() => {
console.log('task3 working')
done()
}, 100)
}
// 通過 series 串行任務
exports.series = series(task1, task2, task3)
// 通過 parallel 並行任務
exports.parallel = parallel(task1, task2, task3)
分別調用 yarn gulp series 和 yarn gulp parallel 可以查看效果
異步任務
回調方式
// 回調函數方式
exports.callback = done => {
console.log('calback task')
done()
}
// 錯誤優先回調函數
exports.callback_err = done => {
console.log('calback task')
done(new Error('task failed'))
}
promise
// 使用promise
exports.promise = () => {
console.log( `Promise task` )
return Promise.resolve()
}
// 使用promise 拋出異常
exports.promise_err = () => {
console.log( `Promise task` )
return Promise.reject( `failed Promise` )
}
async
// 模擬異步請求
const timeroutPromise = time => {
return new Promise(resolve => {
console.log( `等待${time}` )
setTimeout(resolve, time)
})
}
// 模擬錯誤異步請求
const errPromise = time => {
return new Promise((resolve, reject) => {
console.log( `等待${time}` )
setTimeout(reject, time)
})
}
// 使用 async
exports.async = async () => {
await timeroutPromise(100)
console.log( `async task` )
}
// 使用 async
exports.async_err = async () => {
await errPromise(100)
console.log( `asyncerr task` )
}
流操作(常用)
const fs = require('fs')
// stream 流操作
exports.stream = () => {
// 讀取定義
const readStream = fs.createReadStream('package.json')
// 寫入定義
const writeStream = fs.createWriteStream('package.json.tmp')
// 管道操作
readStream.pipe(writeStream)
return readStream
}
// stream 使用回調方式模擬流操作結束
exports.streamMock = done => {
// 讀取定義
const readStream = fs.createReadStream('package.json')
// 寫入定義
const writeStream = fs.createWriteStream('package.json.tmp')
// 管道操作
readStream.pipe(writeStream)
readStream.on('end', () => {
done()
})
}
構建過程核心原理
模擬壓縮CSS
目錄結構如下
├── dist
├── gulpfile.js
├── package.json
├── package.json.tmp
├── src
│ └── style.css
└── yarn.lock
style.css
/* body註釋 */
body {
background: #ffffff;
}
/* html註釋 */
html {
background: #e0e5e5;
}
gulpfile.js
const fs = require('fs')
const {
Transform
} = require('stream')
exports.default = () => {
// 文件讀取流
const read = fs.createReadStream('src/style.css')
// 文件寫入流
const write = fs.createWriteStream('dist/style.min.css')
// 文件轉換流
const transform = new Transform({
transform: (chunk, encoding, callback) => {
// 核心轉換過程實現
// chunk => 讀取流中讀取到內容(Buffer)
const input = chunk.toString()
// 模擬壓縮轉換
const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')
// 返回結果,第一個參數是異常 沒有異常傳 null ,第二個參數是返回值
callback(null, output)
}
})
// 管道操作
read
.pipe(transform) //轉換
.pipe(write) // 寫入
return read
}
執行 yarn gulp
文件操作APi以及轉換流
將 src/*.css 導入到 dist目錄下
const {
src,
dest
} = require('gulp')
exports.default = () => {
return src('src/*.css')
.pipe(dest('dist'))
}
安裝 yarn add gulp-clean-css -D (壓縮css)
安裝 yarn add gulp-rename -D (重命名)
// 將 src/*.css 導入到dist目錄下並壓縮且重命名
const {
src,
dest
} = require('gulp')
// 引入壓縮css插件
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'))
}
gulp-常見構建任務
git clone [email protected]:zce/zce-gulp-demo.git 下載模板項目
# 安裝gulp
yarn add gulp -D
# 創建gulpfile文件
echo gulpfile.js
引入gul並導出src dest
const {
src,
dest
} = require('gulp')
樣式編譯
// yarn add gulp-sass -D
const sass = require('gulp-sass')
const style = () => {
// 下劃線 _xx.scss會被忽略過
return src('src/assets/styles/*.scss', {
base: 'src'
})
.pipe(sass({
// 設置輸出爲展開模式
outputStyle: 'expanded'
}))
.pipe(dest('dist'))
}
js文件編譯
// yarn add gulp-babel @babel/preset-env @babel/core -D
const babel = require('gulp-babel')
const script = () => {
return src('src/assets/scripts/*.js', {
base: 'src'
})
.pipe(babel({
presets: ['@babel/preset-env']
}))
.pipe(dest('dist'))
}
模板文件編譯
// yarn add gulp-swig -D
const swig = require('gulp-swig')
const page = () => {
// **/* 所有子目錄通配方式
return src('src/*.html', {
base: 'src'
})
.pipe(swig({
// 需要設置 cache 爲false,否則後面的自動更新不會生效
defaults: {
cache: false
},
data
}))
.pipe(dest('dist'))
}
圖片壓縮
// yarn add gulp-imagemin -D
const imagemin = require('gulp-imagemin')
const image = () => {
return (src('src/assets/images/**', {
base: 'src'
}))
.pipe(imagemin())
.pipe(dest('dist'))
}
字體文件處理
const font = () => {
return (src('src/assets/fonts/**', {
base: 'src'
}))
.pipe(imagemin())
.pipe(dest('dist'))
}
無需處理文件複製到dist
// 額外文件拷貝
const extra = () => {
return (src('public/**', {
base: 'public'
}))
.pipe(dest('dist'))
}
自動刪除dist
// yarn add del -D
const del = require('del')
const clean = () => {
return del(['dist'])
}
useref 文件引用處理(處理文件引用關係)
// yarn add gulp-useref -D
const useref = require('gulp-useref')
const useRef = () => {
return (src('dist/**/*.html', {
base: 'dist'
}))
.pipe(useref({
searchPath: ['dist', '.']
}))
.pipe(dest('dist'))
}
壓縮文件
// 壓縮文件
// gulp-if 判斷
// html js css 壓縮
// yarn add gulp-htmlmin gulp-uglify gulp-clean-css gulp-if -D -D
const useref = require('gulp-useref')
const gulpif = require('gulp-if')
const htmlmin = require('gulp-htmlmin')
const cleanCss = require('gulp-clean-css')
const uglify = require('gulp-uglify')
const useRef = () => {
return (src('dist/**/*.html', {
base: 'dist'
}))
.pipe(useref({
searchPath: ['dist', '.']
}))
// 壓縮js
.pipe(gulpif(/\.js$/, uglify()))
// 壓縮css
.pipe(gulpif(/\.css$/, cleanCss()))
// 壓縮html
.pipe(gulpif(/\.html$/, htmlmin({
minifyCSS: true,
minifyCSS: true,
minifyURLs: true,
collapseWhitespace: true,
removeComments: true
})))
.pipe(dest('release'))
}
組合功能
const {
parallel,
series
} = require('gulp')
// 開發打包
const compile = series(clean, parallel(style, script, page))
// 生產環境打包
const build = series(compile, parallel(image, font), extra, useRef)
熱更新服務器
const browserSync = require('browser-sync')
const bs = browserSync.create()
const server = () => {
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/**/*.js', script)
watch('src/**.html', page)
watch(['src/assets/fonts/**', 'src/assets/images/**', 'public/**'], bs.reload)
bs.init({
// 關閉右上角提示
notify: false,
// 端口
port: 6001,
// 不默認打開
open: false,
// 監聽文件變化,自動刷新
files: "dist/**",
// 服務設置
server: {
// 運行文件夾
baseDir: ['dist', 'src', 'public'],
// 配置映射,routes 高於 baseDir
routes: {
// 將 /node_modules 映射到工程下的node_modules
'/node_modules': 'node_modules'
}
}
})
}
// 組合出dev功能,先編譯在啓動
const dev = series(compile, server)
gulp自動載入包
yarn add gulp - load - plugins - D
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
// 使用方式
// plugins.babel
// 如果是 gulp-ab-cd-ef
// 調用名爲 abCdEf
完整的gulpfile.js文件
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 {
src,
dest,
watch
} = require('gulp')
const del = require('del')
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
// 樣式編譯
const style = () => {
// 下劃線 _xx.scss會被忽略過
return src('src/assets/styles/*.scss', {
base: 'src'
})
.pipe(plugins.sass({
// 設置輸出爲展開模式
outputStyle: 'expanded'
}))
.pipe(dest('temp'))
.pipe(bs.reload({
stream: true
}))
}
// js文件編譯
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({
// 需要設置 cache 爲false,否則後面的自動更新不會生效
defaults: {
cache: false
},
data
}))
.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'))
}
// 自動刪除dist文件
const clean = () => {
return del(['dist', 'temp'])
}
const useref = () => {
return (src('temp/**/*.html', {
base: 'temp'
}))
.pipe(plugins.useref({
searchPath: ['temp', '.']
}))
// 壓縮js
.pipe(plugins.if(/\.js$/, plugins.uglify()))
// 壓縮css
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
// 壓縮html
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
minifyCSS: true,
minifyCSS: true,
minifyURLs: true,
collapseWhitespace: true,
removeComments: true
})))
.pipe(dest('dist'))
}
// 組合功能
const {
parallel,
series
} = require('gulp')
const compile = parallel(style, script, page)
const build = series(clean, parallel(series(compile, useref), image, font, extra))
// 優化開發環境 減少不必要開銷 例如圖片壓縮 字體壓縮
const browserSync = require('browser-sync')
const bs = browserSync.create()
const server = () => {
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/**/*.js', script)
watch('src/**.html', page)
watch(['src/assets/fonts/**', 'src/assets/images/**', 'public/**'], bs.reload)
bs.init({
// 關閉右上角提示
notify: false,
// 端口
port: 6001,
// 不默認打開
open: false,
// 服務設置
server: {
// 運行文件夾
baseDir: ['temp', 'src', 'public'],
// 配置映射,routes 高於 baseDir
routes: {
// 將 /node_modules 映射到工程下的node_modules
'/node_modules': 'node_modules'
}
}
})
}
const dev = series(compile, server)
module.exports = {
build,
dev,
clean
}