问题引入
为webpack
官网中提到,webpack
是可以打包所有资源的。
但是我们在主文件中导入一个css文件或者其他文件类型时打包虽然可以成功,但是会抛出一个错误异常You may need an appropriate loader to handle this file type
,这个时候我们就可以使用loader
解决这个问题。
loader 的作用
loader
用于对模块的源代码进行转换,也就是我们可以去将源代码进行编辑在进行打包。
使用loader
前的chunk
解析模块
使用loader
后chunk
解析模块
loader 的配置
在使用loader
前我们现在webpack.config.js
文件中配置module
,module
决定了如何处理不同的模块。
// webpack.config.js
module.exports = {
mode: 'development', // 开发模式,方便看打包后的代码
module: {
rules: [{
test: /index\.js$/,
use: [{
loader: './loaders/loader.js'
}]
}]
}
}
module.rules
数组里存放的是需要处理的文件,test
为匹配的规则,use
为使用当前loader
的处理函数。
loader 的应用
我们先来看loader
的作用,当我在打包主文件内输入了一个错误的语法,我们可以成功打包且抛出错误,但是这样的文件我们浏览器是不认识的,这个时候我们的loader
就起到了关键性的作用。
// ./src/index.js
变量 a = 123;
变量 b = 456;
console.log(b + b);
我们一般把loader
处理函数放在一个统一的loaders
文件夹里,形参sourceCode
记录着目标文件的源代码,return
返回的是处理过后的源代码,经过loader
处理过后打包我们就可以直接使用了。
// ./loaders/loader.js
module.exports = function(sourceCode) {
return sourceCode.replace(/变量/g, 'var');
}
loader 处理 非 js 文件
我们再来看看对于css文件
的处理。
// webpack.config.js
module.exports = {
mode: 'development',
module: {
rules: [{
test: /index\.js$/,
use: [{
loader: './loaders/loader.js'
}]
}, {
test: /style\.css$/,
use: [{
loader: './loaders/styleloader.js'
}]
}]
}
}
webpack
中遵循的是CommonJS
规范,我们需要将css
样式交给打包好的mian.js
交给浏览器来追加样式,所以需要返回的是浏览器可以解析的语法。
// ./loaders/styleloader.js
module.exports = function(sourceCode) {
return `var style = document.createElement('style');
style.innerHTML = \`${sourceCode}\`;
document.head.appendChild(style);`
}
loader 传递参数
我们前面写到变量 a = 123;
,然后在loader
中使用使用 var
替换到 变量
,这是一个固定的,我们可以通过参数的形式给loader
,参数保存在this
中。
// webpack.config.js
module.exports = {
mode: 'development',
module: {
rules: [{
test: /index\.js$/,
use: [{
loader: './loaders/loader.js',
options: {
changeVar: '变量'
}
}]
}]
}
}
loader-utils 模块
这个this
通过打印我们可以发现是非常庞大的,我们可以使用第三方模块loader-utils
进行解析。
// ./loaders/loader.js
const loaderUtils = require('loader-utils');
module.exports = function(sourceCode) {
const rex = new RegExp(loaderUtils.getOptions(this).changeVar, 'g')
return sourceCode.replace(rex, 'var')
}
我们还可以直接简写为一下形式,直接在通过 ?
传递参数。
module.exports = {
mode: 'development',
module: {
rules: [{
test: /index\.js$/,
use: ['./loaders/testloaders.js?changeVar=变量']
}]
}
}
loader 的执行顺序
当单个模块满足了某个规则后追加在loaders
数组中,然后依次从loaders
数组中从后向前执行。
module.exports = {
mode: 'development',
module: {
rules: [{
test: /index\.js$/,
use: ['./loaders/loader1.js', './loaders/loader2.js']
}, {
test: /\.js$/,
use: ['./loaders/loader3.js', './loaders/loader4.js']
}]
}
}
当我index.js
中没有引入外部文件时,执行顺序是loader4,loader3,loader2,loader1
。
当我在index.js
引入了a.js
时,在第一次执行了后又再一次的匹配到了第二个规则,所以执行顺序是loader4,loader3,loader2,loader1,loader4,loader3
。