写作不易,未经作者容许禁止以任何模式转载!
如果感觉文章不错,欢送关注、点赞和分享!
掘金原文链接
意识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
- 继续分享技术博文,关注微信公众号