9012教你如何使用gulp4開發項目腳手架

本文將會介紹如何使用gulp4來搭建項目腳手架,若是您還在使用gulp3或更老的版本,您也以經過本文的一些思想將以前的項目進行完善,更新。若是gulp不是大家團隊的重點,也能夠移步個人另外一篇文章:

用 webpack 4.0 擼單頁/多頁腳手架 (jquery, react, vue, typescript)css

前言

因爲本文重點是介紹gulp4.0搭建腳手架的思路,相關插件的用法以及項目結構的設計,因爲gulp的基本用法很簡單,若是不熟悉能夠移步官網自行研究學習。該腳手架的設計思路和功能以下:html

同時爲了提升開發環境的效率,這裏咱們參考webpack的配置,區分開發環境和生產環境,在接下來將會具體介紹。

腳手架用到的第三方插件介紹

  • gulp-jshint ——js語法檢測
  • gulp-util ——終端控制檯打印自定義錯誤信息
  • http-proxy-middleware ——設置代理,配合gulp-connect使用
  • gulp-less ——將less編譯成css
  • gulp-file-include ——用於文件模塊化導入,如用include的方式導入公共部分
  • gulp-connect ——用於啓動本地服務器
  • gulp-clean ——清理目錄
  • gulp-uglify --壓縮js
  • gulp-minify-css ——壓縮css
  • gulp-autoprefixer ——自動添加瀏覽器前綴
  • imagemin-pngquant ——png圖片壓縮
  • gulp-imagemin ——圖壓縮
  • gulp-cache ——設置gulp打包的緩存,通常用於img
  • gulp-md5-plus ——將文件名進行md5處理便於打包更新

固然gulp還有不少經常使用的插件能夠更好的爲咱們的項目服務,你們也能夠整合本身的插件讓項目更加完善。前端

項目目錄設計

1.src目錄,即咱們開發項目時的源目錄,具體結構以下:

咱們定義views是咱們視圖層,即頁面文件的目錄,js目錄爲業務邏輯的腳本文件,lib存放第三方框架,include目錄爲公共部分的存放目錄,咱們能夠用gulp-file-include來導入到html中,images和css你們都比較清楚,分別時存放image和css文件的目錄。

2. dist目錄,即輸出的目錄,具體結構以下:

能夠看到咱們會看到src打包後的目錄對應static目錄,爲何咱們會加一層static呢?個人設計是若是項目使用node等服務層框架,咱們能夠用gulp一併打包放入dist下,這樣dist就是一個完整的包括先後端服務的項目目錄了,固然你們也能夠直接將src打包後的文件和文件夾直接放到dist下,根具業務需求靈活設計吧。

在這裏我要說一點,因爲筆者親測gulp-md5-plus有時候打包不穩定,可能不會給html自動添加對應的md5後綴,因此筆者在這塊作了特殊的處理,若是你們在工做中有更好的方案,能夠及時和筆者溝通交流。vue

3. gulpfile文件配置

因爲咱們要區分開發環境和生產環境,因此這裏咱們使用兩個不一樣的配置文件,根據NODE_ENV來區分用哪一個文件。node

咱們將配置文件統一放到build目錄下,config爲公共配置文件,gulp.dev.js和gulp.prod.js分別爲開發和生產環境配置文件。咱們總體的目錄結構以下:

腳手架完整源碼(部分插件和配置會給出詳細註釋)

  1. config.js
module.exports = {
    dist: './dist/static',  // 配置構建目錄
}
複製代碼
  1. gulp.dev.js
const gulp = require('gulp');
// js
const Jshint = require("gulp-jshint");          //js檢查
const Gutil = require('gulp-util');
const Proxy = require('http-proxy-middleware');
// const Webpack = require('webpack');
// const WebpackConfig = require('./webpack.config.js');

// css
const Less = require('gulp-less');              // 編譯less

// html
const FileInclude = require('gulp-file-include'); // 文件模塊化

// server
const Connect = require('gulp-connect');        //引入gulp-connect模塊 

const Clean = require('gulp-clean');            // 清理目錄

// 配置文件
const config = require('./config');
const { dist } = config;

// html
async function html() {
    return gulp.src('src/views/*.html')
        .pipe(FileInclude({ // HTML模板替換,具體用法見下文
            prefix: '##',
            basepath: '@file'
        })).on('error', function(err) {
            console.error('Task:copy-html,', err.message);
            this.end();
        })
        .pipe(gulp.dest(dist)) // 拷貝 
        .pipe(Connect.reload())
}

// css
async function css() {
    return await gulp.src('src/css/*.less')
    .pipe(Less())       //編譯less
    .pipe(gulp.dest(dist + '/css')) //當前對應css文件
    .pipe(Connect.reload());//更新
}

// js
// const compilerJS = Webpack(WebpackConfig);

async function js() {
    return await gulp.src('src/js/**')
    .pipe(Jshint())//檢查代碼
    // .pipe(Babel({
    // presets: ['es2015']
    // }))
    .on('error', function(err) {
        Gutil.log(Gutil.colors.red('[Error]'), err.toString());
    })
    .pipe(gulp.dest(dist + '/js')) // 拷貝
    .pipe(Connect.reload()); //更新
    
    // 使用es6+能夠單獨配置
    // compilerJS.run(function(err, stats) {
    // if(err) throw new Gutil.PluginError("webpack:js", err);
    // Gutil.log("[webpack]", stats.toString({
    // colors: true
    // }));
    // cb()
    // });
}

// image
async function image() {
    return await gulp.src('src/images/*')
    .pipe(gulp.dest(dist + '/images'));
}

// clean dir
async function clean() {
    // 不設置allowEmpty: true會報File not found with singular glob
    return await gulp.src(dist, {allowEmpty: true}).pipe(Clean());
}

// 服務器函數
async function server() {
    Connect.server({
        root:dist, //根目錄
        // ip:'192.168.11.62',//默認localhost:8080
        livereload:true, //自動更新
        port:9909, //端口
        middleware: function(connect, opt) {
            return [
                Proxy('/api', {
                    target: 'http://localhost:8080',
                    changeOrigin:true
                }),
                Proxy('/otherServer', {
                    target: 'http://IP:Port',
                    changeOrigin:true
                })
            ]
        }
    })
}

module.exports = {
    html,
    css,
    js,
    image,
    clean,
    server
}
複製代碼
  1. gulp.prod.js
const gulp = require('gulp');
// const Rename = require('gulp-rename'); // 重命名
// js
const Uglify = require('gulp-uglify');          // 壓縮js
// const Babel = require('gulp-babel');
// css
const Minifycss = require('gulp-minify-css');   // 壓縮css
const Less = require('gulp-less');              // 編譯less
const Autoprefixer = require('gulp-autoprefixer');  // 瀏覽器前綴
// html
const MinifyHtml = require("gulp-minify-html"); //壓縮html
const FileInclude = require('gulp-file-include'); // 文件模塊化
// image
const Imagemin = require('gulp-imagemin');
const Pngquant = require('imagemin-pngquant');  //png圖片壓縮插件
const Cache = require('gulp-cache'); 

const Clean = require('gulp-clean');            // 清理目錄

// md5 發版本的時候爲了不瀏覽器讀取了舊的緩存文件,須要爲其添加md5戳
const md5 = require("gulp-md5-plus");

const config = require('./config');
const { dist } = config;
// html
async function html() {
    return gulp.src('src/views/*.html')
        .pipe(FileInclude({ // HTML模板替換,具體用法見下文
            prefix: '##',
            basepath: '@file'
        }))
        // .pipe(MinifyHtml())
        .on('error', function(err) {
            console.error('Task:copy-html,', err.message);
            this.end();
        })
        .pipe(gulp.dest(dist)) // 拷貝 
}

// css
async function css() {
    return await gulp.src('src/css/**')
    .pipe(Less())       //編譯less
    .pipe(Autoprefixer({
        cascade: true, //是否美化屬性值 默認:true 像這樣:
        //-webkit-transform: rotate(45deg);
        // transform: rotate(45deg);
        remove: true //是否去掉沒必要要的前綴 默認:true
    }))
    .pipe(Minifycss({   // 壓縮css
        //類型:Boolean 默認:true [是否開啓高級優化(合併選擇器等)]
        advanced: true,
        //保留ie7及如下兼容寫法 類型:String 默認:''or'*' [啓用兼容模式; 'ie7':IE7兼容模式,'ie8':IE8兼容模式,'*':IE9+兼容模式]
        compatibility: '',
        //類型:Boolean 默認:false [是否保留換行]
        keepBreaks: false,
        //保留全部特殊前綴 當你用autoprefixer生成的瀏覽器前綴,若是不加這個參數,有可能將會刪除你的部分前綴 
        keepSpecialComments: '*'
    }))
    .pipe(gulp.dest(dist + '/css'))
    .pipe(md5(10, dist + '/*.html', {
        mappingFile: 'manifest.json',
        connector: '.' // 文件名和hash的鏈接符
    }))
    .pipe(gulp.dest(dist + '/css')) //當前對應css文件
}

// js
async function js() {
    return await gulp.src('src/js/**')
    // .pipe(Babel({
    // presets: ['es2015']
    // }))
    .pipe(Uglify()) // 壓縮js
    .pipe(gulp.dest(dist + '/js'))
    .pipe(md5(10, dist + '/*.html', {
        mappingFile: 'manifest.json',
        connector: '.'
    }))
    .pipe(gulp.dest(dist + '/js')) // 拷貝
}

// image
async function image() {
    return await gulp.src('src/images/*')
    .pipe(Cache(Imagemin({
        optimizationLevel: 5, //類型:Number 默認:3 取值範圍:0-7(優化等級)
        progressive: true, //類型:Boolean 默認:false 無損壓縮jpg圖片
        interlaced: true, //類型:Boolean 默認:false 隔行掃描gif進行渲染
        multipass: true, //類型:Boolean 默認:false 屢次優化svg直到徹底優化
        svgoPlugins: [{removeViewBox: false}],//不要移除svg的viewbox屬性
        use: [Pngquant()] //使用pngquant深度壓縮png圖片的imagemin插件
    })))
    .pipe(gulp.dest(dist + '/images'));
}


// clean dir
async function clean() {
    // 不設置allowEmpty: true會報File not found with singular glob
    return await gulp.src(dist, {allowEmpty: true}).pipe(Clean());
}



module.exports = {
    html,
    css,
    js,
    image,
    clean
}
複製代碼
  1. gulpfile.js
const gulp = require('gulp');

// 根據環境引入不一樣的配置文件
let buildConfig;
if(process.env.NODE_ENV === 'dev') {
    buildConfig = require('./build/gulp.dev');
    gulp.task('server', buildConfig.server);  // 本地服務
    
} else {
    buildConfig = require('./build/gulp.prod');
    // gulp.task('md5', gulp.series(buildConfig.md5Css, buildConfig.md5Js));
    gulp.task('clean', buildConfig.clean);    // 清理目錄 
}

gulp.task('html', buildConfig.html);      // 打包html
gulp.task('js', buildConfig.js);          // 打包js
gulp.task('css', buildConfig.css);        // 打包css
gulp.task('images', buildConfig.image);   // 打包image
gulp.task('sources', gulp.series('html', gulp.parallel('js', 'css', 'images')));


// 監聽文件變化
gulp.task('watch', async () => {
    gulp.watch('src/views/*', gulp.series('html')); // 監聽HTML變化
    gulp.watch('src/js/**', gulp.series('js')); // 監聽js變化
    gulp.watch('src/css/*', gulp.series('css')); // 監聽css變化
    gulp.watch('src/images/*', gulp.series('images')); // 監聽image變化
});

// build
if(process.env.NODE_ENV === 'dev') {
    gulp.task('dev', gulp.series('sources', 'server', 'watch'));
} else {
    gulp.task('build', gulp.series('sources'));
}


複製代碼
  1. package.json
{
  "dependencies": {
    "@babel/core": "^7.4.5",
    "babel-preset-es2015": "^6.24.1",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^6.1.0",
    "gulp-babel": "^8.0.0",
    "gulp-cache": "^1.1.2",
    "gulp-clean": "^0.4.0",
    "gulp-connect": "^5.7.0",
    "gulp-file-include": "^2.0.1",
    "gulp-imagemin": "^6.0.0",
    "gulp-jshint": "^2.1.0",
    "gulp-less": "^4.0.1",
    "gulp-md5-plus": "^1.0.3",
    "gulp-minify-css": "^1.2.4",
    "gulp-minify-html": "^1.0.6",
    "gulp-rename": "^1.4.0",
    "gulp-uglify": "^3.0.2",
    "gulp-util": "^3.0.8",
    "http-proxy-middleware": "^0.19.1",
    "http-server": "^0.11.1",
    "imagemin-pngquant": "^8.0.0",
    "jshint": "^2.10.2",
    "jsonfile": "^5.0.0",
    "webpack": "^4.35.2"
  },
  "scripts": {
    "start": "NODE_ENV=dev gulp dev",
    "build": "NODE_ENV=prod gulp clean && gulp build",
    "serve": "http-server dist/static -p 3000"
  },
  "devDependencies": {}
}

複製代碼

要想獲取項目完整源碼和demo,請移步gulp4_multi_pagesreact

最後

該腳手架任然有須要完善的地方,好比如何兼容uglify和babel,md5須要使用兩次的狀況,若是更好的解決方案,歡迎隨時交流。在腳手架選型上,也不必定非要用gulp,webpack,通常的經驗是傳統型的靜態網站適合用gulp,因爲不須要編譯es6,因此有更小的體積,固然也能夠用webpack,本文主要是給你們提供一使用gulp4搭建個腳手架的思路,但願能有所收穫。jquery

更多推薦

歡迎關注下方公衆號,獲取更多前端知識精粹學習社羣webpack

在公衆號點擊進羣,能夠加入vue學習小組,一塊兒學習前端技術;

回覆學習路徑,將獲取筆者多年從業經驗的前端學習路徑的思惟導圖git

相關文章
相關標籤/搜索