关于前端:Webpack-Loader知识分享

30次阅读

共计 10758 个字符,预计需要花费 27 分钟才能阅读完成。

写作不易,未经作者容许禁止以任何模式转载!
如果感觉文章不错,欢送关注、点赞和分享!
掘金原文链接

意识 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
  1. 查找 PostCSS 在构建工具中的扩大,比方 webpack 中的 postcss-loader;
  2. 抉择能够增加你须要的 PostCss 相干插件

手动应用 PostCSS

应用 postcss-cli 操作,须要用到 autoprefixer 插件

  1. npm install postcss postcss-cli --save
  2. npm install autoprefixer --save
  3. npx postcss --use autoprefixer -o '输入门路' '输入门路' ,例如:

    1. npx postcss --use autoprefixer -o ./src/css/result.css ./src/css/user.css

Webpack 配置文件应用 PostCSS

  1. 装置 postcss-loader 和 autoprefixer

    • npm install postcss-loader autoprefixer --save
  2. 编写配置文件

    • 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

心愿解析、读取文件,例如图片

  1. 装置 file-loader

    • npm install --save file-loader
  2. 编写配置文件

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
  • 继续分享技术博文,关注微信公众号👇🏻

正文完
 0