1. 程式人生 > >前端自動化構建工具Webpack開發模式入門指南 (網上看到的,寫得很詳細)

前端自動化構建工具Webpack開發模式入門指南 (網上看到的,寫得很詳細)

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自動建立
現在將命令符引導到我們專案的根路徑

輸入: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 create = require('./tool.js');
var p = create('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
可以打包我們的檔案並且時刻監控我們的檔案
一旦發生變化(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"
    ...
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

這裡我起了一個名為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
    }
}