前端自動化構建工具Webpack1.x開發模式使用指南

Webpack

Webpack是時下最流行的模塊打包器
它的主要任務就是將各種格式的JavaScript代碼,甚至是靜態文件
進行分析、壓縮、合併、打包,最後生成瀏覽器支持的代碼

特點:

  • 代碼拆分方案:webpack可以將應用代碼拆分成多個塊,每個塊包含一個或多個模塊,塊可以按需異步加載,極大提升大規模單頁應用的初始加載速度
  • 智能的靜態分析:webpack的智能解析器幾乎可以處理任何第三方庫
  • Loader加載器:webpack只能處理原生js模塊,但是loader可以將各種資源轉換爲js模塊
  • plugin插件:webpack有功能豐富的插件系統,滿足各種開發需求
  • 快速運行:webpack 使用異步 I/O 和多級緩存提高運行效率,使得它能夠快速增量編譯

不過它也有自己的缺點
配置過於複雜;如果使用不當(比如過度優化),會產生難以預料的問題
但是瑕不掩瑜,它仍是優秀的前端自動化構建工具

安裝

webpack是使用Node.js開發的工具,可通過npm(Node.js的包管理工具)進行安裝
我們可以去Node.js官網下載

我的電腦是Windows×64位操作系統,那麼我就點擊Windows Installer 64-bit下載

下載安裝完畢後我們可以打開命令提示符檢測一下
使用Win+R快捷鍵,輸入cmd打開命令行
輸入:node -v

顯示了node版本6.2
這就證明我們安裝成功了

準備

下面我們要做的是在全局安裝webpack
因爲大部分情況我們需要已命令行工具的形式使用webpack,所以安裝在全局更方便(-g)
輸入:npm install webpack -g
安裝完畢後我們需要手動構建項目文件

現在我在桌面上新建了一個項目文件夾demo

內部很簡單,一個網頁index.html和一個用來放資源的文件夾


下面我們還要配置package.json項目文件
一個很簡單的方式就是利用npm自動創建
現在將命令符引導到我們項目的根路徑

(如果下載了git,可以直接在桌面git bash)

輸入:npm init

(最好將webpack也作爲項目依賴安裝到項目中,而不限於全局,官方推薦,方便引用 npm install webpack

輸入這個命令後,會讓你輸入項目名稱、項目描述等等
由於我們不需要發佈,這些都不重要,所以我們直接回車默認就可以了

完畢後我們就會發現文件根目錄下多了一個文件

有了package.json,webpack才能管理好模塊之間的依賴關係

文件

下面做一個十分簡單的頁面

/* greet.css */
p {
    font-size: 50px;
    color: orangered;
}

css中我們爲p標籤添加樣式

/* greet.js */
require('../css/greet.css');
var p = document.createElement('p');
p.innerHTML = 'hello world!';
module.exports = p;

greet.js中引入模塊,創建p標籤

/*entry.js*/
var p = require('./greet');
document.appendChild(p);

入口文件中將p標籤添加到頁面

<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script src="./out/index.js"></script>
</body>
</html>

html值只是簡單的引入腳本
不過此時它還不存在
它是我們要使用webpack將會生成的文件


上面的依賴關係用圖片來展示就是個這個樣子地

配置文件

僅僅做了這些還不夠
我們還有一個最重要的步驟沒有完成——編寫webpack配置文件
在項目根目錄下創建一個文件webpack.config.js

/* webpack.config.js */
module.exports = {
    entry: './src/js/entry.js',
    output: {
        path: './out',
        publicPath: './out',
        filename: 'index.js'
    },
    module: {
        loaders: [
            {test: /\.js$/, loader: 'babel'},
            {test: /\.css$/, loader: 'style!css'},
            {test: /\.(jpg|png|gif|svg)$/, loader: 'url?limit=8192}
        ]
    }
}

entry是項目的入口文件
output是構建項目輸出結果的描述,本身爲對象

  • path:輸出目錄
  • publicPath:輸出目錄對應外部路徑(從瀏覽器中訪問)
  • filename:輸出文件名

publicPath其實是很重要的配置,它表示構建結果最終被真正訪問的路徑
但在我們的demo中,直接通過相對路徑訪問靜態資源,不涉及打包上線CDN,所以沒那麼重要

加載器

要想使用loaders就必須安裝
爲了讓我們不用在所有文件中都重複的引用加載器
我們還需要在webpack.config.js下的module中添加配置信息
形式參考上面的代碼
它有以下配置選線:

  • test(必選):匹配loaders處理文件拓展名的正則表達式
  • loader(必選):加載器名稱
  • include/exclude(可選):手動添加必須處理的文件或屏蔽不需要處理的文件;
  • query(可選):爲加載器提供額外設置選項

babel-loader

Babel是一種多用途的編譯器,通過它我們可以:

  • 使用瀏覽器沒有完全支持的新標準(ES6、ES7)
  • 使用基於JavaScript拓展的語言(React JSX、CoffeeScript…)

Babel是幾個模塊化的包,其核心功能位於babel-core的npm包中
每一個功能的拓展包,都需要我們單獨下載
比如解析Es6的babel-preset-es2015包
解析JSX的babel-preset-react包
我們可以在命令行中統一下載它們 npm install + 要下載的包(多個包用空格隔開)
輸入:npm install babel-core babel-loader babel-preset-es2015 babel-preset-react --save-dev
(我們的demo中可能用不到這些加載器,我是爲了演示)

注:–save命令可以把信息自動寫進package.json的dependencies字段
–save-dev可以寫入到devDependences中
(dependencies表示在生產環境中需要依賴的包,DevDependencies表示僅在開發和測試環節中需要依賴的包)

module: {
    loaders:[
        {
            test: /\.js$/, 
            loader: 'babel',  //loader: 'babel-loader'
            query: {
                presets: ['es2015','react']
            }
        },
        ...
    ]
}

注意我的註釋,‘-loader’是可以省略的

css-loader

css-loader使我們能夠利用@import 和 url(…)替代 require()
style-loader將計算後的樣式加入頁面中
二者組合在一起使我們能夠把css嵌入webpack打包後的js文件中(style標籤中)
在命令行中下載
輸入:npm install css-loader style-loader --save-dev

module: {
    loaders:[
        {
            test: /\.css$/, 
            loader: 'style!css'  //loader: 'style-loader!css-loader'
        },
        ...
    ]
}

使用“!”可以加載並列的加載器

url-loader

webpack中一切都是模塊,包括JavaScript,包括css,也包括圖片等靜態資源
爲了將圖片資源變成模塊,我們需要url-loader(將圖片轉換爲base64),它還依賴file-loader
所以都需要下載
輸入:npm install url-loader file-loader --save-dev

module: {
    loaders:[
        {
            test: /\.(jpg|png|gif|svg)$/, 
            loader: 'url?limit=8192'    //loader: 'url-loader?limit=8192'
        },
        ...
    ]
}

上面的limit=8192意思是不大於8KB(1024×8)的圖片纔會被打包處理爲base64的圖片

打包監聽

完成了配置文件
在命令行輸入webpack -w
可以打包我們的文件並且時刻監控我們的文件
(如果僅僅想打包就輸入webpack 命令)
一旦發生變化(Ctrl+S保存後),立刻重新打包
(ps:若想在命令行取消監聽,使用Ctrl+C)

出現了這些,表示打包成功了

、

回到我們的文件夾,我們發現out文件夾由webpack自動生成了
node-modules內部裝的就是我們剛剛下載的那些loaders

頁面中也成功顯示了hello world

插件系統

除了loader外,plugin插件是另一個擴展webpack能力的方式
與loader的處理資源內容的轉換不同,plugin功能範圍更廣泛
由於這是一篇入門指南的文章
簡單介紹一個插件,主要看這個流程
插件同樣需要安裝(內置的插件不需要額外安裝)和配置

extract-text-webpack-plugin

在我們的示例中,通過JavaScript加載了CSS是藉助style-loader的能力
(將CSS已style標籤的形式插入頁面,標籤內容通過JavaScript生成)
這種方法有很大弊端:樣式內容生效時間延後
換句話說,點開頁面的一瞬間,你會看到無樣式的頁面
雖然這時間很短,但是用戶體驗非常的差

一般情況下,我們都是會把link標籤插入head中,script放到body的最後面
這樣文檔被解析前,樣式就已經下載並解析
但現在樣式與JavaScript一起加載,所以造成這樣的效果
不過這個缺陷可以避免,那就是使用extract-text-webpack-plugin插件


由於它不是內置插件
所以我們首先利用命令行下載npm install extract-text-webpack-plugin
還要修改配置文件

/* webpakc.config.js */
var ExtractTextPlugin = require('extract-text-webpack-plugin'); 
module.exports = {
    ...
    plugins: [
        new ExtractTextPlugin('[name].css')
    ]
}

添加plugins配置。值是一個數組,數組的每一項是一個plugin實例

重新打包監聽

我們發現它爲我們創建了main.css文件
然後在html中引用它

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="./out/main.css">
</head>
<body>
    <script src="./out/index.js"></script>
</body>
</html>

這樣便不會出現短暫的無樣式瞬間了

構建本地服務器

我們之前都是利用 webpack -w 的watch模式進行實時構建打包監聽
除此之外,webpack還提供了webpack-dev-server來輔助開發與調試
(直接寫到內存而不是硬盤裏,更快的打包速度)
webpack-dev-server是一個基於Express框架的Node.js服務器
它提供一個客戶端運行環境,會被注入到頁面代碼中執行,並通過Socket.IO與服務器通信
於是服務器端的每次改動與重新構建都會通知頁面,頁面隨之可作出反應

使用它我們同樣需要安裝npm install webpack-dev-server
然後啓動即可webpack-dev-server
(它也擁有類似 webpack -w 的功能,輸入後打包監聽)
如果要配置本地服務器,那麼就在webpack.config.js中設置devserver屬性
devserver可配置選項如下:

  • contentBase 設置本地服務器所在目錄(默認爲根文件夾)
  • port 設置監聽端口,(默認8080)
  • inline 若設置true,源文件改變時自動刷新頁面
  • colors 若設置爲true,終端輸出文件爲彩色
  • historyApiFallback 若設置爲true,所有跳轉將指向index.html(開發單頁應用時非常有用,依賴於H5 history API)
/* webpakc.config.js */
module.exports = {
    ...
    output: {
        path: './out/',
        publicPath: 'http://localhost:8080/out/',
        filename: '[name].js'
    },
    ...
    devServer: {
        colors: true, 
        historyApiFallback: true, 
        inline: true 
    } 
}

我們的html中也需要對路徑作出一些修改

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="http://localhost:8080/out/main.css">
</head>
<body>
    <script src="http://localhost:8080/out/index.js"></script>
</body>
</html>

這樣我們可以在瀏覽器輸入網址localhost:8080看到我們頁面了
不過這是因爲瀏覽器默認查找盤符下的index.html
如果我們的html文件名字不是index.html而是abc.html
那麼查看網頁就需要輸入localhost:8080/abc.html

調試

開發最討厭的就是找bug了
找bug就就需要調試
不過我們發現,一旦我們出錯了
瀏覽器顯示錯誤的位置都是webpack爲我們打包出的文件
這很不利於我們調試,所以我們需要Source Maps

用法很簡單,只需要在webpack.config.js添加devtool配置信息

/* webpakc.config.js */
module.exports = {
    devtool: 'eval-source-map',
    entry: ...
    output: ...
}

devtool有以下配置選項

  • source-map
    在一個單獨的文件中產生一個完整且功能完全的文件(具有最好的source map),但是會減慢打包文件的構建速度
  • cheap-module-source-map
    在一個單獨的文件中生成一個不帶列映射的map,不帶列映射提高項目構建速度,但是也使得瀏覽器開發者工具只能對應到具體的行,不能對應到具體的列(符號),會對調試造成不便;
  • eval-source-map
    使用eval打包源文件模塊,在同一個文件中生成乾淨的完整的source map。這個選項可以在不影響構建速度的前提下生成完整的source map,但是對打包後輸出的JS文件的執行具有性能和安全的隱患。不過在開發階段這是一個非常好的選項,但是在生產階段一定不要用這個選項
  • cheap-module-eval-source-map
    這是在打包文件時最快的生成source map的方法,生成的Source Map 會和打包後的JavaScript文件同行顯示,沒有列映射,和eval-source-map缺點類似

在我們的生產環境,最好的選項就是eval-source-map了

除此之外我們還可以利用命令行使用source map
webpack-dev-server --devtool sourcemap

指令

在開發中我們還比較常用的指令
--colors 部分信息高亮
--progress 顯示進度百分比信息

不同指令可以用空格隔開 webpack-dev-server --progress --colors --devtool sourcemap
不過每次都要敲這麼長的指令很麻煩
沒關係有辦法
找到package.json文件
在scripts下存儲我們的指令

/* package.json */
{
  "name": "demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "abc": "webpack-dev-server --progress --colors --devtool sourcemap" //增
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.21.0",
    "babel-loader": "^6.2.10",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react": "^6.16.0"
    ...
  }
}

這裏我起了一個名爲abc的指令
這樣在命令行中直接輸入npm run abc
就相當於使用了webpack-dev-server --progress --colors --devtool sourcemap


最後最後
我們來複習一下webpakc.config裏面的構造

/* webpakc.config.js */
var ExtractTextPlugin = require('extract-text-webpack-plugin'); /* 引用插件 */
module.exports = {
    devtool: 'eval-source-map',   /* source map 使調試更容易 */
    entry: './src/js/entry.js',   /* 主入口文件路徑 */
    output: {
        path: './out',   /* 輸出文件路徑 */
        publicPath: './out',   /* 靜態資源完整路徑 */
        filename: 'index.js'   /* 輸出文件名 */
    },
    module: {
        loaders: [  /* 加載器 */
            {test: /\.js$/, loader: 'babel'},
            {test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader','css-loader')},
            {test: /\.(jpg|png|gif|svg)$/, loader:'url?limit=8192'}
        ]
    },
    plugins: [   /* 插件 */
        new ExtractTextPlugin('[name].css')
    ],
    devServer: {   /* 本地服務器配置 */
        colors: true,
        historyApiFallback: true,
        inline: true
    }
}


感謝webpack中文對本文章的收錄
感謝官方對我的肯定b( ̄▽ ̄)d
更多官方收錄優秀資料戳這裏:傳送門

==主頁傳送門==

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章