上一章重学webpack系列(二) -- webpack解决的问题与实现模块化的具体实践,我们讲解了webpack
解决了前端项目在实施落地
的过程中遇到的一些问题,也初步去配置了webpack
的entry
、output
、mode
,实现了文件的打包,很明显在项目中我们不仅仅只有js
文件,还有一些非js
文件webpack
也需要去做处理,那么处理非js
文件的工具就是loaders
了,那么这一章我们就来了解一下loaders
究竟是何方神圣。
webpack怎么去加载非js文件
按照我们以往的经验,我们先尝试在文件目录中添加一个.css
文件,在入口entry
中配置css: "./src/css/index.css"
// src/css/index.css
.index {
display: flex;
justify-content: center;
align-items: center;
color: wheat;
background-color: red;
}
// webpack.config.json
entry: {
index: "./src/index.js",
add: "./src/add.js",
css: "./src/css/index.css"
},
我们打包的时候,控制台会出现报错:
出现这样的原因是因为,
css
的代码语法与js的完全不同,webpack
并不认识这样的语法,所以webpack
提示应该使用loaders
来解决这样的问题。那我们便可以去安装css-loader
并在webpack.config.json
里面配置css-loader
来解决这个问题了。
css-loader
// 安装css-loader
npm i css-loader -S
// webpack.config.json
module.exports = {
...
module:{
rules:[
{
test:/\.css$/, // 用正则来匹配适用的文件
use:'css-loader' // 用loader来处理此文件
}
]
}
...
}
样式效果
在这里我们就可以解决打包报错的问题了,如果你想使用这个
index.css
文件,你通过script
标签引入到页面中,你会发现,此css
文件的代码,并不能给节点加上样式。这是为什么呢?我们得去看一看打包过后的文件。在文件里我们可以看到这样的一段代码
var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".index {\n color: wheat;\n background-color: red;\n}", "",{"version":3,"sources":["webpack://./src/css/index.css"],"names":[],"mappings":"AAAA;EACE,YAAY;EACZ,qBAAqB;AACvB","sourcesContent":[".index {\n color: wheat;\n background-color: red;\n}"],"sourceRoot":""}]);
// Exports
const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);
上面这段代码,出现了我们css
中写的样式,表现为拆分了css
结构成一个字符串,然后通过__WEBPACK_DEFAULT_EXPORT__
暴露到当前的js
文件中,换句话说就是css-loader
只是加载css
模块,但是并没有使用css
模块。为了解决这个问题,我们还需要依赖另外一个loader
。
style-loader
// 安装css-loader
npm i style-loader -S
// webpack.config.json
module.exports = {
...
module:{
rules:[
{
test:/\.css$/, // 用正则来匹配适用的文件
use:[
style-loader,
css-loader
]
// 用loader来处理此文件,如果是多个loader则必须要把style放在最前面,
// 因为style是使用,而其他的是加载、处理。
}
]
}
...
}
样式效果
扩展
为什么style-loader
可以使用css
样式呢?我们发现在style-loader
打包之后,生成了这样的一串代码。
function insertStyleElement(options) {
// 创建style标签
var element = document.createElement("style");
// 设置标签以及标签属性
options.setAttributes(element, options.attributes);
// 插入到html中
options.insert(element, options.options);
return element;
}
原来style-loader
的原理就是在页面创建了一个style
标签去使用那些属性。
在js文件中引入css文件
如果我们单独打包css
文件的话,那么我们就需要在使用层面,单独对js
(源文件是css
文件)文件去引入使用,我们也可以在js
文件中使用import './css/index.css'
来进行加载css
文件到js
文件中,实现统一打包,其实webpack
也建议我们这样做。
// add.js
import './css/index.css'
// webpack.config.json
module.exports = {
mode: "development",
entry: {
add: "./src/add.js"
}
...
}
loader 特性
loader
支持链式传递,一组链式的loader
将按照相反的顺序执行。loader
链中的第一个loader
返回值给下一个loader
。在最后一个loader
,返回webpack
所预期的JavaScript
。loader
可以是同步
的,也可以是异步
的。loader
运行在Node.js
中,并且能够执行任何可能的操作。loader
接收查询参数。用于对loader
传递配置。loader
也能够使用options
对象进行配置。- 除了使用
package.json
常见的main
属性,还可以将普通的 npm 模块导出为 loader,做法是在package.json
里定义一个loader
字段。 - 插件(
plugin
)可以为loader
带来更多特性。 loader
能够产生额外的任意文件。
loader
通过(loader)预处理函数,为JavaScript
生态系统提供了更多能力。 用户现在可以更加灵活地引入细粒度逻辑,例如压缩、打包等。
常见loader的基础实践
-
babel-loader
- 作用:将Es6+ 语法转换为Es5语法。
- 配置
// 安装 npm i babel-loader @babel/core @babel/preset-env -S // webpack.config.json module.exports = { module: { rules: [ { test: /\.js$/, use: { loader: "babel-loader", options: { presets: [ ['@babel/preset-env', { targets: "defaults"}] ] } } }, ] }
-
less-loader / scss-loader、style-loader、css-loader
- 作用:处理css/scss/less文件。
- 配置
// 安装 npm i less-loader / scss-loader css-loader style-loader -S // webpack.config.json module.exports = { module: { rules: [ { test: /\.less$/, // test: /\.scss$/, use: [ 'style-loader', 'css-loader', ’less-loader‘ // 'scss-loader' ] }, ] }
-
file-loader
- 作用:用于处理文件类型资源,如
jpg
,png
等图片。返回值为publicPath
为准。 - 配置
// 安装 npm i file-loader -S // webpack.config.json module: { rules: [ { test: /\.(png|jpg|jpeg)$/, use: [ { loader: "file-loader", options: { name: "[name]_[hash:8].[ext]", // .ext为文件扩展名eg: .png publicPath: "https://www.baidu.com" } } ] } ] }
- 作用:用于处理文件类型资源,如
-
url-loader
- 作用:处理图片资源,根据大小选择打包方式。
- 配置
// 安装 npm i url-loader -S // webpack.config.json module: { rules: [ { test: /\.(png|jpg|jpeg)$/, use: [ { loader: "url-loader", options: { name: "[name]_[hash:8].[ext]", limit: 10240, // 这里单位为(b) 10240 => 10kb // 这里如果小于10kb则转换为base64打包进js文件,如果大于10kb则打包到dist目录 } } ] } ] }
-
eslint-loader
- 作用:用于检查代码是否符合规范,是否存在语法错误,需要配合.eslintrc.js使用。
- 配置
// 安装 npm i npm i eslint-loader eslint -S // webpack.config.json module: { rules: [ { test: /\.ts$/, use: ["eslint-loader", "ts-loader"], enforce: "pre", exclude: /node_modules/ // 除此之外的文件夹 } ] }
-
postcss-loader
- 作用:用于补充css样式各种浏览器内核前缀。
- 配置
// 安装 npm i postcss-loader -S // webpack.config.json module: { rules: [ { test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader", "postcss-loader" ], include: /src/, }, ] }
-
ts-loader
- 作用:编译ts文件,需要配合tsconfig.json使用。
- 配置
// 安装 npm i ts-loader -S // webpack.config.json module: { rules: [ { { test: /\.ts$/, use: "ts-loader", } } ] }
为什么webpack建议我们在js文件中引入其他资源
webpack
不仅仅建议我们在js
中引入css
资源,只要是是当前项目依赖所有资源,webpack
都建议我们在js
文件中引入。为什么webpack
要这样设计呢?
-
便于统一资源管理
- 如果我们单独去打包某个与
js
产生依赖的css
、图片
、字体
等,那么我们将会有额外的对html
页面的操作。 - 如果这一部分功能废弃掉了,那么我们有必须要去操作
html
页面,把不需要的模块资源移除掉,增加了额外
操作。
- 如果我们单独去打包某个与
-
使逻辑更合理化
js
代码肯定是依赖这些非js
资源,才能实现整体
的功能的。
-
防止必要文件丢失
- 通过
依赖
关系,可以最大程度上保证资源文件不会丢失
,在生产环境下可以避免重要经济损失
。
- 通过
实现一个自己的loader
步骤
- 创建一个非
js
文件。 - 引入到打包入口
js
文件中。 - 创建一个自己命名的
loader
。 - 在
webpack.config.json
中配置自己的loader
。
loader的基本要求
loader
机制告诉我们在函数处理结果返回的时候,必须是一段JavaScript
可执行的代码。
具体代码
// mkdown-loader.js
const {marked} = require('marked'); // 借用marked库,让md文件内容转化为html内容
module.exports = (moduleId) => {
const html = marked(moduleId)
// const htmlString = `module.exports = ${JSON.stringify(html)}`
// const htmlString = `export default ${JSON.stringify(html)}`
return html
}
// webpack.confih.json
module.exports = {
module:{
rules:[
{
test:/\.md$/,
use:[
'html-loader',
'./src/mkdown-loader.js'
]
}
]
},
}
使用html-webpack-plugin插件
我们先定义一个template
模板当做插件模板。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="index"></div>
</body>
</html>
配置插件之后的webpack.config.json
// webpack.confih.json
module.exports = {
module:{
rules:[
{
test:/\.md$/,
use:[
'html-loader',
'./src/mkdown-loader.js'
]
}
]
},
plugins:[
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
titie: 'title',
template: './index.html'
}),
],
}
之后在控制台就会输出一段html
文本
我们再去看看打包后的代码的样子
获取loader的配置参数
如果我们在使用loader的时候,想给它传递一些配置项,比如
// webpack.config.json
module.exports = {
...
module: {
rules: [
{
test:/\.md$/,
use:[
{
loader: 'html-loader'
},
{
loader: './src/mkdown-loader.js',
options: {
name: '一溪之石',
age: 22,
sex: '男'
}
}
]
}
]
}
}
// mkdown-loader.js
module.exports = function(moduleId){
// 可以通过this.getOptions来获取options里面的参数
const options = this.getOptions();
const { name = 'null', age = 'null', sex='null' } = options;
const html = marked(moduleId);
console.log(html);
// .... 在loader里面处理其他逻辑
return html; // 返回给下一个loader处理
}
开发一个loader常用的Api
this.async
:获取一个callback
函数,处理异步。this.callback
:同步loader
中,返回的方法。this.emitFile
:产生一个文件。this.getOptions
:根据传入的schema
获取对应参数,在webpack5
中,schema
已经不用再写了,getOptions
可以获取到上下文中的配置参数。this.importModule
:用于子编译器在构建时编译和执行请求。this.resourcePath
:当前资源文件的路径。
总结
好了关于webpack
的loader
的机制,介绍到这里也就结束了,各位同学也看到了本文中使用到了html-webpack-plugin
这个插件,插件确实能够让我们的项目啊,loader
啊具有更丰富的功能,下一章我们就会探索一下webpack
的插件机制,直通车 >>> 重学webpack系列(四) -- webpack的plugins机制的解读