1. 程式人生 > >gulp+webpack工具整合簡介

gulp+webpack工具整合簡介

webpack簡介

Webpack 是一個模組打包器。它將根據模組的依賴關係進行靜態分析,然後將這些模組按照指定的規則生成對應的靜態資源。
這裡寫圖片描述

webpack特點

  • Webpack
    有兩種組織模組依賴的方式,同步和非同步。非同步依賴作為分割點,形成一個新的塊。在優化了依賴樹後,每一個非同步區塊都作為一個檔案被打包。

  • Webpack 本身只能處理原生的 JavaScript 模組,但是 loader 轉換器可以將各種型別的資源轉換成 JavaScript
    模組。這樣,任何資源都可以成為 Webpack 可以處理的模組。

  • Webpack 有一個智慧解析器,幾乎可以處理任何第三方庫,無論它們的模組形式是 CommonJS、 AMD 還是普通的 JS
    檔案。甚至在載入依賴的時候,允許使用動態表示式 require(“./templates/” + name + “.jade”)。

  • Webpack 還有一個功能豐富的外掛系統。大多數內容功能都是基於這個外掛系統執行的,還可以開發和使用開源的 Webpack
    外掛,來滿足各式各樣的需求。

  • Webpack 使用非同步 I/O 和多級快取提高執行效率,這使得 Webpack 能夠以令人難以置信的速度快速增量編譯。

webpack配置及使用

首先需要安裝node環境和npm包管理工具,不知道的可以自行百度。基本環境好了後需要安裝webpack外掛:

npm install webpack --save-dev

接下來就是寫一個demo驗證了。建立一個靜態頁面 index.html 和一個 JS 入口檔案 entry.js

<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <script src="bundle.js"></script>
</body>
</html>

entry.js

document.write('It works.')

編譯 entry.js 並打包到 bundle.js:

webpack entry.js bundle.js

然後在瀏覽器上就可以看到輸出結果了“It works”。

Loader

Webpack 本身只能處理 JavaScript 模組,如果要處理其他型別的檔案,就需要使用 loader 進行轉換。Loader 可以理解為是模組和資源的轉換器,它本身是一個函式,接受原始檔作為引數,返回轉換的結果。這樣,我們就可以通過 require 來載入任何型別的模組或檔案,比如 CoffeeScript、 JSX、 LESS 或圖片。首先安裝loader:

npm install css-loader style-loader

然後我們寫程式碼的時候通過require函式引入進來就可以了,具體使用請自行搜尋。

gulp 簡介

gulp是前端開發過程中對程式碼進行構建的工具,是自動化專案的構建利器;她不僅能對網站資源進行優化,而且在開發過程中很多重複的任務能夠使用正確的工具自動完成;使用它,我們不僅可以很愉快的編寫程式碼,而且大大提高我們的工作效率。
gulp是基於Nodejs的自動任務執行器, 她能自動化地完成 javascript/coffee/sass/less/html/image/css 等檔案的的測試、檢查、合併、壓縮、格式化、瀏覽器自動重新整理、部署檔案生成,並監聽檔案在改動後重復指定的這些步驟。在實現上,她借鑑了Unix作業系統的管道(pipe)思想,前一級的輸出,直接變成後一級的輸入,使得在操作上非常簡單。通過本文,我們將學習如何使用Gulp來改變開發流程,從而使開發更加快速高效。
gulp 和 grunt 非常類似,但相比於 grunt 的頻繁 IO 操作,gulp 的流操作,能更快地更便捷地完成構建工作。

gulp安裝使用

1、安裝nodejs

1.1、說明:gulp是基於nodejs,理所當然需要安裝nodejs;
1.2、安裝:開啟nodejs官網,點選碩大的綠色Download按鈕,它會根據系統資訊選擇對應版本(.msi檔案)。然後一路next。

2、使用命令列(如果你熟悉命令列,可以直接跳到第3步)

2.1、說明:什麼是命令列?命令列在OSX是終端(Terminal),在windows是命令提示符(Command Prompt);
2.2、注:之後操作都是在windows系統下;
2.3、簡單介紹gulp在使用過程中常用命令,開啟命令提示符執行下列命令(開啟方式:window + r 輸入cmd回車):
node -v檢視安裝的nodejs版本,出現版本號,說明剛剛已正確安裝nodejs。PS:未能出現版本號,請嘗試登出電腦重試;
npm -v檢視npm的版本號,npm是在安裝nodejs時一同安裝的nodejs包管理器,那它有什麼用呢?稍後解釋;
cd定位到目錄,用法:cd + 路徑 ;
dir列出檔案列表;
cls清空命令提示符視窗內容。
這裡寫圖片描述

3、npm介紹

在這裡直接略過,npm詳解

4、選裝cnpm

4.1、說明:因為npm安裝外掛是從國外伺服器下載,受網路影響大,可能出現異常,如果npm的伺服器在中國就好了,所以我們樂於分享的淘寶團隊幹了這事。32個!來自官網:“這是一個完整 npmjs.org 映象,你可以用此代替官方版本(只讀),同步頻率目前為 10分鐘 一次以保證儘量與官方服務同步。”;
4.2、官方網址:http://npm.taobao.org
4.3、安裝:命令提示符執行npm install cnpm -g –registry=https://registry.npm.taobao.org; 注意:安裝完後最好檢視其版本號cnpm -v或關閉命令提示符重新開啟,安裝完直接使用有可能會出現錯誤;
注:cnpm跟npm用法完全一致,只是在執行命令時將npm改為cnpm(以下操作將以cnpm代替npm)。

5、全域性安裝gulp

5.1、說明:全域性安裝gulp目的是為了通過她執行gulp任務;
5.2、安裝:命令提示符執行cnpm install gulp -g;
5.3、檢視是否正確安裝:命令提示符執行gulp -v,出現版本號即為正確安裝。

6、新建package.json檔案

這個也忽略,相當於maven組織專案的。

7、本地安裝gulp外掛

7.1、安裝:定位目錄命令後提示符執行cnpm install –save-dev;
7.2、本示例以gulp-less為例(編譯less檔案),命令提示符執行cnpm install gulp-less –save-dev;
這裡寫圖片描述
7.3、將會安裝在node_modules的gulp-less目錄下,該目錄下有一個gulp-less的使用幫助文件README.md;
7.4、為了能正常使用,我們還得本地安裝gulp:cnpm install gulp –save-dev;
PS:細心的你可能會發現,我們全域性安裝了gulp,專案也安裝了gulp,全域性安裝gulp是為了執行gulp任務,本地安裝gulp則是為了呼叫gulp外掛的功能。

8、新建gulpfile.js檔案(重要)

8.1、說明:gulpfile.js是gulp專案的配置檔案,是位於專案根目錄的普通js檔案(其實將gulpfile.js放入其他資料夾下亦可)。
8.2、它大概是這樣一個js檔案(更多外掛配置請檢視這裡
來看一個例子:

//匯入工具包 require('node_modules裡對應模組')
var gulp = require('gulp'), //本地安裝gulp所用到的地方
    less = require('gulp-less');

//定義一個testLess任務(自定義任務名稱)
gulp.task('testLess', function () {
    gulp.src('src/less/index.less') //該任務針對的檔案
        .pipe(less()) //該任務呼叫的模組
        .pipe(gulp.dest('src/css')); //將會在src/css下生成index.css
});

gulp.task('default',['testLess', 'elseTask']); //定義預設任務 elseTask為其他任務,該示例沒有定義elseTask任務

//gulp.task(name[, deps], fn) 定義任務  name:任務名稱 deps:依賴任務名稱 fn:回撥函式
//gulp.src(globs[, options]) 執行任務處理的檔案  globs:處理的檔案路徑(字串或者字串陣列) 
//gulp.dest(path[, options]) 處理完後文件生成路徑

9、執行gulp

9.1、說明:命令提示符執行gulp 任務名稱;
9.2、編譯less:命令提示符執行gulp testLess;
9.3、當執行gulp default或gulp將會呼叫default任務裡的所有任務[‘testLess’,’elseTask’]。

10、使用webstorm執行gulp任務

10.1、說明:使用webstorm視覺化執行gulp任務;
10.2、使用方法:將專案匯入webstorm,右鍵gulpfile.js 選擇”Show Gulp Tasks”開啟Gulp視窗,若出現”No task found”,選擇右鍵”Reload tasks”,雙擊執行即可。
這裡寫圖片描述

webpack&gulp整合

接下來要到重點了,gulp和webpack各有各的優點,那麼我們整合這兩個工具呢?

建立webpack物件

var webpack = require("webpack")

建立gulp任務

//gulp --product
gulp.task('default', function() {
  isProduct = argv.product;
  isWatch = !isProduct;
  console.log('正在處理:' + (isProduct ? '線上' : '本地') + '環境');
  fse.emptydirSync('./dist');
  gulp.start('webpack');
});

ps:本地開發(命令:gulp)和線上(命令:gulp –product)部署gulp命令分開,本地會做檔案更改的監聽,並且不會壓縮。

webpack任務配置

入口檔案配置:

entry: {
            index: './index.js'
            common: [
                'jquery',
                'understore',
                'template',
                'store'
            ]
        }

該引數主要是配置入口檔案,打包出來的檔案就是以裡面的key作為名字的檔案(即bundle)。
輸出路徑配置:

output: {
            path: './dist/',
            filename: '[name].js',
            publicPath: ''
        }

別名配置:

resolve: {
            alias: {
                jquery: path.resolve('../global/lib/jquery.js'),
                template: path.resolve('../global/lib/template.js'),
                understore: path.resolve('../global/lib/underscore.js'),
                store: path.resolve('../global/lib/store.js')
            }
        }

配置完別名,就可以用別名來進行require了,不管是在webpack配置中,還是在js程式碼中。

webpack外掛配置

webpack內部全域性變數:

new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    template: 'template',
    store: 'store',
    _: 'understore'
})

配置完全域性的變數,在js中就直接可以用,不需要再require。該全域性不是掛載到window物件上,只對webpack打包出來的js有用。
合併相同的模組:

new webpack.optimize.DedupePlugin()

說明:該外掛會把相同的模組合併,不推薦使用,最好自己做到模組唯一性,如果打出來兩個相同的模組,說明你的程式碼潛藏出錯風險。
css抽離外掛:

new ExtractTextPlugin("[name].css")

該外掛會把css抽離出來作為單獨的css檔案進行打包,如果不使用該外掛,會把css打包到js中,然後以style標籤的形式,把css作為內聯樣式使用。
公共檔案外掛:

new webpack.optimize.CommonsChunkPlugin('common', 'common.js')

該外掛會把公共的模組進行抽離到公共的js模組裡面,防止js重複引用打包。
壓縮外掛:

new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        },
        mangle: {
            except: ['$', 'm', 'webpackJsonpCallback']
        }
    })

該外掛會把js進行壓縮,在線上環境進行使用。
注:mangle該關鍵詞不需要混淆,因為在我們實踐過程中,壓縮後的程式碼有很多地方報錯。
由於webpack會把所有的js都打包到一個js檔案中,這樣就不方便開發人員debug,故需要進行sourcemap的配置:

devtool: (isProduct ? false : 'source-map')

載入器loader

js載入器:

{
    test: /\.js[x]?$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    query: {
        compact: false
    }
}

js使用babel來進行載入,這樣就可以使用一些es6的特性來開發,IE8下面使用如下方案來進行解決轉換後的程式碼不相容問題。

ps:解決IE8 babel 轉換:我們把shim.js & sham.js,自己合併到一起吧。
css、less、sass載入器:

{
    test: /\.css$/,
    loader: ExtractTextPlugin.extract('style-loader', 'css-loader?-convertValues')
}, {
    test: /\.less$/,
    loader: ExtractTextPlugin.extract('style-loader', 'css-loader?-convertValues!less-loader')
}, {
    test: /\.scss$/,
    loader: ExtractTextPlugin.extract('style-loader', 'css-loader?-convertValues!sass-loader')
}

ExtractTextPlugin是抽離css的外掛。css-loader是處理css,會把url(xxx.png)處理成require,然後通過對應字尾名的其他載入器進行處理。==如果是線上環境,css會進行壓縮,很多配置要進行調整如果直接採用預設的,轉換後的css會出現一些問題,常見問題如:

  • 問題一,壓縮後字型檔案不能處理。解決方法:把iconfont.css裡面的註釋刪除掉即可
  • 問題二,壓縮後的px單位會轉換成pc,pt。解決方法:在載入器裡面加上-convertValues引數。
  • 問題三,css require的優先順序問題,按照常理是後require的css是後加載。但是在實踐過程中,(1)通過import引入的js模組中引入的css是優先於require引入的js模組中引入的css;(2)js中require的css名稱如果和js所在模組的資料夾名字相同即使部分相同的話,優先順序也會提高。
  • 關於更多的問題請參照Loader配置。
    圖片字型檔案載入器,file-loader:
{
    test: /\.(png|jpg|gif|woff|woff2|ttf|eot|svg)$/,
    loader: "file-loader?name=[name]_[sha512:hash:base64:7].[ext]"
}

對一些圖片和字型資源進行載入,我們會把相關檔案抽離出來進行名字加上hash值的前7位做了處理後的名字。
html載入器;

{
    test: /\.html/,
    loader: "html-loader"
}

gulp指令碼檔案(重要)

gulpfile.js

var gulp = require('gulp'),
    argv = require('yargs').argv,
    fs = require('fs'),
    fse = require('fs-extra'),
    path = require('path'),
    util = require('util');
var webpack = require("webpack"),
    ExtractTextPlugin = require("extract-text-webpack-plugin");

var isWatch = true;
var isProduct = false;
var project = 'iwjw-pc';
//gulp --product
gulp.task('default', function() {
    isProduct = argv.product;
    isWatch = !isProduct;

    console.log('正在處理:' + (isProduct ? '線上' : '本地') + '環境');

    fse.emptydirSync('./dist');

    gulp.start('webpack');
});

//webpack靜態處理
gulp.task('webpack', function(callback) {
    var minfy = [];
    isProduct && minfy.push(new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        },
        mangle: {
            except: ['$', 'm', 'webpackJsonpCallback']
        }
    }));
    //webpack配置檔案
    var config = {
        watch: isWatch,
        entry: {
            index: './index.js',
            common: [
                'jquery',
                'understore',
                'template',
                'store'
            ]
        },
        debug: true,

        devtool: (isProduct ? false : 'source-map'),

        output: {
            path: './dist/',
            filename: '[name].js',
            publicPath: ''
        },

        resolve: {
            alias: {
                jquery: path.resolve('../global/lib/jquery.js'),
                template: path.resolve('../global/lib/template.js'),
                understore: path.resolve('../global/lib/underscore.js'),
                store: path.resolve('../global/lib/store.js')
            }
        },


        plugins: [
            new webpack.ProvidePlugin({
                $: 'jquery',
                jQuery: 'jquery',
                template: 'template',
                store: 'store',
                _: 'understore'
            }),
            new webpack.optimize.DedupePlugin(),
            new ExtractTextPlugin("[name].css"),
            new webpack.optimize.CommonsChunkPlugin('common', 'common.js')
        ].concat(minfy),
        module: {
            loaders: [{
                test: /\.js[x]?$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    compact: false
                }
            }, {
                test: /\.css$/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader?-convertValues')
            }, {
                test: /\.less$/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader?-convertValues!less-loader')
            }, {
                test: /\.scss$/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader?-convertValues!sass-loader')
            }, {
                test: /\.(png|jpg|gif|woff|woff2|ttf|eot|svg)$/,
                loader: "file-loader?name=[name]_[sha512:hash:base64:7].[ext]"
            }, {
                test: /\.html/,
                loader: "html-loader"
            }]
        }
    }; 
    webpack(config, function(err, stats) {
        console.log(stats.toString());
    });
});
gulp.task('sham', function(){
    gulp.src('../global/lib/es5-shim-sham.js').pipe(gulp.dest('./dist'));
})

pacakge.json(根據專案情況配置)

{
"name": "iwfe",
"version": "0.0.0",
"private": true,
"scripts": {
  "start": "node ./bin/server"
},
"dependencies": {
  "babel-loader": "^5.3.2",
  "bundle-loader": "^0.5.4",
  "css-loader": "^0.16.0",
  "exports-loader": "^0.6.2",
  "extract-text-webpack-plugin": "^0.8.2",
  "file-loader": "~0.8.4",
  "fs-extra": "*",
  "html-loader": "^0.4.0",
  "imports-loader": "^0.6.5",
  "jquery": "~2.1.4",
  "sass-loader": "^3.1.2",
  "source-map-loader": "^0.1.5",
  "style-loader": "~0.12.3",
  "through2": "*",
  "url-loader": "~0.5.6",
  "webpack": "*",
  "webpack-dev-server": "^1.10.1",
  "yargs": "*",
  "gulp-zip": "*"
},
"devDependencies": {
  "gulp": "*",
  "gulp-util": "*"
},
"description": "前端靜態庫",
"repository": {
  "type": "git",
  "url": "xxx"
},
"author": "jade",
"license": "ISC"
}