webpack3.0小案例系列,拋磚引玉,希望大家多提意見,一起學習:
前兩節文章我們手把手搭建了一個基於webpack的前端開發工程環境,並講解了一些常用外掛及loader的基本用法,包括程式碼分割、模板讀取、檔案清理、ES6編譯及css處理等等,這篇文章我繼續在前兩篇的基礎上,繼續探討:
- 圖片處理 , 包括file-loader、url-loader、image-webpack-loader的用法,file-loader和url-loader的區別等。
- 服務端啟動webpack-dev-server ,包括手動搭建測試服務端以及運用webpack建立webpackServer和一些注意事項等。
- webpack打包優化 ,包括程式碼壓縮,公共程式碼提取等配置。
image圖片處理
webpack對圖片的處理常用的有url-loader、file-loader、image-webpack-loader,各個載入器都在打包過程中有著自己的功能職責,在探討他們的使用方法之前,老規矩,我們先弄明白他們各自都做了什麼。
- file-loader , 將專案中定義載入的圖片通過webpack編譯打包,並返回一個編碼後的公共的url路徑。
- url-loader ,url-loader作用和file-loader的作用基本是一致的,不同點是url-loader可以通過配置一個limit值來決定圖片是要像file-loader一樣返回一個公共的url路徑,或者直接把圖片進行base64編碼,寫入到對應的路徑中去。
- image-webpack-loader ,用來對編譯過後的檔案進行壓縮處理,在不損失圖片質量的情況下減小圖片的體積大小。
file-loader
1、首先,我們在src目錄下新建assets資料夾用來放置所有的靜態資源,在該目錄下我們放入兩張圖片bg_webpack-sm.jpg 和 bg_webpack-lg.jpg,大小分別是6kb和12kb(之所以放兩張不同大小的圖片是用於體驗url-loader的limit引數的作用)。
2、開啟style.less檔案,新增一個container的css類,並在index.html中新增一個類名為container的div
.container{
width:300px;
height: 100px;
background: url('../assets/bg_webpack-lg.jpg')
}
複製程式碼
<body>
<div id="app"></div>
<div class="container"></div>
</body>
複製程式碼
3、 命令列定位到根目錄下,安裝file-loader,並在webpack配置檔案中新增一條處理規則:
cnpm install file-loader --save-dev
複製程式碼
...
module: {
rules: [
...
{
test: /\.(png|jpg|gif|svg)$/,
exclude: /node_modules/,
use: [{
loader: 'file-loader',
options: {}
}]
}
]
}
...
複製程式碼
再次執行webpack命令進行編譯,我們發下在dist目錄下已經打包出了我們引用的圖片資源
file-loader配置
現在,圖片檔案已經打包成功了,但是我們發現兩個問題:
- 開啟dist資料夾下面的index.html,發現圖片並沒有在介面中顯示出來。
- 打包後的圖片直接生成在了dist目錄下,而我們想要他生成的指定資料夾下面,這顯然和我們的期望是不符合的。
彆著急,為了解決這兩個問題,我們需要先對file-loader的部分配置項引數作用有一個大致的瞭解:
-
name ,指定打包生成後資原始檔的name值,file-loader給我們指定了一些佔位符來自定義name,我們可以自己任意搭配佔位符來得到我們想要生成的檔名稱的格式,預設情況下是:[hash].[ext]
名稱 描述 [ext] 檔案的字尾名,預設是原始資源的字尾名 [name] 檔名稱,也就是原始資源的名稱 [path] 檔案存放的路徑 [hash] hash值,預設是md5 [N] 數字 -
publicPath ,用來指定打包生成後的css或者url引用中路徑的配置,也就是說pulicPath針對的是路徑,並不是檔案本身。
-
outputPath,用來配置打包生成的檔案輸出的位置,它針對的是檔案儲存的位置,和程式碼中的路徑引用無關。
-
useRelativePath, 配置打包生成後的路徑是否為相對位置,預設情況下是false。需要注意的是,開啟useRelativePath後,outputPath的設定會以src目錄中的assets資料夾為準,設定其他的名稱不會生效。
-
emitFile ,配置對打包的資原始檔是否需要進行釋出,預設為true。
從這些配置項中,我們大致可以找到以上兩個問題的原因:
- 1、圖片生成位置不對是因為我們沒指定outputPath,預設讀取了出口(output)的值;
- 2、在瀏覽器中開啟但沒有顯示出圖片,是由於未設定publicPath的值,因此在生成的css的background的url中會自動預設到與css檔案同一級裡面,導致url地址不對,因此找不到圖片資源。
因此針對這兩個問題,我們對配置檔案進行配置:
...
module: {
rules: [
...
{
test: /\.(png|jpg|gif|svg)$/,
exclude: /node_modules/,
use: [{
loader: 'file-loader',
options: {
name: '[hash].[ext]',
outputPath: '/assets/',//定義圖片輸出存放的資料夾位置
useRelativePath: true,//設定路徑為相對位置
}
}]
}
]
}
...
複製程式碼
在配置項中,我們定義了檔案輸出的名稱以hash值來命名,並指定檔案輸出到dist下的assets資料夾下,然後開啟路徑為相對路徑,再次執行webpack編譯,發現檔案已經按照設定生成到assets目錄下了。
開啟dist下的index.html,圖片成功載入
當然,在設定file-loader的option的時候,由於paublicPath會去讀取webpack出口中的publicPath的值,因此他們之間是有這聯絡的,出口中設定的publicPath不一樣就會導致file-loader中設定的要跟著改變,這一點要把握好。
url-loader
前面我們說過,url-loader是對file-loader的封裝,因此,使用url-loader和file-loader方法基本一樣,在這裡,我們就探討一下他們不同的地方,也就是limit引數。
1、首先,我們安裝好url-loader,並將之前的file-loader替換成url-loader,並在index.html頁面中新增類名為container-sm的div元素。
cnpm install url-loader --save-dev
複製程式碼
// webpack.config.js
...
module: {
rules: [
...
{
test: /\.(png|jpg|gif|svg)$/i,
exclude: /node_modules/,
use: [{
loader: 'url-loader',
options: {
limit: 10240,
outputPath: 'assets/',
useRelativePath: true
}
}]
}
]
}
...
複製程式碼
配置項中,我們指定圖片大於10kb的圖片進行檔案載入,小於10kb的檔案則直接轉換出base64編碼直接寫入到css中,這樣我們剛剛放入的兩張圖片結果應該是大的一張以圖片資源的方式載入,而小的一張則直接被編碼成base64後寫入到css檔案中。最後效果如下:
可以發現結果和我們預計的一樣,表示url-loader已經生效。url-loader對我們處理小型圖片很有幫助,他可以有效減小訪問服務端的次數,減小訪問時間,特別是針對一些icon圖示的時候很有效果。
使用url-loader的時候,我們需要對limit大小界限值進行權衡;base64編碼可以減少瀏覽器的訪問次數,但是它的不足是編碼特別長,這樣很容易導致程式碼體積變大。而通過路徑的方式可以減少程式碼的體積量,並且瀏覽器在多次訪問的時候也會對檔案進行快取處理,但畢竟會增加對伺服器資源的訪問次數。因此,如何設定limit的大小,需要根據專案的情況來定
image-webpack-loader 圖片壓縮
由於url-loader在對檔案處理的時候會增大檔案的體積,比如對檔案進行base64編碼,就會使檔案的體積比原版體積更大,因此,image-webpack-loader就應運而生,它的作用就是在進行url-loader處理之前,首先對靜態資源進行壓縮處理,處理過後的圖片資源再決定是否用base64編碼。
1、首先我們對image-webpack-loader進行安裝,然後再webpack配置中進行對配置的修改:
cnpm install image-webpack-loader --save-dev
複製程式碼
// webpack.config.js
...
module: {
rules: [
...
{
test: /\.(png|jpg|gif|svg)$/i,
exclude: /node_modules/,
use: [{
loader: 'url-loader',
options: {
limit: 10240,
outputPath: 'assets/',
useRelativePath: true
}
}, {
loader: 'image-webpack-loader',//新增image-webpack-loader
options: {
mozjpeg: {//設定對jpg格式的圖片壓縮的程度設定
progressive: true,
quality: 65
},
}
}]
}
]
}
...
複製程式碼
在配置項中,我們對圖片進行壓縮處理,並設定對jpg型別的圖片壓縮到65%質量值,最後執行webpack命令,發現兩張圖片都被壓縮成4.75kb大小的圖片,在css中,兩張圖片都已經以base64編碼的方式呈現了。
image-webpack-loader對不同型別的圖片都對應有不同的處理配置設定,針對不同的圖片開發者可以自定義壓縮比例。
- gifsicle — 壓縮gif圖片格式
- mozjpeg — 壓縮JPEG圖片格式
- optipng — 壓縮PNG圖片格式
- pngquant — 壓縮PNG圖片格式
- webp — 壓縮PNG和JPEG圖片成webp格式
- svgo — 壓縮SVG圖片格式
服務端執行設定
到目前為止,我們的小案例已經把常用的基本的loader梳理了一遍,但是我們又發現了一些問題,比如每次更改程式碼後都需要進行webpack重新編譯,並且,我們的檔案還是以檔案系統的方式在瀏覽器中開啟的,這個似乎不是webpack給我們提供的編碼姿勢,因此,接下來,我們將考慮如何讓我們的專案在伺服器上跑起來等一系列問題。
DIY一個臨時伺服器
既然我們指定了webpack最終打包後的檔案在dist目錄下,那麼現在我們只要將dist內的檔案在伺服器端跑起來不就可以了嗎,這似乎可行的,那麼我就先來嘗試一下
1、在根目錄下,新建一個server.js的檔案,並安裝express
cnpm install express --save-dev
複製程式碼
2、在server.js內,我們利用express來寫一個簡單的伺服器:
//server.js
var path = require('path');
var express = require('express');
var app = express();
var port = process.env.port || 3000;
app.use(express.static(path.join(__dirname, 'dist')));//設定可訪問的靜態資源目錄
app.get('/', function(req, res, next) {
req.url = 'index.html';
next()
})
app.listen(port, function() {
console.log(`server is running at ${port}`)
});
複製程式碼
3、開啟package.json,在script配置項下新增如下命令:
//package.json
...
"scripts": {
"server": "node server.js",//設定用node執行server.js
"build": "webpack"//設定webpack編譯命令
}
...
複製程式碼
這樣我們就可以在控制檯通過cnpm來執行專案了:
cnpm run build /*執行webpack打包命令*/
cnpm run server /*讓編譯後的檔案通過伺服器載入*/
複製程式碼
執行完以上命令後,在瀏覽器中輸入:localhost:3000,即可訪問最終打包的檔案了,但是,我們要的熱更新依然沒有,而且還是需要每次都打包,除了用伺服器跑起來似乎並沒有解決我們的問題(黑人鬱悶臉)。
webpack-dev-server
其實解決在開發中的這個實時編譯問題,webpack早已為我們提供了簡便的方法。
webpack-dev-server是一個小型的nodejs express伺服器,支援兩種模式來自動重新整理頁面,並且都提供熱更新,我們的實踐是以inline模式為例的
- iframe模式,頁面放在iframe中,當發生改變時過載
- inline模式,將webpack-dev-sever的客戶端入口新增到包(bundle)中
熱更新的好處是隻替換更新的部分,而不需要去頁面過載。
1、安裝webpack-dev-server
cnpm install webpack-dev-server --save-dev
複製程式碼
2、安裝完成後,我們開啟package.json檔案,在script配置項下新增:
//package.json
...
"scripts": {
"server": "node server.js",//設定用node執行server.js
"build": "webpack",//設定webpack編譯命令
"dev": "webpack-dev-server"
}
...
複製程式碼
這樣,我們只要在命令列輸入 “cnpm run dev ”既可以把專案執行起來。
3、 用webpack-dev-server執行的時候,預設埠是8080,因此,我們在執行webpack-dev-server後,在瀏覽器執行 localhost:8080,即可訪問專案,這個時候因為專案是處於開發環境,檔案只是儲存在記憶體中而並沒有真正打包出來,因此,專案中並不會生成dist資料夾,也正因如此,我們動態改變程式碼的時候,webpack就會自動更新到介面上去。
webpack在配置項中也為我們提供了配置webpack-dev-server的節點,也就是devServer配置項,在這個配置項中,我們可以更改啟動的埠號,載入模式(iframe或者inline)等等,但需要注意的是webpack3.0已經預設熱更新,如果在devServer中再次設定hot屬性的時候,熱更新反而會失效
...
entry: path.resolve(__dirname, './src/main.js'),
output: {
path: path.resolve(__dirname, './dist'),
filename: 'js/[name]-[chunkhash].js'
},
devServer: {
// hot: true, 不要設定hot熱更新屬性,設定後反而會失效
port: 3000,
inline: true
},
...
複製程式碼
webpack打包優化
webpack優化是一個很有技術性的話題,展開來說的話估計又是一個比較長的話題,我們這一節裡先針對我們目前的專案結構羅列幾項,拋磚引玉。
程式碼壓縮 UglifyJsPlugin
UglifyJsPlugin是webpack預設提供的程式碼壓縮優化器,當在配置項中使用了這個外掛之後,打包後的js檔案會進行程式碼壓縮和註釋刪除等壓縮手段,這個在專案build的時候,會減小打包後專案的體積。
...
plugins: [
//啟用js壓縮
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
drop_console: false,
}
}),
new htmlWebpackPlugin({
template: 'index.html',
filename: 'index.html'
}),
new cleanWebpackPlugin(
['dist'], {
verbose: false,
dry: false,
root: __dirname
}
)
]
...
複製程式碼
減小範圍 include && exclude
webpack的loader載入器設定中,每一個loader都允許對處理檔案進行指定,包括哪一個或者不包括哪一個資料夾內的檔案,這樣會很大程度上減小掃描的範圍,減小打包時間。實際上,在我們的專案中我們已經設定了exclude引數,以排除loader對node_modules中檔案的掃描。
{
test: /\.js$/,
use: [{ loader: "babel-loader" }],
exclude: /node_modules/,
include: /src/
}
複製程式碼
公共程式碼提取 CommonsChunkPlugin
webpack預設提供的CommonsChunkPlugin外掛可以將程式碼中公用的模組提取出來,比如常用的類似jquery、moment這樣的額第三方外掛,如果在每個引用的js打包檔案中都進行打包,最終的專案體積就會成倍增加,通過使用CommonsChunkPlugin可以將這些公用部分提取,有效減小專案體積。
設定 babel 的 cacheDirectory 為true
babel編譯是一個比較費時間的過程,對babel處理的設定,我們不僅要設定include && exclude來儘可能準確定位編譯的檔案,還可以充分利用快取來進一步提升速度,設定babel的cacheDirectory為true,是一個很好的優化方式。
{
test: /\.js$/,
use: [{ loader: "babel-loader" ,cacheDirectory: true}],
exclude: /node_modules/,
include: /src/
}
複製程式碼
最後
這一節我們從webpack對靜態資源相關的處理以及伺服器載入和webpack打包優化等做了總結,其實好多依然講的不全,很多地方也沒有做詳細的解釋,但千里之行始於足下,一步一步來,我相信你終究會得到你想要的。
還是不忘說一句,願各位早日成為一名合格的前端高手,加油!