写作不易,未经作者容许禁止以任何模式转载!
如果感觉文章不错,欢送关注、点赞和分享!
掘金原文链接
意识 Loader
Loader 能够用于对模块的源代码进行转换;
在加载这个模块时,webpack 其实并不知道如何对其进行加载,咱们必须定制对应的 loader 来实现这个性能。
loader 配置形式
- 内联形式:
import "css-loader!../css/index.css"
; loader 和文件门路用!分隔 - 配置形式:webpack.config.js
-
配置形式示意的意思是在咱们的 webpack.config.js 文件中写明配置信息
- module.rules 中容许咱们配置多个 loader(因为咱们也会持续应用其余 Loader,来实现其余文件的加载)
- 这种形式能够更好地标识 loader 配置,也不便前期的保护,同时也让你对各个 Loader 有一个全局的概览;
- module.rules 的配置如下
- rules 属性对应的值是一个数组:[Rule]
-
数组中寄存的是一个个 Rule,Rule 是一个对象,对象中能够设置多个属性
- test 属性:用于多 resource(资源)文件名进行匹配,通常会设置成正则表达式;
-
use 属性:对应的值是一个数组 [useEntry]
- 执行程序从后往前,比方解析 css,css-loader 应在 style-loader 前面。
-
UseEntry 是一个对象,能够通过对象的属性来设置一些其余属性
- loader:必须有一个 loader 属性,对应的值是一个字符串
- options:可选的属性,值是一个字符串或者对象,值会被传入到 loader 中;
- query:目前已被 options 代替
- 传递字符串(如:use:[‘style-loader’])是 loader 属性的简写(如:use:[{loader:’style-loader’}])
常见 Loader
CSS loader
- 咱们能够将 css 文件也看成是一个模块,咱们是通过 import 来加载这个模块的
-
那么须要一个什么样的 loader 呢
- 对于加载 css 文件来说,咱们须要一个能够读取 css 文件的 loader
- 最罕用的是 css-loader
-
只负责解析 css 文件,不会作用于页面,此外还须要 style-loader 作用于页面
npm i style-loader --save
-
css-loader 的装置:
npm install css-loader --save
- 以下为一个 css-loader 的配置文件
const path = require("path");
module.exports = {
entry: "./src/js/main.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
test: /\.css$/, // 资源文件匹配正则表达式
use: [
{
loader: "style-loader",
options: {},},
// 简写://“css-loader”{
// 残缺写法
loader: "css-loader", // 对应的 loader
options: {},},
],
},
],
},
};
-
less 同理
npm install less-loader less --save
装置这两个插件后 - 顺次在 use 数组内写入 style-loader、css-loader、less-loader
浏览器兼容性
-
如果在 css 兼容性和 js 兼容性下共享咱们的配置兼容性条件呢
- 就是当我么们设置了一个条件:>1% last 2 version Firefox、Chrome… not dead 都是可选项,多个选项以,分隔为 || 条件;and 为 && 关系;可用 not 条件
- 意思为 js、css 兼容市场占有率大于 1% 的 Firefox、Chrome 浏览器和最新的两个版本,并且 24 个月内有官网反对和更新的浏览器(dead 过滤条件)
- 在根目录.browserlistrc 文件编写条件
- 通过可实现对市场占有比率的要去,版本要求等进行配置,适配浏览器
-
所用工具:
- 数据起源:browserlist,浏览器市场占有率,准确到每个版本
- 解决插件:autoprefixer、babel、postcss-preset-env 等
意识 PostCSS 工具
-
什么是 PostCSS 工具呢?
- PostCSS 是一个通过 JavaScript 来转换款式的工具
- 这个工具能够帮忙咱们进行一些 CSS 款式的转换和适配,比方主动增加浏览器前缀、css 款式的充值;
- 然而实现这些性能,咱们须要借助于 PostCSS 插件
- 如何应用 PostCSS
- 查找 PostCSS 在构建工具中的扩大,比方 webpack 中的 postcss-loader;
- 抉择能够增加你须要的 PostCss 相干插件
手动应用 PostCSS
应用 postcss-cli 操作,须要用到 autoprefixer 插件
npm install postcss postcss-cli --save
npm install autoprefixer --save
-
npx postcss --use autoprefixer -o '输入门路' '输入门路'
,例如:npx postcss --use autoprefixer -o ./src/css/result.css ./src/css/user.css
Webpack 配置文件应用 PostCSS
-
装置 postcss-loader 和 autoprefixer
npm install postcss-loader autoprefixer --save
-
编写配置文件
- css-loader 解决前先用 postcss-loader 解决
- 在 options 的 postcssOptions 中,配置 plugins,应用 autoprefixer
{
test: /\.css$/, // 资源文件匹配正则表达式
use: [
{
loader: "style-loader",
options: {},},
// 简写://“css-loader”{
// 残缺写法
loader: "css-loader", // 对应的 loader
options: {},},
{
loader: "postcss-loader",
options: {
postcssOptions: {plugins: [require("autoprefixer")],
},
},
},
],
},
postcss-preset-env
- 事实上,在配置 postcss-loader 时,咱们配置插件并不需要应用 autoprefixer。
-
咱们能够应用另外一个插件:postcss-preset-env
- postcss-preset-env 也是一个 postcss 的插件;
-
它能够帮忙咱们将一些古代的 CSS 个性,转成大多数浏览器意识的 CSS,并且会依据指标浏览器或者运行时环 境增加所需的 polyfill;
- 例如八位的十六进制色彩会帮咱们转换成 RGBA
- 也包含会主动帮忙咱们增加 autoprefixer(所以相当于曾经内置了 autoprefixer);
-
首先,咱们须要装置 postcss-preset-env:
npm install postcss-preset-env --save
- 之后,咱们间接批改掉之前的 autoprefixer 即可:
-
配置简化
- 在 use 中只写 postcss-loader
- 在我的项目根目录建设 postcss.config.js,写入配置
webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/js/main.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
test: /\.css$/, // 资源文件匹配正则表达式
use: [
{
loader: "style-loader",
options: {},},
// 简写://“css-loader”{
// 残缺写法
loader: "css-loader", // 对应的 loader
options: {},},
"postcss-loader",
],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "postcss-loader", "less-loader"],
},
],
},
};
postcss.config.js
module.exports = {
plugins: [
// 引入
// require("postcss-preset-env")
// 简写
"postcss-preset-env"
],
};
成果
Before
:fullscreen{
}
.content{
user-select: none;
transition: all 2s ease;
}
After
:-webkit-full-screen{
}
:-ms-fullscreen{
}
:fullscreen{
}
.content{
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
transition: all 2s ease;
}
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInVzZXIuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztBQUVBOztBQUZBOztBQUVBOztBQUZBOztBQUVBOztBQUVBO0lBQ0kseUJBQWlCO09BQWpCLHNCQUFpQjtRQUFqQixxQkFBaUI7WUFBakIsaUJBQWlCO0lBQ2pCLHVCQUF1QjtBQUMzQiIsImZpbGUiOiJyZXN1bHQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiOmZ1bGxzY3JlZW57XG5cbn1cblxuLmNvbnRlbnR7XG4gICAgdXNlci1zZWxlY3Q6IG5vbmU7XG4gICAgdHJhbnNpdGlvbjogYWxsIDJzIGVhc2U7XG59Il19 */
importLoaders
- 失常 css 文件解析遇到 @import,不会再从 use 数组最初一个 loader 开始从新解析
- 通过设置 importLoaders 能够实现递归解析
- 填写的数字为在 use 数组中往后的 loader 的数量
- 例如:
const path = require("path");
module.exports = {
entry: "./src/js/main.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
test: /\.css$/, // 资源文件匹配正则表达式
use: [
{
loader: "style-loader",
options: {},},
// 简写://“css-loader”{
// 残缺写法
loader: "css-loader", // 对应的 loader
options: {importLoaders: 1,},
},
"postcss-loader",
],
},
{
test: /\.less$/,
use: [
"style-loader",
{
// 残缺写法
loader: "css-loader", // 对应的 loader
options: {importLoaders: 2,},
},
"postcss-loader",
"less-loader",
],
},
],
},
};
File Loader
心愿解析、读取文件,例如图片
-
装置 file-loader
npm install --save file-loader
- 编写配置文件
rule:
-
options 解释:
- name: 输入名称,[name] 名称援用,. 字符,[hash:6] 哈希信息截取 6 位,[ext] 后缀名
- ouputPath: 输入的文件夹,也能够在文件名前加 img/
{test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader:"file-loader",
options:{name:"[name].[hash:6].[ext]",
outputPath:"img"
// limit(url-loader)}
}
],
},
-
和 file-loader 有同样性能的还有 url-loader
- 不同的是,它不是打包,而是把图片转为 BASE64 编码。
- 能够通过 use 对象的 options 下写入 limit 属性,限度大小,对小图片进行 BASE4 编码,缩小申请数。
Asset module type
-
咱们以后应用的 webpack 版本是 webpack5:
- 在 webpack5 之前,加载这些资源咱们须要应用一些 loader,比方 raw-loader、url-loader、file-loader;
- 在 webpack5 之后,咱们能够间接应用资源模块类型 (asset module type),来代替下面的这些 loader;
-
资源模块类型 (asset module type),通过增加 4 种新的模块类型,来替换所有这些 loader:
- asset/resource 发送一个独自的文件并导出 URL。之前通过应用 file-loader 实现;
- asset/inline 导出一个资源的 data URI。之前通过应用 url-loader 实现;
- asset/source 导出资源的源代码。之前通过应用 raw-loader 实现;
- asset 在导出一个 data URI 和发送一个独自的文件之间主动抉择。之前通过应用 url-loader,并且配置资源
- 比方加载图片,咱们能够应用上面的形式:
// 打包图片
{test: /\.(png|jpe?g|gif|svg)$/,
type: "asset/resource",
generator: {filename: "img/[name].[hash:6][ext]",
},
},
// BASE64 编码并限度大小
{test: /\.(png|jpe?g|gif|svg)$/,
type: "asset/inline",
parser: {
dataUrlCondition: {maxSize: 100 * 1024,},
},
},
// 混合应用,限度大小,判断打包形式
{test: /\.(png|jpe?g|gif|svg)$/,
type: "asset",
generator: {filename: "img/[name].[hash:6][ext]",
},
parser: {
dataUrlCondition: {maxSize: 100 * 1024,},
},
},
-
然而,如何能够自定义文件的输入门路和文件名呢?
- 形式一: 批改 output,增加 assetModuleFilename 属性;
- 形式二: 在 Rule 中,增加一个 generator 属性,并且设置 filename;
// 形式一 ouput 对象
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "build"),
assetModuleFilename: "img/[name].[hash:6][ext]",
},
// 形式二 rule 对象(举荐){test: /\.(png|jpe?g|gif|svg)$/,
type: "asset/resource",
generator: {filename: "img/[name].[hash:6][ext]",
},
}
加载字体文件
rule 对象:
{
test: /\.ttf|eot|woff2?$/i,
type: "asset/resource",
generator: {filename: "font/[name].[hash:6][ext]",
},
},
自定义 Loader
Loader 是用于对模块的源代码进行转换解决,之前曾经应用过很多 Loader,例如 css-loader、style-loader、babel-loader 等
- Loader 实质是一个导出为函数的 JavaScript 模块
- Loader runner 库会调用这个函数,而后将上一个 loader 产生的后果或者资源文件传入进去。
-
编写一个自定义 Loader 会接管三个参数
- content:资源文件参数
- map:sourcemap 相干数据
- meta:一些元数据
-
注:传入的门路是和 content 有关系的
- webpack.config.js
{context: path.resolve(__dirname, "../"),
entry: "./src/main.js",
output: {path:path.resolve(__dirname,"../build"),
filename:"bundle.js",
},
module: {
rules:[
{
test:/\.js$/i,
use:["./loaders/demoLoader.js"]
}
]
}
}
- 如果心愿间接去加载 loader 文件夹,能够配置 resoveLoader 属性
module: {
rules:[
{
test:/\.js$/i,
use:["demoLoader"]
}
]
},
resolveLoader:{modules:["./loaders", "node_modules"]
}
-
多个 Loader 应用执行程序
- 从后向前,从右向左
rules:[
{
test:/\.js$/i,
use:[
"demoLoader1",
"demoLoader2",
"demoLoader3",
]
}
]
- 后果
Pitch-Loader 和 enforce
事实上还有另一种 Loader,称之为 PitchLoader
-
其实这也是为什么 loader 的执行程序是相同的
- run-loader 先优先执行 PitchLoader,在执行 PitchLoader 时进行 loaderIndex++;
- run-loader 之后会执行 NormalLoader,在执行 NormalLoader 时进行 loaderIndex–;
-
那么,怎么扭转他们的执行程序?
- 能够拆分成多个 Rule 对象,通过 enforce 来扭转它们的程序
- enforce 是 rule 对象的一个属性。
-
enforce 一共有四种形式
- 默认所有 loader 都是 normal
- 在行内设置的 loader 都是 inline
- 也能够通过 enforce 设置 pre 和 post
-
在 Pitch 和 Normal 它们的执行程序别离是
- post、inline、normal、pre
- pre、normal、inline、post
同步的 Loader
-
什么是同步 loader?
- 默认创立的 Loader 就是同步的 Loader
- 这个 Loader 必须通过 return 或者 this.callback 来返回后果,交给下一个 loader 来解决
- 通常在有谬误的状况下,会应用 this.callback
-
this.callback 用法如下
- 第一个参数为 ERR 或者 null
- 第二个参数是 string 或者 buffer
- loader.js
module.exports = function (content){console.log("loader", content)
this.callback(null,content)
}
异步的 Loader
-
什么是异步的 Loader
- 有时候应用 Loader 进行一些异步的操作
- 咱们心愿在异步操作实现之后,再返回这个 loader 解决的后果
- 这个时候就要应用异步的 Loader 了
- loader-runner 曾经在执行 loader 时给咱们提供了办法,让 loader 变成一个异步的 loader
- loader.js
module.exports = function (content){const callback = this.async()
setTimeout(()=>{console.log("async loader",content)
callback(null, content)
})
}
传入和获取参数
- 在应用 loader 时传入参数,
-
能够通过 webpack 官网提供的一个解析库 loader-utils
npm i loader-utils
- webpack.config.js
{
test:/\.js$/i,
use:[
"syncLoader",
"asyncLoader",
{
loader:"optionLoader",
options:{
type:"options",
desc:"demo"
}
}
]
}
- optionLoader.js
const {getOptions} = require("loader-utils")
module.exports = function (content){const callback = this.async();
const options = getOptions(this)
setTimeout(()=>{console.log("asyncLoader",content, options)
callback(null, content)
})
}
// asyncLoader console.log("main.js") {type: 'options', desc: 'demo'}
参数校验
-
能够通过 wepack 官网提供的校验库 schema-utils
npm i schema-utils
- schema.json(校验规定)
{
"type": "objcet",
"properties": {
"type": {
"type": "string",
"description": "请输出正确的参数类型"
},
"desc": {
"type": "string",
"description": "形容必须为字符串类型"
}
},
"additionalProperties": true
}
- loader.js
const {getOptions} = require("loader-utils")
const {validate} = require("schema-utils")
const loaderSchema = require("../schema/schema.json")
module.exports = function (content){const callback = this.async();
const options = getOptions(this)
validate(loaderSchema, options)
setTimeout(()=>{console.log("asyncLoader",content, options)
callback(null, content)
})
}
babel-loader 案例
- 咱们晓得 babel-loader 能够进行 JS 代码转换
- 接下来尝试定义一个本人的 babel-loader
- myBabelLoader.js
const babel = require("@babel/core")
const {getOptions} = require("loader-utils")
const {validate} = require("schema-utils")
const babelSchema = require("../schema/babel.json")
module.exports = function (content){const callback = this.async();
const option = getOptions(this);
validate(babelSchema, option)
babel.transform(content, option, (err, res) =>{if(err){callback(err)
}else{callback(null, res.code)
}
})
}
- babelSchema.json
{
"type": "object",
"properties": {
"presets": {"type": "array"}
},
"additinalProperties": true
}
- webapck.config.js
module: {
rules:[
{
test:/\.js$/i,
use:[
"syncLoader",
"asyncLoader",
{
loader:"optionLoader",
options:{
type:"options",
desc:"demo"
}
},
{
loader:"myBabelLoader",
options: {presets:["@babel/preset-env"]
}
}
]
}
]
},
自定义 md-loader
-
借助 marked 和 highlight 库实现 markdownLoader
npm i marked highlight.js
-
webpack.config.js
- 配置 css 加载和 md 加载规定
rules:[
{
test:/\.css$/i,
use:[
"style-loader",
"css-loader"
]
},
{
test:/\.md$/i,
use:["mdLoader"]
},
]
-
mdLoader.js
- 应用 marked 和 highlight 解决 content,并返回模块化代码
const marked = require("marked")
const hljs = require("highlight.js")
module.exports = function (content){
marked.setOptions({highlight:function (code,lang){return hljs.highlight(lang, code).value
}
})
const htmlContent = marked(content)
const innerContent = "`" + htmlContent + "`"
const moduleCode = `var code=${innerContent};export default code;`
console.log(moduleCode)
return moduleCode;
}
-
main.js
- 同时引入 css 文件
import md from "./index.md"
import "highlight.js/styles/default.css"
console.log("main.js")
const ele = document.createElement("div")
ele.innerHTML = md;
document.body.appendChild(ele)
- 成果
- 掘金原文链接
- 掘金:前端 LeBron
- 知乎:前端 LeBron
- 继续分享技术博文,关注微信公众号👇🏻