Webpack(二):使用 loader
- 2019 年 11 月 7 日
- 筆記
Webpack 提倡一切皆模塊,所有類型的文件(css、圖片等)都可以經過 loader 處理變成我們可加載的模塊。
1. loader 安裝和配置
安裝 loader 統一方式是 npm insatll xxx-loader --save -dev
-dev
表示開發時依賴。
webpack.config.js
基本配置方式:
module.exports = { entry: ..., output:{...}, module:{ rules:[ { test: ..., use:['xxx-loader','xxx-loader',...] }, {...}, {...}, ] } }
2. 樣式處理
2.1 css-loader 和 style-loader
css-loader 通過 npm 安裝,但是要把樣式真正掛載到 dom 上,還需要安裝 style-loader
通過 webpack.conifg.js
配置 css-loader 和 style-loader。注意 webpack 是從右向左讀取的,書寫順序有要求。
module:{ rules:[ { test: /.css$/, use:['style-loader','css-loader'] } ] }
2.1 less-loader
同樣的,還可以使用 less-loader:
module:{ rules:[ { test: /.css$/, use:['style-loader','css-loader'] }, { test:/.less$/, use:[{ loader:"style-loader" },{ loader:"css-loader" },{ loader:"less-loader" }] } ] }
從 npm 3.x 開始,less 不會再隨着 less-loader 的安裝而自動給你安裝,所以我們需要手動安裝 less,也就是完整命令:npm install less less-loader --save -dev
,否則後續打包會報錯。
3. 圖片打包
和圖片打包相關的兩個 loader ,一個是 url-loader,一個是 file-loader。
如果圖片較多,會發送很多 http 請求,降低頁面性能。因此,url-loader 會將引入的圖片編碼,生成 base64 的 dataURL —— 相當於把圖片數據翻譯成一串字符,再把這串字符打包到文件中,最終只需要引入這個文件就能訪問圖片了。
當然,如果圖片較大,編碼會消耗性能,打包文件體積也會變大,因此 url-loader 提供了一個 limit
參數(一般是8kb),小於 limit
的圖片依然被轉為 DataURL;大於 limit
的圖片則調用 file-loader 進行copy,並輸出到 dist 文件夾中。 雖說 url-loader 封裝了 file-loader,但實測如果不額外安裝 file-loader 的話,在圖片體積較大時打包還是會報錯的,因此兩個都要安裝。
下面通過例子說明在 CSS 或者 JS 中使用圖片時,loader 是如何處理的。
2.1 css 引用
這裡我們在 src 下新建 img 文件夾,裏面放 test1.jpg 和 tets2.jpg,一張大於8kb,一張小於8kb。在前面的 index.css
文件中,我們像往常一樣使用圖片作為背景:
div{ width:100px; height:200px; background:url("../img/test1.jpg"); }
之後打包發現報錯,所以我們安裝 url-loader,再次打包。瀏覽器查看:

會發現圖片可以正常引用了,而且是以 dataURL 的形式引用的。
接着測試大於8kb的圖片(修改上面代碼為 test2.jpg )。這時,如果直接打包會報錯提示缺少 file-loader,所以我們這裡安裝一下 file-loader。
再次打包,雖說這次不報錯了,但是我們發現瀏覽器里圖片沒有顯示出來。看一下控制台:

可以看到,路徑是直接引用的圖片名字,同時會看到 dist 文件夾下輸出了原始圖片的副本。也就是說,其實這時候 webpack 認為我們的 index.html
文件在 dist 文件夾中,所以選擇了這樣的路徑引用,但其實我們的 index.html
文件在外層。同時,我們也不希望直接輸出在 dist 文件夾下,最好是裏面還有一個 img 文件夾,所以我們先來 webpack.config.js
配置一下:
{ test: /.(png|jpg|gif|jpeg)$/, use:[{ loader:"url-loader", options:{ limit:8192, outputPath:'img' } }] }
這裡就設置了圖片的輸出路徑,另外,圖片默認以 32 位 hash 命名,這樣太長了,而且也不知道具體是哪張圖片,所以我們順便配置一下圖片命名規則:
{ test: /.(png|jpg|gif|jpeg)$/, use:[{ loader:"url-loader", options:{ limit:8192, outputPath:'img', name:'[name].[hash:8].[ext]' } }] }
再次打包,打開控制台:

可以看到,命名正確了,文件輸出方式也正確了(dist 下多出一個 img 文件夾),但是圖片路徑還是錯的,所以不顯示圖片。我們接着來配置一下圖片路徑。 我們前面說過,webpack 認為 index.html
在 dist 文件夾中,所以才會直接通過圖片名字引用圖片。那麼 index.html
實際上是在 dist 文件夾外面的,對於 index.html
來說,它就要通過 ./dist/img
才能順利找到圖片,也就是說,我們可以在原本路徑(圖片名)的基礎上加一個固定前綴(./dist/img),使之正確指向圖片位置(./dist/img/圖片名)。
而 publicPath
正是可以用來做這件事的:
{ test: /.(png|jpg|gif|jpeg)$/, use:[{ loader:"url-loader", options:{ limit:8192, outputPath:'img', publicPath:'./dist/img', name:'[name].[hash:8].[ext]' } }] }
publicPath
會給使用了相對路徑引用的圖片加上統一前綴。比如我們的圖片路徑一開始是 img/test2.95a05a82.jpg
,那麼使用了 publicPath
後,圖片路徑就變成 ./dist/img/test2.95a05a82.jpg
。通常可以給 publicPath
配置一個 cdn 地址前綴,比如 https://xxx.cdn.com
,上線的時候圖片就都統一使用 cdn 地址了。
那麼配置好後再次打包,瀏覽器查看:

路徑正常,而且圖片也正常顯示了。
另外,我們也可以選擇給 output.publicPath
配置 ./dist/
,這樣的話不止是圖片,所有使用相對路徑引用的靜態資源都會加上這個前綴了。不過要注意,這個前綴需要加一個 /
,而圖片的 publicPath
是不需要的。
2.2 js 引用
js 中引用圖片需要使用 require,舉個例子:
// module2.js // require 拿到圖片路徑字符串 var img = require('../img/test2.jpg'); // 模板字符串構建 img 標籤 export var demo = `<img src="${img}"/>`
// main.js import demo from './js/module2.js'; document.body.innerHTML = demo;
只管根據圖片和 module2.js
的路徑關係正常引入圖片即可,後面路徑會被正確替換。 如果我們之前沒有配置 publicPath
的話,會發現打包後的路徑是 img/test2.95a05a82.jpg
,也即 index.html
依然被當作是位於 dist 文件夾下的。因為我們前面配置了,所以這裡路徑是正確的,最後可以正常顯示圖片:

4. Babel 轉譯
命令行安裝:
npm install --save -dev babel-loader@7 babel-core babel-preset-es2015
配置 webpack.config.js
:
module:{ rules:[ {...}, {...}, { test:/.js$/, exclude:/(node_modules|bower_components)/, use:{ loader:'babel-loader', options:{ presets:['es2015'] } } } ] }
exclude:/(node_modules|bower_components)/
表示不轉譯 node_modules
文件夾中的 js 。
babel-loader 的預設:
babel-preset-es2015
,babel-preset-es2016
等:支持不同版本的ECMAScript規範;babel-preset-latest
: 支持現有所有 ECMAScript 版本的新特性,包括處於stage 4里的特性(已經確定的規範,將被添加到下個年度的)。babel-preset-env
:但很多時候我們需要更靈活的 preset —— 比如大部分瀏覽器已經支持了 ES6 的某個特性,那麼對於這個特性我們其實是不必去轉譯的,但前面所說的那些 preset 會一概轉譯。所以這裡還提供了babel-preset-env
,它可以根據我們指定的目標環境(比如某個版本的瀏覽器)來選擇它不支持的特性進行轉譯。
5. 集成 Vue
注意這不是一個開發時依賴:
npm install vue@2.5.21 --save
如果遇到這個報錯:
You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
這是因為 Vue 有兩個版本:
- runtime-only
- runtime-compiler
其中,runtime-only 版本無法編譯模板,要麼用 render 函數,要麼修改 webpack.config.js
配置改用其它版本。這裡我們先選擇後者:
module.exports = { entry: ..., output: {...}, module: {...}, resolve:{ alias:{ 'vue$':'vue/dist/vue.esm.js' // 指定版本 } } }
另外,webpack 還需要分別藉助 vue-loader 和 vue-template-compiler 去加載和解析 .vue 文件:
npm install vue-loader@13.0.0 vue-template-compiler@2.5.21 --save -dev
之後我們就可以編寫單文件組件了( 使用 vscode 的話強烈建議安裝 vetur 插件)。
Note:
因為我們安裝的 vue 版本是 vue@2.5.21,所以這裡的 vue-loader 和 vue-template-compiler 要注意版本對應問題,總之報錯信息也寫得很清楚了。
(要說為什麼用這麼低版本的 vue,因為視頻里的項目也是用低版本 Vue 構建的,所以暫時先跟着講師的步伐吧 =。=)