前端自動化構建工具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
}
}