前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Webpack Loader知识分享

Webpack Loader知识分享

作者头像
前端LeBron
发布2021-12-08 16:34:50
4910
发布2021-12-08 16:34:50
举报
文章被收录于专栏:前端LeBron前端LeBron

原创不易,未经作者允许禁止转载!!

认识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是一个对象,对象中可以设置多个属性
    • 执行顺序从后往前,比如解析css,css-loader应在style-loader后面。
    • UseEntry是一个对象,可以通过对象的属性来设置一些其他属性
    • 传递字符串(如:use:['style-loader'])是loader属性的简写(如:use:[{loader:'style-loader'}])
    • loader:必须有一个loader属性,对应的值是一个字符串
    • options:可选的属性,值是一个字符串或者对象,值会被传入到loader中;
    • query:目前已被options替代
    • test属性:用于多resource(资源)文件名进行匹配,通常会设置成正则表达式;
    • use属性:对应的值是一个数组[useEntry]

常见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的配置文件
代码语言:javascript
复制
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
代码语言:javascript
复制
{
        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
    • 例如八位的十六进制颜色会帮我们转换成RGBA
    • postcss-preset-env也是一个postcss的插件;
    • 它可以帮助我们将一些现代的CSS特性,转成大多数浏览器认识的CSS,并且会根据目标浏览器或者运行时环 境添加所需的polyfill;
    • 也包括会自动帮助我们添加autoprefixer(所以相当于已经内置了autoprefixer);
    • 首先,我们需要安装postcss-preset-env: npm install postcss-preset-env --save
    • 之后,我们直接修改掉之前的autoprefixer即可:
  • 配置简化
    • 在use中只写postcss-loader
    • 在项目根目录建立postcss.config.js,写入配置

webpack.config.js

代码语言:javascript
复制
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

代码语言:javascript
复制
module.exports = {
  plugins: [
      // 引入
      // require("postcss-preset-env")
      //简写
      "postcss-preset-env"
    ],
};

效果

Before

代码语言:javascript
复制
:fullscreen{

}

.content{
    user-select: none;
    transition: all 2s ease;
}

After

代码语言:javascript
复制
:-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的数量
  • 例如:
代码语言:javascript
复制
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/
代码语言:javascript
复制
{
        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,并且配置资源
  • 比如加载图片,我们可以使用下面的方式:
代码语言:javascript
复制
// 打包图片
      {
        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;
代码语言:javascript
复制
// 方式一 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对象:

代码语言:javascript
复制
{
        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
代码语言:javascript
复制
{
    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属性
代码语言:javascript
复制
module: {
        rules:[
            {
                test:/\.js$/i,
                use:[
                    "demoLoader"
                ]
            }
        ]
},
resolveLoader:{
    modules:["./loaders", "node_modules"]
}
  • 多个Loader使用执行顺序
    • 从后向前,从右向左
代码语言:javascript
复制
rules:[
    {
        test:/\.js$/i,
        use:[
            "demoLoader1",
            "demoLoader2",
            "demoLoader3",
        ]
    }
]
  • 结果

image.png

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
代码语言:javascript
复制
module.exports = function (content){
    console.log("loader", content)
    this.callback(null,content)
}

异步的Loader

  • 什么是异步的Loader
    • 有时候使用Loader进行一些异步的操作
    • 我们希望在异步操作完成之后,再返回这个loader处理的结果
    • 这个时候就要使用异步的Loader了
  • loader-runner已经在执行loader时给我们提供了方法,让loader变成一个异步的loader
  • loader.js
代码语言:javascript
复制
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
代码语言:javascript
复制
{
    test:/\.js$/i,
    use:[
        "syncLoader",
        "asyncLoader",
        {
            loader:"optionLoader",
            options:{
                type:"options",
                desc:"demo"
            }
        }
    ]
}
  • optionLoader.js
代码语言:javascript
复制
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(校验规则)
代码语言:javascript
复制
{
  "type": "objcet",
  "properties": {
    "type": {
      "type": "string",
      "description": "请输入正确的参数类型"
    },
    "desc": {
      "type": "string",
      "description": "描述必须为字符串类型"
    }
  },
  "additionalProperties": true
}
  • loader.js
代码语言:javascript
复制
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
代码语言:javascript
复制
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
代码语言:javascript
复制
{
  "type": "object",
  "properties": {
    "presets": {
      "type": "array"
    }
  },
  "additinalProperties": true
}
  • webapck.config.js
代码语言:javascript
复制
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加载规则
代码语言:javascript
复制
rules:[
    {
      test:/\.css$/i,
      use:[
          "style-loader",
          "css-loader"
      ]
    },
    {
      test:/\.md$/i,
      use:["mdLoader"]
    },
]
  • mdLoader.js
    • 使用marked和highlight处理content,并返回模块化代码
代码语言:javascript
复制
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文件
代码语言:javascript
复制
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)
  • 效果
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端LeBron 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 认识Loader
    • loader配置方式
    • 常见Loader
      • CSS loader
        • 浏览器兼容性
          • 认识PostCSS工具
            • 手动使用PostCSS
            • Webpack配置文件使用PostCSS
            • postcss-preset-env
            • 效果
          • importLoaders
            • File Loader
              • Asset module type
                • 加载字体文件
            • 自定义Loader
              • Pitch-Loader和enforce
                • 同步的Loader
                  • 异步的Loader
                    • 传入和获取参数
                      • 参数校验
                        • babel-loader案例
                          • 自定义md-loader
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档