工程中,咱們須要和 HTML、CSS、模板、圖片和字體等打交道,那如何處理這些這類靜態資源呢?css
其實在 webpack 的眼中,這些靜態資源都是模塊。webpack 自己只認識 js,其餘類型資源必須預先定義一個或多個 loader 轉譯,輸出爲 webpack 能接收的形式在繼續進行處理,因此說 loader 作的就是預處理工做。html
好比組件 js 中加載該組件須要的 css 文件(實現高內聚,若是都在全局引入組件css 文件,哪天去掉這個組件 js,還要同時去掉組件 css 文件),其實只是表達二者之間的依賴關係,由於 css 最終仍是會打包到輸出資源目錄下,對 js 沒有任何實質性影響。vue
每一個 loader 本質都是一個函數,output = loader(input)
。在 webpack4 以前,input 和 output 都必須爲字符串,而 webpack4 以後,也支持**抽象語法樹(AST)**的傳遞,那 loader 就能夠是鏈式的了,即 output = loaderA(loaderB(input))
。node
注意,第一個 loader 的輸入時源文件,以後全部 loader 的輸入是上一個 loader 的輸出,最後一個 loader 輸出給 webpack。webpack
module.exports = function loader(content, map, meta) {
var callback = this.async();
var result = handler(content, map, meta);
callback(
null, // err
result.content, // 轉換後的 內容
result.map, // 轉換後的 source-map
result.meta // 轉換後的 AST
);
};
複製代碼
從上面可看出,本質是個函數,功能是將收到後的內容進行轉換,而後返回轉換後的結果,可能有 source-map 和 AST 對象。git
一個組件中會 import './index.css'
,那 webpack 怎麼處理呢?這就須要 css-loader
這個包來轉譯。github
1.安裝包web
npm install css-loader -D
複製代碼
2.配置正則表達式
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: ['css-loader'] }
]
}
};
複製代碼
css-loader 處理 css 的各種加載語法,而後可交給 style-loader 來將樣式字符串包裝成 style 標籤插入頁面。typescript
1.安裝包
npm install style-loader -D
複製代碼
2.配置
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
};
複製代碼
style-loader 放在 css-loader 後面是由於 webpack 打包機制是按照數組從後往前的順序將資源交個 loader 處理。
exclude
用來排除指定目錄下的模塊,即下面 node_modules
中的模塊不會執行這條規則,該配置項一般是必加的,不然會拖慢總體打包速度;include
用來包含指定目錄下的模塊;module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
exclude: /node_modules/
}
]
}
};
複製代碼
注意,exclude 和 include 都存在時,exclude 優先級高。
二者都是用於更加精確地肯定模塊規則的做用範圍。使用頻率不高。二者關係以下:
好比組件 import './index.css'
,能夠這麼理解:被加載模塊是 resource,加載方就是 issuer。一般 css 配置的加載方是全局的,如今咱們要限定配置。
module.exports = {
module: {
rules: [
{
use: ['style-loader', 'css-loader'],
resource: {
test: /\.css$/,
exclude: /node_modules/
},
issuer: {
test: /\.js$/,
exclude: '/node_modules/',
include: '/src/pages/'
}
}
]
}
};
複製代碼
用來指定一個 loader 的種類,其默認值爲 normal,可選值爲
module.exports = {
module: {
rules: [
{
test: /\.js$/,
enforce: 'pre',
use: 'eslint-loader'
}
]
}
}
複製代碼
其功能是用來將 ES6+ 編譯爲 ES5,從而沒必要關注 ES6+ 特性在各平臺不兼容問題。
1.安裝包
npm install babel-loader @babel/core @babel/preset-env -D
複製代碼
2.配置
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: '/node_modules/',
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: [
[ 'env', { modules: false } ]
]
}
}
}
]
}
};
複製代碼
true
,在重複打包未改變的模塊時防止二次編譯,提升打包速度,指向 node_modules/.cache/babel-loader
;false
,意思是禁止讓 @babel/preset-env 將模塊語句轉換,讓 ES6 Module 語法給 webpack 處理,如果爲 true
,會將 ES6 Module 模塊轉化爲 CommonJS 形式,這將會致使 tree-shaking 特性失效;注意,babel-loader 支持從
.babelrc
文件讀取 babel 配置,可將 presets 和 plugins 從配置中提取出來。
將 HTML 文件轉化爲字符串並進行格式化,而後將 HTML 片斷經過 JS 加載進來。
1.安裝包
npm install html-loader -D
複製代碼
2.配置
module.exports = {
module: {
rules: [
{
test: /\.html$/,
use: 'html-loader'
}
]
}
};
複製代碼
1.安裝包
npm install ts-loader typescript -D
複製代碼
2.配置
module.exports = {
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader'
}
]
}
};
複製代碼
注意,typescript 的配置不是在 ts-loader 中,而是在工程目錄的 tsconfig.json 中,相似
{
"compilerOptions": {
"target": "es5",
"sourceMap": true
}
}
複製代碼
打包文件類型文件,並返回到 output.publicPath 中。
1.安裝包
npm install file-loader -D
複製代碼
2.配置
module.exports = {
entry: './app.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: './assets/'
},
rules: [
{
test: /\.(png|jpg|jpeg|webp|gif)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
// publicPath: './new-assets/'
}
}
}
]
};
複製代碼
3.組件
import avatar from './assets/avatar.jpg';
console.log(avatar); // ./assets/xxxxxxx.jpg
複製代碼
注意,配置中 file-loader 的 options.publicPath 會覆蓋 output.publicPath,優先級高些。
和 file-loader 做用相似,區別在於 url-loader 用戶能夠設置一個文件大小的閾值,若大於閾值其返回和 file-loader 同樣,若小於閾值返回文件以 base64 形式編碼。
1.安裝包
npm install url-loader -D
複製代碼
2.配置
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|jpeg|webp|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 10240,
name: '[name].[ext]',
publicPath: './assets/'
}
}
}
]
}
};
複製代碼
注意,options 參數多了 limit 。
咱們知道 vue 組件包含模板、js 和樣式。vue-loader 用來將模板、JS 和樣式拆分。因此還得額外安裝另外的預加載器。
vue-template-compiler
來編譯模板;css-loader
處理樣式;1.安裝包
npm install vue vue-loader vue-template-compiler css-loader -D
複製代碼
2.配置
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
use: 'vue-loader'
}
]
}
};
複製代碼
說了有表明性的幾個 xxx-loader,有時候咱們須要改寫或新建本身的 loader 來實現本身的一些願望或目的。好比如今要實現給全部 JS 文件啓動嚴格模式。
1.新建目錄 abc-strict-loader
,爲何是這麼名稱?後面安裝這個包後,從node_modules 中方便找,就在前面嘛; 2.進入 abc-strict-loader
目錄; 3.npm 初始化 npm init -y
4.新建文件 index.js
;
module.exports = function(content) {
// 處理 content
var useStrict = "// 這是 abc-strict-loader \n'use strict';\n\n";
return useStrict + content;
};
複製代碼
5.按說咱們應該先發布到 npm 這樣的社區,而後在項目中在安裝這個包,但是每當更改下就要重複上面 2 步,着實不是好辦法。這時可使用 npm 或 yarn 的軟鏈功能進行本地調試,等到符合咱們的預期需求後再發布到 npm 或 yarn 社區; 6.在項目工程目錄安裝 abc-strict-loader
,npm install ./abc-strict-loader
,此時項目的 node_modules 中會建立一個指向實際 abc-strict-loader
目錄的軟鏈,也就是說後面直接修改 abc-strict-loader
,那項目中的依賴 abc-strict-loader
也會有效; 7.更改 webpack.config.js 配置
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: 'abc-strict-loader'
}
]
}
};
複製代碼
8.啓動服務,查看,而後改動 abc-strict-loader
,而後再看下 node_modules/abc-strict-loader
是否發生變化;
若是文件和依賴包都沒有更改,那 loader 就直接使用緩存,而不是重複轉換。更改 abc-strict-loader/index.js
module.exports = function(content) {
// 判斷緩存
if (this.cacheable) {
console.log('緩存');
this.cacheable();
}
// 處理 content
var useStrict = "// 這是 abc-strict-loader \n'use strict';\n\n";
return useStrict + content;
};
複製代碼
1.首先,更改配置文件 webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'abc-strict-loader',
options: {
sourceMap: true
}
}
}
]
}
};
複製代碼
在 options
加入了 sourceMap:true
2.更改 abc-strict-loader
,這裏爲了獲取 webpack.config.js
中的配置,須要安裝包 loader-utils
。
const loaderUtils = require('loader-utils');
module.exports = function(content) {
// 判斷緩存
if (this.cacheable) {
console.log('緩存');
this.cacheable();
}
// source-map
var options = loaderUtils.getOptions(this) || {};
console.log('abc-strict-loader options: ', options); // abc-strict-loader options: { sourceMap: true }
// 處理 content
var useStrict = "// 這是 abc-strict-loader \n'use strict';\n\n";
return useStrict + content;
};
複製代碼
控制檯你會看到關於 webpack.config.js 文件中關於 options 的打印信息。
使用 loaderUtils.getOptions
獲取配置對象,接下來就要展現真正 source-map 功能了。source-map 便於開發者在瀏覽器查看源代碼。若是沒有對 source-map 處理,最終也生成不了 map 文件,那在瀏覽器 devtool 中可能會看到錯誤的源碼。
3.安裝依賴包 source-map。進入 abc-strict-loader
目錄。
npm install source-map
複製代碼
4.繼續更改 abc-strict-loader
文件。
const loaderUtils = require('loader-utils');
const SourceNode = require('source-map').SourceNode;
const SourceMapConsumer = require('source-map').SourceMapConsumer;
module.exports = function(content, sourceMap) {
const useStrict = "// 這是 abc-strict-loader \n'use strict';\n\n";
// 判斷緩存
if (this.cacheable) {
console.log('緩存');
this.cacheable();
}
// 支持 source-map
const options = loaderUtils.getOptions(this) || {};
if (options.sourceMap && sourceMap) {
const currentRequest = loaderUtils.getCurrentRequest(this);
const node = SourceNode.fromStringWithSourceMap(
content,
new SourceMapConsumer(sourceMap)
);
node.prepend(useStrict);
const result = node.toStringWithSourceMap({
file: currentRequest
});
const callback = this.async();
callback(null, result.code, result.map.toJSON());
}
// 不支持 source-map
return useStrict + content;
};
複製代碼
完整代碼可查看目錄 09 =>O(∩_∩)O~