自動(dòng)化構(gòu)建
一切重復(fù)的工作都應(yīng)該被自動(dòng)化
自動(dòng)化構(gòu)建是前端工程化過程中一個(gè)重要的組成部分
自動(dòng)化構(gòu)建工作流可以使我們?cè)陂_發(fā)階段使用一些高效的語法、規(guī)范、標(biāo)準(zhǔn)和工具,脫離運(yùn)行環(huán)境兼容性帶來的問題,并最終自動(dòng)轉(zhuǎn)換成能夠被運(yùn)行環(huán)境支持的可執(zhí)行代碼
自動(dòng)化構(gòu)建初體驗(yàn)
嘗試使用sass開發(fā)頁面樣式,并自動(dòng)編譯為css文件
創(chuàng)建項(xiàng)目目錄,并使用
yarn init -y初始化添加index.html頁面用于測(cè)試
-
yarn add sass --dev添加sass模塊作為開發(fā)依賴- 此時(shí)可以使用sass編寫樣式文件*.scss,并使用
yarn sass <source> <target>命令將scss文件編譯為css文件
- 此時(shí)可以使用sass編寫樣式文件*.scss,并使用
-
在package.json文件中,添加script屬性,定義npm運(yùn)行腳本
- 此時(shí)可以通過
npm run <command>或yarn <command>運(yùn)行script中定義的命令
- 此時(shí)可以通過
yarn add npm-run-all --dev安裝npm-run-all作為開發(fā)依賴,可以用來順序同時(shí)運(yùn)行多個(gè)script中定義的命令yarn add browser-sync --dev安裝browser-sync作為開發(fā)依賴,可以監(jiān)控指定目錄下的文件改動(dòng),并自動(dòng)刷新瀏覽器-
上述步驟實(shí)現(xiàn)了開發(fā)過程中,自動(dòng)將scss文件編譯為css文件,并自動(dòng)監(jiān)聽文件變化,刷新瀏覽器實(shí)時(shí)查看最新頁面效果
// package.json { "name": "sample", "version": "1.0.0", "main": "index.js", "license": "MIT", "scripts": { "build": "sass scss/main.scss css/main.css --watch", "serve": "browser-sync . --files \"*.html, css/*.css\"", "start": "run-p build serve" }, "devDependencies": { "browser-sync": "^2.26.12", "sass": "^1.26.10", "npm-run-all": "^4.1.5" } }
常用的自動(dòng)化構(gòu)建工具

- Grunt
- 插件生態(tài)豐富,可以實(shí)現(xiàn)任意類型項(xiàng)目的構(gòu)建
- 工作過程基于臨時(shí)文件,磁盤讀寫頻繁,構(gòu)建速度較慢
- Gulp
- 插件生態(tài)豐富
- 工作過程基于內(nèi)存,構(gòu)建速度較快
- 支持同時(shí)進(jìn)行多個(gè)構(gòu)建任務(wù)
- FIS
- 由百度前端團(tuán)隊(duì)推出的構(gòu)建工具
- 捆綁式全家桶
- 適合新手
-
Webpack?- Webpack屬于模塊打包工具
Grunt
-
基本使用
新建項(xiàng)目目錄,并使用
yarn init -y命令初始化yarn add grunt添加grunt模塊-
添加gruntfile.js文件
- Grunt的入口文件
- 用于定義Grunt自動(dòng)執(zhí)行的任務(wù)
- 需要導(dǎo)出一個(gè)函數(shù)
- 函數(shù)接收grunt形參,用于提供創(chuàng)建任務(wù)時(shí)將會(huì)用到的的API
-
grunt.registerTask注冊(cè)待執(zhí)行的task- 第一個(gè)參數(shù)為task的名稱,如果名稱為
default,則為默認(rèn)task,執(zhí)行yarn grunt不指定task明時(shí)默認(rèn)執(zhí)行 default task - 第二個(gè)參數(shù)如果是回調(diào)函數(shù),則表示task執(zhí)行時(shí)要執(zhí)行的內(nèi)容
- 第二個(gè)參數(shù)如果是字符串,則是對(duì)該task的描述信息,當(dāng)執(zhí)行
yarn grunt --help時(shí)會(huì)顯示相應(yīng)任務(wù)的描述 - 第二個(gè)參數(shù)如果是數(shù)組,則接收由task名稱組成的字符串?dāng)?shù)組,執(zhí)行該task將會(huì)依次執(zhí)行數(shù)組中指定的task
- 如果task執(zhí)行的是一個(gè)異步任務(wù),需要使用調(diào)用
this.async()返回的函數(shù)來標(biāo)記異步操作執(zhí)行完成,此時(shí)grunt會(huì)等待異步操作執(zhí)行完成
- 第一個(gè)參數(shù)為task的名稱,如果名稱為
-
yarn grunt <task>執(zhí)行g(shù)runtfile.js中定義的task// gruntfile.js module.exports = grunt => { grunt.registerTask('foo', () => { console.log('hello grunt') }) grunt.registerTask('bar', 'description', () => { console.log('hello bar') }) grunt.registerTask('default', ['foo', 'bar']) grunt.registerTask('async-task', function() { const done = this.async(); setTimeout(()=> { console.log('async task done') done() }, 1000) }) }
-
標(biāo)記任務(wù)失敗
同步任務(wù)通過在一個(gè)task的回調(diào)函數(shù)中
return false來實(shí)現(xiàn)順序執(zhí)行多個(gè)任務(wù)時(shí),當(dāng)有一個(gè)任務(wù)被標(biāo)記失敗,后續(xù)任務(wù)將不再繼續(xù)執(zhí)行
使用
--force參數(shù)來強(qiáng)制執(zhí)行所有任務(wù)-
異步任務(wù)標(biāo)記失敗,需要使用
this.async()返回的函數(shù),傳入false// gruntfile.js module.exports = grunt => { grunt.registerTask('foo', () => { console.log('hello grunt') }) grunt.registerTask('bar', 'description', () => { console.log('hello bar') }) grunt.registerTask('bad', ()=> { console.log('bad task') return false }) grunt.registerTask('default', ['foo', 'bad', 'bar']) grunt.registerTask('async-task', function() { const done = this.async(); setTimeout(()=> { console.log('async task fail') done(false) }, 1000) }) }
-
配置方法
使用
grunt.initConfig({})方法進(jìn)行g(shù)runt配置參數(shù)的初始化-
在task的回調(diào)函數(shù)中,通過
grunt.config(key)方法可以獲取參數(shù)配置對(duì)象中的值// gruntfile.js module.exports = grunt => { grunt.initConfig({ hello: { what: 'world' } }) grunt.registerTask('foo', () => { console.log(`hello ${grunt.config('hello.what')}`) }) }
-
多目標(biāo)任務(wù)
多目標(biāo)模式,可以讓任務(wù)根據(jù)配置形成多個(gè)子任務(wù)
使用
grunt.registerMultiTask(<task>, <func>來注冊(cè)多目標(biāo)任務(wù)-
多目標(biāo)任務(wù)需要通過
grunt.initConfig來配置相應(yīng)的子任務(wù)- 以task名稱作為key,value是配置對(duì)象
- 配置對(duì)象以子任務(wù)名稱作為key,在任務(wù)運(yùn)行時(shí)可以通過
this.target獲取當(dāng)前執(zhí)行的子任務(wù)名稱 - 配置對(duì)象的值為運(yùn)行時(shí)的配置數(shù)據(jù),在任務(wù)運(yùn)行時(shí)可以通過
this.data獲取當(dāng)前執(zhí)行的子任務(wù)的配置數(shù)據(jù) - 當(dāng)配置對(duì)象的key為options時(shí),代表任務(wù)運(yùn)行時(shí)的選項(xiàng)配置,而不代表一個(gè)子任務(wù)
- 子任務(wù)的配置數(shù)據(jù)中也可以包含options,此時(shí)該子任務(wù)中配置的options覆蓋任務(wù)全局的options
// gruntfile.js module.exports = grunt => { grunt.initConfig({ build: { options: { hello: 'world' }, css: { value: 'css' }, js: { options: { hello: 'grunt' }, value: 'js' } }, hello: { what: 'world' } }) grunt.registerMultiTask('build', function() { console.log(this.options()) console.log(this.target) console.log(this.data) }) }
-
插件的使用
安裝相應(yīng)的grunt插件模塊,例如
yarn add grunt-contrib-clean使用
grunt.loadNpmTasks加載插件,并在grunt.initConfig()中配置相關(guān)的參數(shù)(這里grunt-contrib-clean是多目標(biāo)任務(wù))-
執(zhí)行插件相關(guān)的task
// gruntfile.js module.exports = grunt => { grunt.initConfig({ clean: { temp: 'temp/*.txt' } }) grunt.loadNpmTasks('grunt-contrib-clean') }
-
常用插件及總結(jié)
- grunt-sass 用于scss轉(zhuǎn)css
yarn add grunt-sass sass --dev
- grunt-babel 用于js語法轉(zhuǎn)換
yarn add grunt-babel @babel/core @babel/present-env --dev
- grunt-contrib-watch 開發(fā)階段監(jiān)控文件改動(dòng),并自動(dòng)執(zhí)行配置指定的task
yarn add grunt-contirb-watch --dev
- load-grunt-tasks 用于自動(dòng)加載所有g(shù)runt插件中的任務(wù)
yarn add load-grunt-tasks --dev
- grunt-sass 用于scss轉(zhuǎn)css
Gulp
-
gulp的基本使用
新建項(xiàng)目目錄,并使用
yarn init -y命令初始化yarn add gulp添加gulp模塊-
添加gulpfile.js文件
- gulp的入口文件
- 用于定義gulp自動(dòng)執(zhí)行的任務(wù)
- gulp 4.0以上的版本通過exports.task的方式導(dǎo)出一個(gè)task,exports.default為默認(rèn)的task
- gulp定義任務(wù)為異步任務(wù),需要在任務(wù)執(zhí)行的函數(shù)中調(diào)用參數(shù)傳入的方法,去標(biāo)記任務(wù)是否執(zhí)行完成
-
yarn gulp <task>執(zhí)行g(shù)ulpfile.js中定義的task// gulpfile.js gulp的入口文件 exports.foo = done => { console.log('gulp foo') done() } exports.default = done => { console.log('gulp default') done() } // gulp 4.0以前版本定義task方式 const gulp = require('gulp') gulp.task('bar', done => { console.log('gulp bar') done() })
-
gulp的組合任務(wù)
- gulp提供了
series和parallel兩種方式來組合執(zhí)行多個(gè)任務(wù) - series為串行執(zhí)行多個(gè)任務(wù)
- parallel為并行執(zhí)行多個(gè)任務(wù)
// gulpfile.js const { series, parallel } = require('gulp') const task1 = done => { setTimeout(() => { console.log('task 1 working') done() }, 1000); } const task2 = done => { setTimeout(() => { console.log('task 2 working') done() }, 1000); } const task3 = done => { setTimeout(() => { console.log('task 3 working') done() }, 1000); } exports.series = series(task1, task2, task3) exports.parallel = parallel(task1, task2, task3) - gulp提供了
-
gulp的異步任務(wù)常用的幾種方式
-
回調(diào)函數(shù)方式
-
gulp異步任務(wù)函數(shù)接收一個(gè)回調(diào)函數(shù)參數(shù),通過該回調(diào)函數(shù),可以標(biāo)記異步任務(wù)是否執(zhí)行完成,或者發(fā)生異常,當(dāng)發(fā)生異常,后續(xù)任務(wù)將不再繼續(xù)執(zhí)行
exports.callback = done => { console.log('callback') done() } exports.callback_error = done => { console.log('callback error') done(new Error('callback error')) }
-
-
Promise
-
gulp異步任務(wù)支持使用Promise方式,標(biāo)記異步任務(wù)執(zhí)行完成,返回一個(gè)resolved狀態(tài)的Promise對(duì)象,標(biāo)記任務(wù)失敗,返回一個(gè)rejected狀態(tài)的Promise對(duì)象
exports.promise = () => { console.log('promise') return Promise.resolve() } exports.promise_error = () => { console.log('promise error') return Promise.reject(new Error('promise error')) }
-
-
async/await
-
async/await是Promise的語法糖,使用node版本8以上時(shí),gulp也支持使用該方式處理異步任務(wù)
const timeout = time => { return new Promise((resolve) => { setTimeout(resolve, time) }) } exports.async = async () => { await timeout(1000) console.log('async timeout') }
-
-
stream
通常在自動(dòng)化構(gòu)建過程中需要處理大量的文件,stream也是gulp異步任務(wù)處理中常用的方式
-
通過返回一個(gè)stream對(duì)象,當(dāng)stream的end方法被調(diào)用時(shí),異步任務(wù)會(huì)被標(biāo)記完成
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_callback = done => { const readStream = fs.createReadStream('package.json') const writeStream = fs.createWriteStream('temp.txt') readStream.pipe(writeStream) readStream.on('end', () => { done() }) }
-
-
gulp構(gòu)建過程核心工作原理
官方對(duì)于gulp的定義是the streaming build system
即基于流的構(gòu)建系統(tǒng)
構(gòu)建的流程通常是:讀取文件 -> 處理讀取內(nèi)容 -> 寫入文件
-
對(duì)應(yīng)gulp的工作過程:讀取流(read stream) -> 轉(zhuǎn)換流(transform stream) -> 寫入流 (write stream)
// 嘗試模擬實(shí)現(xiàn)css的壓縮 const fs = require('fs') const { Transform } = require('stream') exports.transform = () => { const read = fs.createReadStream('style.css') const write = 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) } }) return read .pipe(transform) .pipe(write) }
-
gulp文件操作API
-
使用gulp提供的src與dest來實(shí)現(xiàn)文件的讀取流與寫入流
// 使用src批量讀取文件,使用dest批量寫入文件 // 借助gulp-clean-css插件壓縮css // 借助gulp-rename插件重命名 const { src, dest } = require('gulp') const cleanCss = require('gulp-clean-css') const rename = require('gulp-rename') exports.minify = () => { return src('style.css') .pipe(cleanCss()) .pipe(rename({ extname: '.min.css' })) .pipe(dest('dist')) }
-
-
gulp案例
這里通過一個(gè)工程樣例來展示,在實(shí)際開發(fā)過程中,對(duì)一個(gè)前端工程使用gulp進(jìn)行自動(dòng)化構(gòu)建可能會(huì)涉及到的各種編譯配置
例如樣式文件sass的編譯,腳本ES6+的轉(zhuǎn)換,HTML文件的轉(zhuǎn)換等等
-
樣式編譯
const { src, dest } = require('gulp'); const sass = require('gulp-sass'); const style = () => { return src('src/assets/styles/*.scss', { base: 'src' }) .pipe(sass({ outputStyle: 'expanded' })) .pipe(dest('dist/styles')) } module.exports = { style, } -
腳本編譯
// gulpfile.js 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, } -
頁面模板編譯
// gulpfile.js 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(plugins.swig({ data, defaults: { cache: false } })) // 關(guān)閉緩存防止修改不實(shí)時(shí)生效 .pipe(dest('temp')) } module.exports = { page, } -
圖片和字體文件轉(zhuǎn)換
// gulpfile.js const { src, dest} = require('gulp'); 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')) } module.exports = { image, font, } -
其他文件及文件清除
const del = require('del'); const extra = () => { return src('public/**', { base: 'public' }) .pipe(dest('dist')) } const clean = () => { return del(['dist']) } -
自動(dòng)加載插件
const loadPlugins = require('gulp-load-plugins'); const plugins = loadPlugins(); // 使用gulp-load-plugins插件自動(dòng)加載所有已安裝的以gulp-開頭的插件,并使用plugins.xxxx訪問相應(yīng)的插件 // 例如使用plugins.sass訪問gulp-sass插件 -
開發(fā)服務(wù)器
const browserSync = require('browser-sync'); const bs = browserSync.create(); const serve = () => { bs.init({ notify: false, files: 'dist/**', server: { baseDir: 'dist', routes: { '/node_modules': './node_modules' } } }) } -
監(jiān)視變化以及構(gòu)建優(yōu)化
- 使用gulp提供的watch API來監(jiān)控指定文件,并在文件變化時(shí),執(zhí)行相應(yīng)的task
- watch接收兩個(gè)參數(shù),第一個(gè)參數(shù)是文件通配符字符串或者通配符字符串?dāng)?shù)組,表示要監(jiān)視的文件,第二個(gè)參數(shù)是文件變化時(shí)要執(zhí)行的task函數(shù)
- 在開發(fā)階段,對(duì)于圖片及字體的壓縮以及靜態(tài)資源的拷貝意義不大,同時(shí)會(huì)增加構(gòu)建任務(wù)的開銷,將這些文件保留在源文件目錄(需指定baseDir)并直接通過watch對(duì)這些目錄進(jìn)行監(jiān)視,在文件發(fā)生變化時(shí),執(zhí)行bs.reload刷新頁面
// gulpfile.js const { src, dest, series, parallel, watch } = require('gulp'); const bs = browserSync.create(); const serve = () => { watch('src/assets/styles/*.scss', style) watch('src/assets/scripts/*.js', script) watch('src/**/*.html', page) // watch('src/assets/images/**', image) 開發(fā)階段意義不大 // watch('src/assets/fonts/**', font) 開發(fā)階段意義不大 // watch('public/**', extra) 開發(fā)階段意義不大 watch([ 'src/assets/images/**', 'src/assets/fonts/**', 'public/**', ], bs.reload) bs.init({ notify: false, files: 'dist/**', server: { baseDir: ['dist', 'src', 'public'], routes: { '/node_modules': './node_modules' } } }) } -
useref文件引用處理
- 依賴gulp-useref插件,可以將html中依賴的js、css根據(jù)注釋提取并生成到指定的文件中,并替換依賴的資源文件路徑為新生成的文件路徑
const useref = () => { return src('dist/*.html', { base: 'dist' }) .pipe(plugins.useref({ searchPath: ['dist', '.'] })) .pipe(dest('dist')) } -
文件壓縮
- 使用相關(guān)的gulp插件對(duì)相應(yīng)類型的文件進(jìn)行壓縮處理
- gulp-uglify 壓縮js文件
- gulp-clean-css 壓縮css文件
- gulp-htmlmin 壓縮html文件
- 為避免文件讀寫沖突,可以將壓縮后的代碼放入另外的文件夾,例如release
const useref = () => { return src('dist/*.html', { base: 'dist' }) .pipe(plugins.useref({ searchPath: ['dist', '.'] })) .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')) } - 使用相關(guān)的gulp插件對(duì)相應(yīng)類型的文件進(jìn)行壓縮處理
-
重新規(guī)劃構(gòu)建過程
- 對(duì)上述構(gòu)建任務(wù)進(jìn)行重新整理
- style、script、page可以通過parallel組合成并行處理任務(wù)compile
const compile = parallel(style, script, page),生成的文件由于需要后續(xù)進(jìn)行useref壓縮處理,構(gòu)建過程中臨時(shí)存放在temp目錄,最終壓縮后放入dist目錄 - useref需要先進(jìn)行compile生成臨時(shí)文件,可以使用series組合成串行處理任務(wù),最終組合成構(gòu)建任務(wù)build
const build = series(clean, parallel(series(compile, useref), image, font, extra)) - 去除不必要exports的任務(wù)
- 將對(duì)應(yīng)的構(gòu)建任務(wù)加入package.json的script中
- 完整的gulpfile.js如下
// gulpfile.js const { src, dest, series, parallel, watch } = require('gulp'); const loadPlugins = require('gulp-load-plugins'); const browserSync = require('browser-sync'); const del = require('del'); const plugins = loadPlugins(); const bs = browserSync.create(); 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 style = () => { return src('src/assets/styles/*.scss', { base: 'src' }) .pipe(plugins.sass({ outputStyle: 'expanded' })) .pipe(dest('temp')) } const script = () => { return src('src/assets/scripts/*.js', { base: 'src' }) .pipe(plugins.babel({ presets: ['@babel/preset-env'] })) .pipe(dest('temp')) } const page = () => { return src('src/**/*.html', { base: 'src' }) .pipe(plugins.swig({ data })) .pipe(dest('temp')) } 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 clean = () => { return del(['dist', 'temp']) } const compile = parallel(style, script, page) const serve = () => { watch('src/assets/styles/*.scss', style) watch('src/assets/scripts/*.js', script) watch('src/**/*.html', page) // watch('src/assets/images/**', image) 開發(fā)階段意義不大 // watch('src/assets/fonts/**', font) 開發(fā)階段意義不大 // watch('public/**', extra) 開發(fā)階段意義不大 watch([ 'src/assets/images/**', 'src/assets/fonts/**', 'public/**', ], bs.reload) bs.init({ notify: false, files: 'temp/**', server: { baseDir: ['temp', 'src', 'public'], routes: { '/node_modules': './node_modules' } } }) } const useref = () => { return src('temp/*.html', { base: 'temp' }) .pipe(plugins.useref({ searchPath: ['temp', '.'] })) .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')) } const build = series(clean, parallel(series(compile, useref), image, font, extra)) const dev = series(compile, serve) module.exports = { clean, build, dev, }
-
-
封裝工作流
多個(gè)項(xiàng)目中復(fù)用gulpfile中的task
-
準(zhǔn)備
- 創(chuàng)建項(xiàng)目托管repository(github/gitee等)
- 創(chuàng)建項(xiàng)目目錄xxx,并初始化
yarn init -y - 創(chuàng)建目錄結(jié)構(gòu)及文件lib/index.js
-
提取gulpfile
- 將原gulpfile.js中的內(nèi)容復(fù)制到lib/index.js文件中
- 將原package.json中devDependencies中的依賴添加到當(dāng)前package.json的dependencies中
- yarn安裝相應(yīng)的依賴
-
yarn link建立軟連接 - 進(jìn)入待構(gòu)建項(xiàng)目目錄,執(zhí)行
yarn link xxx,執(zhí)行相應(yīng)的命令yarn xxx,測(cè)試當(dāng)前的封裝
-
解決模塊中的問題
- 將原gulpfile.js中使用到的data數(shù)據(jù),改為讀取配置文件xxx.config.js
- 對(duì)babel使用的插件模塊,使用require引入
-
抽象路徑配置
-
將原gulpfile.js中使用到的項(xiàng)目路徑,通過config進(jìn)行配置,并提供默認(rèn)值,同時(shí)替換所有使用位置為config讀取
// lib/index.js const { src, dest, series, parallel, watch } = require('gulp'); const loadPlugins = require('gulp-load-plugins'); const browserSync = require('browser-sync'); const del = require('del'); const plugins = loadPlugins(); const bs = browserSync.create(); const cwd = process.cwd() let config = { // default config build: { src: 'src', temp: 'temp', dist: 'dist', public: 'public', paths: { styles: 'assets/styles/*.scss', scripts: 'assets/scripts/*.js', pages: '*.html', images: 'assets/images/**', fonts: 'assets/fonts/**', } } } try { const loaded = require(`${cwd}/pages.config.js`) config = { ...config, ...loaded } } catch(e) {} const style = () => { return src(config.build.paths.styles, { base: config.build.src, cwd: config.build.src }) .pipe(plugins.sass({ outputStyle: 'expanded' })) .pipe(dest(config.build.temp)) } const script = () => { return src(config.build.paths.scripts, { base: config.build.src, cwd: config.build.src }) .pipe(plugins.babel({ presets: [require('@babel/preset-env')] })) .pipe(dest(config.build.temp)) } const page = () => { return src(config.build.paths.pages, { base: config.build.src, cwd: config.build.src }) .pipe(plugins.swig({ data: config.data })) .pipe(dest(config.build.temp)) } const image = () => { return src(config.build.paths.images, { base: config.build.src, cwd: config.build.src }) .pipe(plugins.imagemin()) .pipe(dest(config.build.dist)) } const font = () => { return src(config.build.paths.fonts, { base: config.build.src, cwd: config.build.src }) .pipe(plugins.imagemin()) .pipe(dest(config.build.dist)) } const extra = () => { return src('**', { base: config.build.public, cwd: config.build.public }) .pipe(dest(config.build.dist)) } const clean = () => { return del([config.build.dist, config.build.temp]) } const compile = parallel(style, script, page) const serve = () => { watch(config.build.paths.styles, { cwd: config.build.src}, style) watch(config.build.paths.scripts, { cwd: config.build.src}, script) watch(config.build.paths.pages, { cwd: config.build.src}, page) // watch('src/assets/images/**', image) 開發(fā)階段意義不大 // watch('src/assets/fonts/**', font) 開發(fā)階段意義不大 // watch('public/**', extra) 開發(fā)階段意義不大 watch([ config.build.paths.images, config.build.paths.fonts, ], { cwd: config.build.src }, bs.reload) watch([ '**', ], { cwd: config.build.public }, bs.reload) bs.init({ notify: false, files: `${config.build.temp}/**`, server: { baseDir: [config.build.temp, config.build.src, config.build.public], routes: { '/node_modules': './node_modules' } } }) } const useref = () => { return src(config.build.paths.pages, { base: config.build.temp, cwd: config.build.temp }) .pipe(plugins.useref({ searchPath: [config.build.paths.temp, '.'] })) .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(config.build.dist)) } const build = series(clean, parallel(series(compile, useref), image, font, extra)) const dev = series(compile, serve) module.exports = { clean, build, dev, }
-
-
包裝Gulp CLI
項(xiàng)目目錄下新建bin/xxx.js
package.js中增加bin選項(xiàng)配置對(duì)應(yīng)的文件
bin/xxx.js中,讀取process.argv命名行參數(shù)數(shù)組,并添加gulp執(zhí)行相關(guān)參數(shù)
-
require('gulp/bin/gulp')執(zhí)行g(shù)ulp命令#!/usr/bin/env node process.argv = process.argv.concat(['--cwd', process.cwd(), '--gulpfile', require.resolve('..')]) require('gulp/bin/gulp')
-
發(fā)布并使用模塊
- 執(zhí)行
yarn publish --registry=https://registry.yarnpkg.com發(fā)布封裝的工作流模塊到npm - 在待使用的項(xiàng)目中,執(zhí)行
yarn add xxx --dev添加相應(yīng)的依賴 -
xxx build進(jìn)行使用
- 執(zhí)行
-
FIS
由百度前端團(tuán)隊(duì)推出的自動(dòng)化構(gòu)建工具,內(nèi)置了很多常用的任務(wù)以及devServer,不需要開者自己去配置
- 基本使用
-
yarn add fis3 --dev安裝fis3模塊 -
fis3 release編譯,使用-d參數(shù)可以指定輸出目錄fis3 release -d output - 添加配置文件fis-config.js,通過配置文件可以對(duì)fis3編譯進(jìn)行配置
-
- 詳細(xì)用法