使用 Gulp 自動化構建工具快速搭建項目

爲了UED前端團隊更好的協做開發同時提升項目編碼質量,咱們須要將Web前端使用工程化方式構建;javascript

目前須要一些簡單的功能:css

1. 壓縮HTML
  2. 檢查JS
  3. 編譯SASS
  4. 壓縮圖片
  5. 壓縮CSS
  6. 壓縮JS
  7. 雪碧圖製做
  8. 靜態服務器
  9. 代理解決跨域
  10. 打包項目

目前最知名的構建工具: Gulp、Grunt、NPM + Webpack;html

grunt是前端工程化的先驅

  gulp更天然基於流的方式鏈接任務

  Webpack最年輕,擅長用於依賴管理,配置稍較複雜

PC端項目,咱們推薦使用Gulp,Gulp基於nodejs中stream,工做流,效率更好語法更天然,不須要編寫複雜的配置文件前端

演示項目java

順手一下,star或者fork

初識 gulp

簡介node

gulp是前端開發過程當中對代碼進行構建的工具,是自動化項目的構建利器;她不只能對網站資源進行優化,並且在開發過程當中不少重複的任務可以使用正確的工具自動完成;使用她,咱們不只能夠很愉快的編寫代碼,並且大大提升咱們的工做效率。android

gulp是基於Nodejs的自動任務運行器, 她能自動化地完成 javascript/coffee/sass/less/html/image/css 等文件的的測試、檢查、合併、壓縮、格式化、瀏覽器自動刷新、部署文件生成,並監聽文件在改動後重復指定的這些步驟。在實現上,她借鑑了Unix操做系統的管道(pipe)思想,前一級的輸出,直接變成後一級的輸入,使得在操做上很是簡單。經過本文,咱們將學習如何使用Gulp來改變開發流程,從而使開發更加快速高效。webpack

gulp 和 grunt 很是相似,但相比於 grunt 的頻繁 IO 操做,gulp 的流操做,能更快地更便捷地完成構建工做。ios

Gulp 中文網站git

gulp詳細入門教程

Gulp使用

Gulp是基於 Node.js的,須要要安裝 Node.js

  1. 爲了確保依賴環境正確,咱們先執行幾個簡單的命令檢查。

    node -v
    Node是一個基於Chrome JavaScript V8引擎創建的一個解釋器
    檢測Node是否已經安裝,若是正確安裝的話你會看到所安裝的Node的版本號

  2. 接下來看看npm,它是 node 的包管理工具,能夠利用它安裝 gulp 所需的包

    npm -v
    這一樣能獲得npm的版本號,裝 Node 時已經自動安裝了npm

  3. npm介紹

4. 開始安裝Gulp

npm install -g gulp
全局安裝 gulp

gulp -v
獲得gulp的版本號,確認安裝成功

基礎安裝結束

選裝cnpm

由於npm安裝插件是從國外服務器下載,受網絡影響大,這裏建議使用cnpm

npm install -g cnpm --registry=https://registry.npm.taobao.org

cmd命令小技巧

建立空文件夾  mkdir name  name是文件夾名稱
建立空文件    cd.> name.txt
刪除文件夾    rd/s/q name name是文件夾名稱 (物理刪除)
tree命令     tree /f     顯示樹形結構
             tree /f > tree.txt    導出目錄

集合功能

  • HTML組件化和壓縮

  • 編譯Sass和壓縮css

  • 檢查Javascript

  • 編譯es6語法

  • 壓縮Javascript

  • 無損壓縮圖片

  • 雪碧圖製做

  • 建立靜態服務器

  • 設置代理解決跨域

  • 打包項目且生成壓縮包

  • 結合webpack使用(選用)

項目目錄結構

│  .babelrc 
│  .eslintignore     // eslint文件過濾
│  .eslintrc         // eslint語法檢查
│  .gitignore        // git文件過濾
│  config.js         // 路徑配置文件
│  gulpfile.js       // gulp配置文件
│  package.json      // 依賴模塊json文件,npm install會安裝項目全部的依賴模塊
│  README.MD         // 項目說明
│  webpack.config.js // webpack配置文件
│  
├─build              // 發佈環境 和 預覽環境
|
├─node_modules       // 依賴模塊包目錄
│      
└─src                // 開發環境項目
    │  index.html    // 啓動默認頁面
    │  
    ├─assets         // 靜態資源
    │  ├─css         // 樣式文件
    │  ├─images      // 圖片文件
    │  ├─js          // js文件
    │  └─lib         // 插件文件
    │      
    └─page           // 頁面文件

gulpfile配置文件

經常使用的插件

gulp-sass:            sass的編譯
gulp-autoprefixer:    自動添加css前綴
gulp-minify-css:      壓縮css一行
gulp-uglify:          壓縮js代碼
gulp-notify:          加控制檯文字描述用的
gulp-clean:           清理文件
gulp-file-include:    include 文件用
gulp-imagemin:        圖片壓縮
imagemin-pngquant:    圖片無損壓縮
gulp-cache:           檢測文件是否更改
gulp-zip:             自動打包並按時間重命名
gulp-htmlmin:         壓縮html
merge-stream:         合併多個 stream
gulp-util:            打印日誌 log
gulp-plumber:         監控錯誤
gulp-babel:           編譯ES6
gulp-if:              條件判斷
gulp-sequence:        順序執行
gulp-eslint:          代碼風格檢測工具
del:                  刪除文件

製做雪碧圖 (支持同時製做多個)

規定以下

  1. 在src/assets/image中,sprite是做爲須要製做雪碧圖的文件夾

  2. 在sprite文件夾下,必須新建一個文件夾,例如user文件夾,me文件,存放你的圖標

  3. 執行gulp sprites任務

  4. 任務完成,在src/assets/image中就會生成一個icon文件夾,裏面就是生成的雪碧圖,在src/assets/css會生成雪碧圖的scss樣式文件

var srcDir = path.resolve(process.cwd(), config.sprite);
/**
 * 獲取獲取文件名字和路徑
 * @returns 
 */
var iconFolder = function() {
    var filesSrc = []; // 文件路徑
    var filesName = []; // 文件名字
    
    // 遍歷獲取文件名字和路徑
    fs.readdirSync(srcDir).forEach(function(file, i){
        var reg = /\.(png|jpg|gif|ico)/g;
        var isImg = file.match(reg);

        // 判讀是  file.indexOf('sprite') != -1
        if(!isImg){
            filesName.push(file);
            filesSrc.push(path.resolve(srcDir, file, '*.{png,jpg}'));
        }
    });
    // 返回文件名字和路徑
    return {
        'name': filesName,
        'src' : filesSrc
    };;
}

/**
 * 
 * 支持多個文件夾編譯生成雪碧圖
 * 雪碧圖製做規定要求
 * 在images文件夾下icon文件夾,新建一個文件夾就能夠
 * 
 */
var csssPrites = function() {
    var folder = iconFolder();
    var folderName = folder.name;
    var folderSrc = folder.src;

    folderSrc.forEach(function (item, i) {
        var imgName = `images/icon/icon_${folderName[i]}.png`;
        var cssName = `css/icon_${folderName[i]}.scss`;

        return gulp.src(item) // 須要合併的圖片地址
            .pipe(spritesmith({
                imgName: imgName, // 保存合併後圖片的地址
                cssName: cssName, // 保存合併後對於css樣式的地址
                padding: 10,  // 合併時兩個圖片的間距
                algorithm: 'binary-tree', // 註釋1
                cssTemplate: './cssTemplate.tpl' // 模板
                // cssTemplate: function (data) {
                //     var arr=[];
                //     data.sprites.forEach(function (sprite) {
                //         arr.push(".icon-"+sprite.name+
                //         "{" +
                //         "background-image: url('"+sprite.escaped_image+"');"+
                //         "background-position: "+sprite.px.offset_x+"px "+sprite.px.offset_y+"px;"+
                //         "width:"+sprite.px.width+";"+
                //         "height:"+sprite.px.height+";"+
                //         "}\n");
                //     });
                //     return arr.join("");
                // }
            }))
            .pipe(gulp.dest('src/assets/'));
    })
}



/* 生成雪碧圖 */ 
gulp.task('sprites', function () {
    // 執行任務
    csssPrites();
});

gulpfile.js內容

/**
 * gulp 自動化構建工具
 * gulpfile.js 配置文件
 * 
 */

var fs            = require('fs');
var path          = require('path');

var gulp          = require('gulp');
var sass          = require('gulp-sass'); // sass的編譯
var autoprefixer  = require('gulp-autoprefixer'); // 自動添加css前綴
var minifycss     = require('gulp-minify-css'); // 壓縮css一行
var uglify        = require('gulp-uglify'); // 壓縮js代碼
var notify        = require('gulp-notify'); // 加控制檯文字描述用的
var clean         = require('gulp-clean'); // 清理文件
var fileinclude   = require('gulp-file-include'); // include 文件用
var imagemin      = require('gulp-imagemin'); // 圖片壓縮
var pngquant      = require('imagemin-pngquant'); // 圖片無損壓縮
var cache         = require('gulp-cache'); // 檢測文件是否更改
var zip           = require('gulp-zip'); // 自動打包並按時間重命名
var htmlmin       = require('gulp-htmlmin'); // 壓縮html
var mergeStream   = require('merge-stream'); // 合併多個 stream
var gutil         = require('gulp-util'); // 打印日誌 log
var plumber       = require('gulp-plumber'); // 監控錯誤
var babel         = require('gulp-babel'); // 編譯ES6
var gulpif        = require('gulp-if'); // 條件判斷
var minimist      = require('minimist');
var gulpSequence  = require('gulp-sequence'); // 順序執行
var eslint        = require('gulp-eslint'); // 代碼風格檢測工具
var del           = require('del'); // 刪除文件

var spritesmith   = require('gulp.spritesmith'); // 生成雪碧圖 https://github.com/twolfson/gulp.spritesmith

// 結合webpack
var webpack       = require('gulp-webpack');
var webpackConfig = require('./webpack.config.js');

// 靜態服務器和代理請求
var url           = require('url');
var proxy         = require('proxy-middleware');
var browserSync   = require('browser-sync');
var reload        = browserSync.reload;

// 代理請求 / 端口設置 / 編譯路徑
var config = require('./config.js');

// 區分開發和生產環境
var knownOptions = {
    string: 'env',
    default: {
        env: process.env.NODE_ENV || 'development'
    }
};
var options = minimist(process.argv.slice(2), knownOptions);

/**
 * 開發環境和生產環境
 * 先清空原先文件夾,在執行編譯或者打包
 * 
 * @param {any} cb 回調
 */
var cnEnvironment = function(cb) {

   // 先執行清空文件夾內容
   del(config.rootBuild).then(paths => {
        // 通知信息
        gutil.log(gutil.colors.green('Message:Delete complete!'));
        gutil.log(gutil.colors.green('Message:Deleted files and folders:', paths.join('\n')));
        
        // 執行項目打包
        gulpSequence([
            'htmlmin', 'cssmin', 'images', 'jsmin', 'libmin'
        ], function() {

            gutil.log(gutil.colors.green('Message:Compile finished!'));
            // 執行回調
            cb && cb();

        });
    });
}

/**
 * 錯誤輸出
 * 
 * @param {any} error 
 */
var onError = function(error){
    var title = error.plugin + ' ' + error.name;
    var msg = error.message;
    var errContent = msg.replace(/\n/g, '\\A '); // replace to `\A`, `\n` is not allowed in css content

    // system notification
    notify.onError({
        title: title,
        message: errContent, 
        sound: true
    })(error);
    
    // prevent gulp process exit
    this.emit('end');
};


var srcDir = path.resolve(process.cwd(), config.sprite);
/**
 * 獲取獲取文件名字和路徑
 * @returns 
 */
var iconFolder = function() {
    var filesSrc = []; // 文件路徑
    var filesName = []; // 文件名字
    
    // 遍歷獲取文件名字和路徑
    fs.readdirSync(srcDir).forEach(function(file, i){
        var reg = /\.(png|jpg|gif|ico)/g;
        var isImg = file.match(reg);

        // 判讀是  file.indexOf('sprite') != -1
        if(!isImg){
            filesName.push(file);
            filesSrc.push(path.resolve(srcDir, file, '*.{png,jpg}'));
        }
    });
    // 返回文件名字和路徑
    return {
        'name': filesName,
        'src' : filesSrc
    };;
}

/**
 * 
 * 支持多個文件夾編譯生成雪碧圖
 * 雪碧圖製做規定要求
 * 在images文件夾下icon文件夾,新建一個文件夾就能夠
 * 
 */
var csssPrites = function() {
    var folder = iconFolder();
    var folderName = folder.name;
    var folderSrc = folder.src;

    folderSrc.forEach(function (item, i) {
        var imgName = `images/icon/icon_${folderName[i]}.png`;
        var cssName = `css/icon_${folderName[i]}.scss`;

        return gulp.src(item) // 須要合併的圖片地址
            .pipe(spritesmith({
                imgName: imgName, // 保存合併後圖片的地址
                cssName: cssName, // 保存合併後對於css樣式的地址
                padding: 10,  // 合併時兩個圖片的間距
                algorithm: 'binary-tree', // 註釋1
                cssTemplate: './cssTemplate.tpl' // 模板
                // cssTemplate: function (data) {
                //     var arr=[];
                //     data.sprites.forEach(function (sprite) {
                //         arr.push(".icon-"+sprite.name+
                //         "{" +
                //         "background-image: url('"+sprite.escaped_image+"');"+
                //         "background-position: "+sprite.px.offset_x+"px "+sprite.px.offset_y+"px;"+
                //         "width:"+sprite.px.width+";"+
                //         "height:"+sprite.px.height+";"+
                //         "}\n");
                //     });
                //     return arr.join("");
                // }
            }))
            .pipe(gulp.dest('src/assets/'));
    })
}


/* html 打包*/
gulp.task('htmlmin', function() {
    var optionsSet = {
        removeComments: true, // 清除HTML註釋
        collapseWhitespace: true, // 壓縮HTML
        collapseBooleanAttributes: true, // 省略布爾屬性的值 <input checked="true"/> ==> <input />
        removeEmptyAttributes: false, // 刪除全部空格做屬性值 <input id="" /> ==> <input />
        removeScriptTypeAttributes: false, // 刪除<script>的type="text/javascript"
        removeStyleLinkTypeAttributes: false, // 刪除<style>和<link>的type="text/css"
        minifyJS: true, // 壓縮頁面JS
        minifyCSS: true // 壓縮頁面CSS
    };

    return gulp
        .src([config.dev.html, '!*.tpl'], { base: config.rootDev })
        .pipe(plumber(onError))
        .pipe(fileinclude({
            prefix: '@@', 
            basepath: '@file'
         }))
        .pipe(gulpif(options.env === 'production', htmlmin(optionsSet)))
        .pipe(gulp.dest(config.build.html))
        .pipe(reload({ stream: true }));
});

/* css 壓縮 */
gulp.task('cssmin', function() {
    var AUTOPREFIXER_BROWSERS = [
        'last 6 version',
        'ie >= 6',
        'ie_mob >= 10',
        'ff >= 30',
        'chrome >= 34',
        'safari >= 7',
        'opera >= 23',
        'ios >= 7',
        'android >= 4.0',
        'bb >= 10'
    ];

    return gulp
        .src(config.dev.css)
        .pipe(plumber(onError))
        .pipe(sass())
        // .pipe(sass().on('error', sass.logError))
        .pipe(autoprefixer(AUTOPREFIXER_BROWSERS))
        .pipe(gulpif(options.env === 'production', minifycss()))
        .pipe(gulp.dest(config.build.css))
        .pipe(reload({ stream: true }));
});

/* eslint 語法檢查 */
gulp.task('eslint', function() {
   return gulp
    .src([config.dev.js, '!node_modules/**'])
    .pipe(plumber(onError))
    .pipe(eslint({ configFle: './.eslintrc' }))
    .pipe(eslint.format())
    .pipe(eslint.failAfterError());
}) 

/* js 壓縮 */
gulp.task('jsmin', ['eslint'], function() {
    var jsmin = gulp
        .src([config.dev.js, '!node_modules/**'])
        .pipe(plumber(onError))
        .pipe(babel({
            presets: ['es2015'],
            plugins: [
                // es2015 - based off of v6.3.13
                // https://github.com/babel/babel/tree/master/packages
                require('babel-plugin-transform-es2015-object-super'),
                require('babel-plugin-syntax-export-extensions'),
                require('babel-plugin-transform-object-assign'),
                require('babel-plugin-transform-es3-member-expression-literals'),
                require('babel-plugin-transform-es3-property-literals'),
                [require('babel-plugin-transform-es2015-classes'), { loose: true }],
                [require('babel-plugin-transform-regenerator'), { async: false, asyncGenerators: false }],
            ]
        }))
        // .pipe(webpack( webpackConfig ))
        .pipe(gulpif(options.env === 'production', uglify())) // 僅在生產環境時候進行壓縮
        .pipe(gulp.dest(config.build.js))
        .pipe(reload({ stream: true }));

    return mergeStream(jsmin);
});

/* js 插件 */
gulp.task('libmin', function() {
    // lib 插件
    return gulp
        .src(config.dev.lib)
        .pipe(plumber(onError))
        .pipe(gulp.dest(config.build.lib))
        .pipe(reload({ stream: true }));
});

/* webpack */
gulp.task('webpack', function() {
    webpackConfig.refreshEntry();
    return gulp
        .src([config.dev.js])
        .pipe(webpack(webpackConfig))
        .pipe(gulp.dest(config.build.js));
});

/* images 壓縮 */
gulp.task('images', () => {
    return gulp
        .src(config.dev.image)
        .pipe(plumber(onError))
        .pipe(cache(imagemin({
            progressive: true,
            svgoPlugins: [{
                removeViewBox: false
            }],
            use: [pngquant()]
        })))
        .pipe(gulp.dest(config.build.image))
        .pipe(reload({ stream: true }));
});

/* 生成雪碧圖 */ 
gulp.task('sprites', function () {
    // 執行任務
    csssPrites();
});


/* clean 清除*/
gulp.task('clean', function() {
    // return gulp
    //     .src(config.rootBuild, { read: false })
    //     .pipe(clean())
    //     .pipe(notify({ message: 'Clean task complete' }));
    del(config.rootBuild).then(paths => {
        console.log('Deleted files and folders:\n', paths.join('\n'));
    });
});

/* zip 壓縮包 */
gulp.task('zip', function() {
    /**
     * 補零
     * @param {any} i
     * @returns
     */
    function checkTime(i) {
        if (i < 10) { i = '0' + i; }
        return i;
    }

    var d = new Date();
    var year = d.getFullYear();
    var month = checkTime(d.getMonth() + 1);
    var day = checkTime(d.getDate());
    var hour = checkTime(d.getHours());
    var minute = checkTime(d.getMinutes());

    var time = String(year) + String(month) + String(day) + String(hour) + String(minute);
    var build = 'build-' + time + '.zip';
    
    return gulp
        .src(config.build.zip)
        .pipe(plumber(onError))
        .pipe(zip(build))
        .pipe(gulp.dest(config.rootZip))
        .pipe(notify({ message: 'Zip task complete' }));
});

/* watch 文件 */
gulp.task('watch', function() {
    // 看守全部.tpl文件
    gulp.watch(config.dev.tpl, ['htmlmin'])
    // 看守全部.html文件
    gulp.watch(config.dev.html, ['htmlmin'])
    // 看守全部.scss文件
    gulp.watch(config.dev.scss, ['cssmin'])
    // 看守全部.js文件
    gulp.watch(config.dev.js, ['jsmin'])
    // 看守全部js插件文件
    gulp.watch(config.dev.lib, ['libmin'])
    // 看守全部圖片文件
    gulp.watch(config.dev.image, ['images'])
    // 看守全部雪碧圖
    gulp.watch(config.dev.image, ['sprites'])
});

/* server 服務器 */
gulp.task('server', function() {

   cnEnvironment(function(){
        gutil.log(gutil.colors.green('啓動本地服務器'));
        gutil.log(gutil.colors.green('代理請求地址:' + config.proxyTable.target));
        gutil.log(gutil.colors.green('代理請求項目:' + config.proxyTable.inner));

        var proxyOptions = url.parse(config.proxyTable.target + config.proxyTable.inner);
        proxyOptions.route = config.proxyTable.inner;

        browserSync.init({ // 初始化 BrowserSync
            injectChanges: true, // 插入更改
            files: [
                '*.html', '*.css', '*.js'
            ], // 監聽文件類型來自動刷新
            server: {
                baseDir: config.rootBuild, // 目錄位置
                middleware: [proxy(proxyOptions)] // 代理設置
            },
            ghostMode: { // 是否開啓多端同步
                click: true, // 同步點擊
                scroll: true // 同步滾動
            },
            logPrefix: 'browserSync in gulp', // 再控制檯打印前綴
            browser: ["chrome"], //運行後自動打開的;瀏覽器 (不填默認則是系統設置的默認瀏覽器)
            open: true, //       自動打開瀏覽器
            port: config.port   // 使用端口
        });

        // 監聽watch
        gulp.start('watch');
    });

});

/* build 打包項目 */
gulp.task('build', function() {
    cnEnvironment(function(){
        gulp.start('zip', function(){
           gutil.log(gutil.colors.green('Message:Project package is complete'));
        });
    })
});

/* 任務命令 */
gulp.task('default', function() {
    gutil.log(gutil.colors.green('開發環境:          npm run dev 或者 gulp server'));
    gutil.log(gutil.colors.green('打包項目:          npm run build 或者 gulp build --env production'));
    gutil.log(gutil.colors.green('刪除文件夾:        gulp clean'));
    gutil.log(gutil.colors.green('編譯js代碼:        gulp jsmin'));
    gutil.log(gutil.colors.green('編譯css代碼:       gulp cssmin'));
    gutil.log(gutil.colors.green('編譯html代碼:      gulp htmlmin'));
    gutil.log(gutil.colors.green('編譯圖片壓縮:      gulp images'));
    gutil.log(gutil.colors.green('監聽全部文件:      gulp watch'));
});

項目任務命令

開發環境:          npm run dev 或者 gulp server
打包項目:          npm run build 或者 gulp build --env production
刪除文件夾:        gulp clean
編譯js代碼:        gulp jsmin
編譯css代碼:       gulp cssmin
編譯html代碼:      gulp htmlmin
編譯圖片壓縮:      gulp images
監聽全部文件:      gulp watch

ESLint規範代碼

ESLint

爲何使用ESLint
使用使用 ESLint 工具和代碼風格檢測工具,則能夠輔助編碼規範執行,有效控制代碼質量。

ESLint 詳盡使用參見 官方文檔中文文檔

項目地址

git clone git@github.com:vincentSea/gulp-demo.git
相關文章
相關標籤/搜索