目錄結構
project
- css
- bootstrap.min.css
- jb.css
- fonts
- 一些bootstrap的字型
- images
- 一些專案用到的圖片
- js
- bootstrap.min.js
- jquery.min.js
- jb.js
- index.html
- favicon.ico複製程式碼
專案背景
這個是公司要做的一個官方網站。由於專案比較簡單,要求是單頁的,沒有頁面跳轉,所以只有一個 .html 檔案。專案用了比較常規的 bootstrap + jquery 的開發,這個也沒啥好說的。考慮到 CDN 的可控性,所以把所有 bootstrap 的資源都下載到了本地進行引用。專案開始時是用了常規的 js 和 css 引用(css 放前面,js 放後面),在開發完成後,發現有時間多餘,就考慮用 webpack 對他進行處理一下,以鞏固和學習一下 webpack 所用到的知識
初始化工作
1. 初始化
npm init
先初始化一個 package.json 檔案來管理我們 webpack 所依賴的檔案包。一路無腦回車即可。複製程式碼
2. 安裝 webpack
想要用 webpack ,那麼你首先肯定要安裝 webpack 才可以啊。用以下命令:
npm install webpack --save複製程式碼
3. webpack.config.js
在根目錄下新建一個 webpack.config.js 檔案,用來對 webpack 進行配置。
當有這個檔案後,我們系可以在命令列中用以下命令來啟動配置好的 webpack 了。
webpack --config webpack.config.js複製程式碼
4. 修改 webpack 打包命令
以上雖然也可以啟動配置好的 webpack。但是每次要輸這麼一串命令好像有點太長了(懶啊)。所以在 package.json 中修改這樣一項:
"script": {
+ "start": "webpack --config webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1"
}複製程式碼
這樣的話我們就可以在命令列中少敲幾個鍵盤了。直接用以下命令,就等同於上面的命令了:
npm start複製程式碼
好了,到這裡,初始化工作就做完了,那麼開始我們的 webpack 配置吧
webpack.config.js
1. 哪裡來的入口檔案?
什麼事入口檔案
webpack 建立應用程式所有依賴的關係圖(dependency graph)。圖的起點被稱之為入口起點(entry point)。入口起點告訴 webpack 從哪裡開始,並根據依賴關係圖確定需要打包的內容。可以將應用程式的入口起點認為是根上下文(contextual root) 或 app 第一個啟動檔案
跟其他的 spa 應用不一樣,這樣的普通應用,其所有依賴都來自於 index.html 檔案。如果說要有入口檔案的話,怎麼也應該是 index.html 他本身吧。但是 webpack
基本都是用 .js 檔案作為入口檔案,用 .html 作為入口檔案的...(反正我是沒有見到過)。至於這裡有啥原因的話,大家就參考一下這篇文章吧。(其實我也不懂)
所以說,不管怎麼樣,我們都需要有個入口檔案。
那就不管怎麼樣,我們經常看到的 webpack 的配置都是這樣的
module.exports = {
entry: './index.js'
}複製程式碼
那麼我們不管三七二十一,先在根目錄下新建一個 index.js,然後讓他作為我們的入口檔案。
index.js
要知道,對於我們原來的專案而言,我們根本就是不需要這麼一個 index.js 檔案的(沒有他,我們可以活得更好)。但是我們又不得不建立了這樣一個檔案。那麼問題來了,這麼建立出來的檔案,裡面又該放什麼內容呢?我們總不該救這麼建立一個空檔案就算了吧。
在考慮這個問題的時候,我們可以先去看下 webpack 官方的那個很經典的圖(我很懶,就不放圖了,大家自己去網上找吧)。webpack 把左邊亂七八糟的 .js .css .png .jpg .sass。。。等等檔案全部打包成了靜態資源。也就是說,webpack 打包的是除 html 外的所有資源,那麼我們是不是隻要把這些資源都放到入口檔案中那麼就可以讓 webpack 幫我們打包了呢?
但是等等。其他的都沒有問題,什麼 css 啊,什麼 js 啊,都好說,因為用來也不過這麼幾個,但是圖片呢,字型呢?我在專案中用了那麼多圖片,要全部再在 index.js 裡面再寫一遍!天吶!這是要命的啊!那麼我能不能偷懶下,就只寫 css 和 js 呢,其他亂七八糟的我先不管?那就先這麼來吧。修改我們的 index.js 檔案,新增以下內容。
require('./css/bootstrap.min.css')
require('./css/jubang.css')
require('./js/jquery.min.js')
require('./js/bootstrap.min.js')
require('./js/jb.js')複製程式碼
先這樣把他當作我們的入口檔案吧
2. 配置出口檔案
出口檔案就很好配置了,將他打包到根目錄下的 dist 目錄中。嗯 ~ 這很常見!
var path = require('path');
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}複製程式碼
3. 配置 loader
這個不知道怎麼配置,就先看這篇文章吧。
所以說,到這裡我們的 webpack.config.js 就是這樣的:
var path = require('path');
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [{
test: /\.css$/,
use: [
'style.loader',
'css-loader'
]
}, {
test: /\.(png|jpg|svg|git)$/,
use: [
'file-loader'
]
}, {
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}]
}
}複製程式碼
這裡我們用到了三個 loader,需要先安裝下
npm install css-loader style-loader file-loader --save複製程式碼
這三個 loader 的作用大家還是自己去網上查詢吧
4. 第一次打包
配置到這裡,我們可以先來打包一下,有問題再改嘛!
命令列切換到專案目錄下,執行以下命令:
npm start
打包結束後,專案的目錄結構
project
- css
- dist
- fonts
- images
- js
- node-modules
- favicon.ico
- index.html
- index.js
- package.json
- webpack.config.js
我們可以看到,專案根目錄下面多出來一個 dist 的目錄。沒錯,這個就是我們 webpack 打包後檔案生成的目錄,至於為什麼會是 dist 目錄,那是因為你在 webpack.config.js 的 output 中設定的 path。
現在我們來檢視下 webpack 打包出了什麼東西
- dist
- xxxx.jpg
- xxxx.woff2
- xxxx.jpg
- xxxx.svg
- bundle.js
- xxxx.ttf
- xxxx.eot
- xxxx.woff複製程式碼
注:xxxx代表一串數字和字母的組合,為了表示方便就這麼寫了
打包檔案中生成了一個 .js 檔案,兩個 .jpg 檔案,四個字型檔案(.woff2、.ttf、.eot、.woff),和一個 .svg 檔案
我們來看下這是個型別的檔案都來自哪裡吧複製程式碼
1. bundle.js
bundle.js 是我們根據我們入口檔案,將我們在入口檔案中所有的依賴資源都打包進去生成的。這個也是我們最主要要關注的檔案。
2. .jpg
兩個 .jpg 檔案是從哪裡來的呢?檢視了兩張圖片之後,其實我們會知道,這兩個圖片都是在我們自己寫的 jb.css
中用來作為 background-image
引入的。我們說了,webpack 會根據入口文作為起點,並根據依賴關係圖來進行打包的。換句話說,我們在入口檔案中依賴了 jb.css
,而 jb.css
依賴了兩張 .jpg 圖片,所以 webpack 根據依賴分析,將這兩張圖片也都一起打包進來了。
3. 字型檔案 + svg
四個字型檔案的來源就需要我們對 bootstrap 有一定了解了。如果熟悉 bootstrap 的同學肯定會知道,在 bootstrap 的依賴裡面,他正是依賴了這些字型,也就是說這些字型檔案是從 bootstrap.min.css 檔案中被打包進來的。其實我在做這個專案的時候,把 bootstrap 的原始碼弄到本地的時候,會發現裡面有一個 fonts 的資料夾,也就是我們專案根目錄下的 fonts 資料夾,這裡會有四個同樣檔案結尾的字型檔案和一個 .svg 結尾的 svg 檔案。而這五個檔案不就是我們這裡多出來的五個檔案嘛
如果還不放心的話,我們可以再做個驗證。修改 webpack.config.js 檔案
{
test: /\.(png|jpg|svg|git)$/,
use: [
- 'file-loader'
+ 'file-loader?name=[hash:8].[name].[ext]'
]
}, {
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
- 'file-loader'
+ 'file-loader?name=[hash:8].[name].[ext]'
]
}複製程式碼
我們把圖片和字型檔案,在 file-loader 處理後讓他的名字就變成 ’8位hash值-原檔名-字尾名‘的格式,那麼我們就可以比對這幾個檔案的來源了。
重新打包後(你得先刪除原來的 dist 資料夾),我們就可以發現我們的猜測是正確的!
4. 遺留問題
雖然我們的第一次打包成功了,但是還是留下了幾個問題沒有解決:第一,我的 js、css、字型、圖片等資源都被打包進了 dist 目錄,但是作為我們的專案最主要的 index.html 檔案呢?沒有這個檔案,我們打包出來的東西還有什麼意義呢!。第二,我的 images 資料夾裡有那麼多圖片,你這個 webpack 打包後為什麼就只有兩張圖片了,其他的呢?
那麼接下來讓我們急需解決。
5. html-webpack-plugin
要解決第一個問題,我們需要用到 html-webpack-plugin 外掛。這個外掛的具體說明可以檢視這裡。這個外掛的作用是可以將 html 檔案打包,並自動新增對打包後的 output 檔案的引用。具體如何使用,請先安裝:
npm install html-webpack-plugin --save複製程式碼
修改配置檔案:
var path = require('path');
+ var HtmlWebpackPlugin = require('html-webpack-plugin');
在 module 後面加
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]複製程式碼
我們在配置檔案中引入了一個外掛,並向 HtmlWebpackPlugin 建構函式傳遞了一個物件引數,在這個物件引數中,我們指明瞭一個 template 欄位,表面我們要打包的 html 的檔案源。然後我們重新打包,再檢視我們的目錄就可以發現 dist 目錄下多出了一個 index.html。由於這個專案本身就是一個簡單的不依賴任何環境的專案,所以如果正常的話我們直接開啟 index.html 頁面就能在瀏覽器里正常顯示了。雖然不知道會怎麼樣,但是我們還是開啟來試試吧。
當我們開啟 index.html 在瀏覽器中顯示的時候,我們發現瀏覽器中好多圖片都不見了。細想我們的專案程式碼,發現除了在 jb.css 中的背景圖被正確顯示以外,其他的定義在 img 標籤中的圖片沒有一張是顯示出來的。
所以雖然這個檔案配置還有點問題沒解決,那麼我們先來解決 html 中的 img 問題吧,也就是我們上面提到的第二個問題。
6. html-withimg-loader
要解決第二個問題(也就是 html 中的 img 問題),我們需要用到 html-whithimg-loader,具體關於這個怎麼用可以檢視這裡。
修改配置檔案,直接在 rules 中再新增一條配置規則
{
test: /\.(html|htm)$/,
use: [
'html-withimg-loader'
]
}複製程式碼
這條配置規則表明,所有要處理的 html 檔案首先會經過 html-withimg-loader 這個 loader 處理。就是這麼簡單能將我們第二個問題解決嗎?試試看吧,事件是檢驗真理的唯一標準。
修改資源目錄
重新打包,再檢視 dist 目錄,這一檢視不要緊,發現 dist 目錄下多了好多圖片檔案,密密麻麻,亂七八糟的,這些該不會就是我們 html 中的圖片吧。按住自己的強迫症,先找到 index.html (我們最關心的還是他嘛),開啟後再瀏覽器檢視效果。發現果然,我們的圖片都已經在了,而且樣式也差不多對了。但是這麼亂七八糟的 dist 目錄,怎麼會是我們這種強迫症患者所想要的結果呢!我們想要的是圖片都放在圖片資料夾下,字型都放在字型資料夾下,其他的比較少的就先讓他在外面呆著吧。說幹就幹,我們來調整一下我們的配置檔案
{
test: /\.(png|jpg|svg|git)$/,
use: [
- 'file-loader?name=[hash:8].[name].[ext]'
+ 'file-loader?name=images/[hash:8].[name].[ext]'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
- 'file-loader?name=[hash:8].[name].[ext]'
+ 'file-loader?name=fonts/[hash:8].[name].[ext]'
]
} 複製程式碼
自動刪除 dist
還有一個問題我已經忍了很久了,每次打包前,我們都需要手動先去刪除上次打包留下來的 dist 目錄,這個就煩了,雖然只是一個 delete 的事情,但是做多了也煩啊!我們想能不能讓 webpack 自動幫我們做了這件事,讓我們不需要手動去刪除。還好 webpack 夠智慧,總能滿足你提出來的各種無理取鬧。不過我們先要裝一個外掛:
npm install clean-webpack-plugin --save複製程式碼
然後再配置檔案中新增對這外掛的引用
var HtmlWebpackPlugin = require('html-webpack-plugin');
+ var CleanWebpackPlugin = require('clean-webpack-plugin');複製程式碼
在 plugins 中新增對這個外掛的使用
new CleanWebpackPlugin(['dist'])複製程式碼
這樣我們就能不用手動刪除 dist 資料夾了。
重新打包下試試吧!
打包完後我們在檢視 dist 目錄就瞬間感覺清爽多了有木有!
- dist
- fonts
- 一些字型檔案
- images
- 一些圖片檔案
- bundle.js
- index.html複製程式碼
修改 index.html
將打包後的專案再次在瀏覽器中檢視,並檢視控制檯會發現,控制檯報了一堆錯誤。其中有幾個是對一些 css、js 檔案引用的錯誤。因為我們把需要用的 css、js 都打包進了 bundle.js 中了。而我們原來的專案是通過靜態資源引用的方式一個個匯入 html 檔案中的。所以,當我們 webpack 打包成功後,就不需要對這些資源進行引用了,我們只需要對 bundle.js(我們打包後的檔案)進行引用就可以了,所幸的是,打包後的檔案 webpack 已經自動幫我們引用了。所以直接在原來專案中的 index.html 中幹掉那些 css、js 就可以了。然後重新打包後就沒有這些資源找不到的亂七八糟的錯誤了。
但是,有一個小問題就是關於我們的 .ico 檔案。這是我們網站的圖示檔案。他的引用錯誤該如何解決呢?我們可以在生成 html 的時候,將這個問題先給解決了。修改配置檔案
new HtmlWebpackPlugin({
template: './index.html',
+ favicon: path.resolve(__dirname, './favicon.ico')
})複製程式碼
這樣的話我們的圖示檔案也就有了。
7. 關於 jquery
雖然一些亂七八糟的引用錯誤解決了,但是控制檯留下了一個讓我們非常頭疼的問題:jquery 的引用問題:
Uncaught Error: Bootstrap's JavaScript requires jQuery複製程式碼
這裡我們需要用到 expose-loader 這個東西,關於他,可以檢視這裡,廢話不多說:
npm install expose-loader --save複製程式碼
在 module 的 rules 中再新增一個 loader
{
test: require.resolve('./js/jquery.min.js'), // 引入 jquery
use: [{
loader: 'expose-loader',
options: '$'
}, {
loader: 'expose-loader',
options: 'jQuery'
}]
}複製程式碼
當然,網上或許有其他方法關於引入 jquery 的,這裡只是說一種。然後再打包我們的頁面就沒有問題了:各種資源都有了,js 寫的效果也出現了。
8. 提取 css
雖然網站看上去沒啥問題了,但是細心的同學肯定會發現:當我們開啟網站的時候,他會先出現一個沒有樣式的頁面,然後一閃而逝,最後才出現我們預期的樣子。這是為什麼呢?
原因很好理解,因為我們把 css 和 js 都打包進了同一個 bundle.js 裡面了。但是,這個 bundle.js 是在頁面最後面才載入進來的。也就是說,我們的樣式被放在了頁面的底部被載入。這完全不符合我們的預期啊。我們希望的是樣式在 head 中載入,而 js 指令碼才放在頁面底部載入。所以我們就不能把 css 和 js 一起打包進 bundle.js 中了。
extract-text-webpack-plugin
詳細資料看這裡
npm install extract-text-webpack-plugin --save複製程式碼
增加 require
var ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');複製程式碼
修改 css rules
{
test: /\.css$/,
use: ExtractTextWebpackPlugin.extract[{
fallback: 'style-loader',
use: 'css-loader'
}]
}複製程式碼
增加 plugin
new ExtractTextWebpackPlugin('style.css')複製程式碼
打包,然後我們會發現 dist 中多了一個 style.css,然後再 index.html 的 head 中的多了對這個 css 的引用
9. 其他
1. 壓縮 js
增加 plugin
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})複製程式碼
2. 壓縮 html
new HtmlWebpackPlugin({
template: './index.html',
favicon: path.resolve(__dirname, './favicon.ico'),
minify: {
removeAttributeQuotes: true,
removeComments: true,
removeEmptyAttribute: true,
collapseWhitespace: true
}
}),複製程式碼
3. 優化圖片
{
test: /\.(jpg|png|gif|svg)$/,
- use: 'file-loader?name=images/[hash:8].[name].[ext]'
+ use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}複製程式碼
總結
暫時就先那麼多吧,沒時間寫了,以後再說。第一次發文,求輕虐。