Webpack--loader

一、概述

(1)webpack本身只认识javascript,对于其他类型的资源必须预先定义一个或多个loader对其进行转译,输出为webpack能够接收的形式再继续进行。因此loader做的实际上是一个预处理的工作。

(2)loader本质上是一个函数。在webpack4之前,函数的输入和输出都必须为字符串;在webpack4之后,loader也同时支持抽象语法树(AST)的传递,通过这种方法来减少重复的代码解析。

output = loader(input)

1、input可能是工程源文件的字符串,或者是上一个loader转化后的结果,包括转化后的结果(也是字符串类型)、source map,以及AST对象。

2、output同样包含这几种信息,转化后的文件字符串、 source map,以及AST。

3、如果这是最后一个loader,结果将直接被送到webpack进行后续处理,否则将作为下一个loader的输入向后传递。

4、loader是可以链式的

(3)源码结构

module.exports = function loader(content, map, meta) {
  var callback = this.async();
  var result = handler(content, map, meta);
  callback(
    null,//error
    result.content,//转换后的内容
    result.map,//转换后的source-map
    result.meta,//转换后的AST
  );
}

二、loader的配置

与loader相关的配置都在module对象中,其中module.rules代表了模块的处理规则。每条规则可以包含很多配置项,这里只使用了最重要的两项(test和use)

module.exports = {
  module: {
    rules: [{
      test: /\.css$/,
      use: ['style-loader','css-loader']
    }]
  }
}

test:可接收一个正则表达式或者一个元素为正则表达式的数组,只有正则匹配上的模块才会使用这条规则。

use:可接收一个数组,数组包含该规则所使用的loader。

(1)css-loader:处理CSS各种加载语法(@import和url()函数等)

(2)style-loader:将样式插入页面

(3)webpack打包时是按照数组从后往前的顺序将资源交给loader处理的,因此要把最后生效的放在前面。

  • loader options

 配置项。

module.exports = {
  module: {
    rules: [{
      test: /\.css$/,
      use: ['style-loader', {
        loader: 'css-loader',
        options: {
          //css-loader 配置项
        }
      }]
    }]
  }
}
  • exclude与include

 (1)作用:排除或包含指定目录下的模块,可接收正则表达式或者字符串(文件绝对路径),以及由它们组成的数组。

(2)exclude与include同时存在时,exclude的优先级更高。

rules: [
  {
    use: ['style-loader', 'css-loader'],
    exclude: /src\/lib/,
    include: /src/,
  }
]
  • resource与issuer

(1)作用:更加精准得确定模块规则得作用范围。

(2)resource:被加载模块。issuer:加载者

(3)与上面那种形式无法共存,只能选择一种风格进行配置

rules: [
  {
    use: ['style-loader', 'css-loader'],
    resource: {
      test: /\.css$/,
      exclude: /node_modules/,
    },
    issuer: {
      test: /\.js$/,
      exclude: /node_modules/,
    },
  }
]
  •  enforce

(1)作用:指定一个loader的种类(按照执行顺序)。值为“pre”或“post” 。

pre:将在所有正常loader之前执行。

post:将在所有正常loader之后执行。

三、常用loader

  •  babel-loader

(1)作用:处理ES6+并将其编译为ES5

npm install babel-loader @babel/core @babel/preset-env

babel-loader:它是使Babel与Webpack协同工作的模块。

@babel/core:它是babel编译器的核心模块。

@babel/preset-env:它是Babel官方推荐的预置器,可根据用户设置的目标环境自动添加所需的插件和补丁来编译ES6+代码。 

rules: [
  {
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
      loader: 'css-loader',
      options: {
        //启用缓存机制,可接收一个字符串类型的路劲来做为缓存路径
        //这个值也可以是true,此时其缓存目录会指向node_modules/.cache/babel-loader
        cacheDirectory: true,
        presets: [[
          'env', {
            //@babel/preset-env会将ES6 Module转化为CommonJS的形式,这会导致webpack中的tree-shaking特性失效。
            //设置为false可以禁用模块语句的转化,而将ES6 Module的语法交给webpack本身处理。
            module: false
          }
        ]]
      }
    }
  }
]

(2) babel-loader支持从.babelrc文件读取Babel配置,因此可以将presets和plugins从webpack配置文件中提取出来,也能达到相同的效果。

  • ts-loader

npm install ts-loader typescript

(1)作用:用于连接webpack与typescript的模块

(2)注意: typescript本身的配置并不在ts-loader中,而是必须要放在工程目录下的tsconfig.json中。

  • html-loader

(1)作用:将HTML文件转化为字符串并进行格式化,这使得我们可以把一个HTML片段通过JS加载进来

npm install html-loader
  • handlebars-loader 

(1)作用:处理handlebars模块

(2)handlebars文件加载后得到的是一个函数,可以接收一个变量对象并返回最终的字符串。

  • file-loader

(1)作用:打包文件类型的资源,并返回其publicPath

rules: [
  {
    test: /\.(png|jpg|gif)$/,
    use: {
      loader: 'file-loader',
      options: {
        //文件名
        name: '[name].[ext]',
        //可覆盖output中配置的publicPath
        publicPath: './another-path/'
      }
    }
  }
]
//图片路径生成为:./another-path/avatar.jpg
  • url-loader 

 (1)作用:与file-loader类似,唯一不同在于用户可以设置一个文件大小的阈值,当大于该阈值时与file-loader一样返回publishPath,而小于该阈值时返回文件base64形式编码。

rules: [
  {
    test: /\.(png|jpg|gif)$/,
    use: {
      loader: 'url-loader',
      options: {
        limit: 10240,
        //文件名
        name: '[name].[ext]',
        //可覆盖output中配置的publicPath
        publicPath: './another-path/'
      }
    }
  }
]
  • vue-loader 

(1)作用: 用于处理vue组件

四、自定义loader

实现一个loader,为所有JS文件启用严格模式,即在文件头部加上‘user strict’的代码

 (1)loader初始化

1、创建一个force-strict-loader目录

2、创建index.js,也就是loader的主体

module.exports = function (content) {
  var useStrictPrefix = '\'use strict\';\n\n';
  return useStrictPrefix + content
}

3、安装并使用这个loader

npm install <path-to-loader>/force-strict-loader

 在webpack目录下使用相对路径安装,会在项目的node_modules中创建一个指向实际force-strict-loader目录的软链,也就是我们之后可以随时修改loader源码并且不需要重复安装了。

4、添加webpack配置

module: {
  rules: [
    {
      test: /\.js$/,
      use: 'force-strict-loader',
    }
  ]
}

 (2)启用缓存

module.exports = function (content) {
  if (this.cacheable) {
    this.cacheable();
  }
  var useStrictPrefix = '\'use strict\';\n\n';
  return useStrictPrefix + content
}

(3)获取options

module: {
  rules: [
    {
      test: /\.js$/,
      use: {
        loader: 'force-strict-loader',
        options: {
          sourceMap: true
        }
      }
    }
  ]
}

需要安装一个依赖库loader-utils,主要用于提供一些帮助函数。

var loaderUtils = require('loader-utils')
module.exports = function (content) {
  if (this.cacheable) {
    this.cacheable();
  }
  var options = loaderUtils.getOptions(this) || {};
  //....
  var useStrictPrefix = '\'use strict\';\n\n';
  return useStrictPrefix + content
}

 (4)支持souce-map

var loaderUtils = require('loader-utils')
var SourceNode = require('source-map').SourceNode;
var SourceMapConsumer = require('source-map').SourceMapConsumer
module.exports = function (content, sourceMap) {
  if (this.cacheable) {
    this.cacheable();
  }
  var options = loaderUtils.getOptions(this) || {};
  if (options.sourceMap && sourceMap){
    //......
  }
    var useStrictPrefix = '\'use strict\';\n\n';
  return useStrictPrefix + content

五、loader相关知识

(1)loader-runner 

1、定义:

loader-runner 允许你在不安装 webpack 的情况下运行 loaders 

2、作用:

  • 作为 webpack 的依赖,webpack 中使用它执行 loader
  • 进行 loader 的开发和调试

3、loader-runner 的使用

import { runLoaders } from "loader-runner";
runLoaders(
  {
    resource: "/abs/path/to/file.txt?query", // String: 资源的绝对路径(可以增加查询字符串)
    loaders: ["/abs/path/to/loader.js?query"], // String[]: loader 的绝对路径(可以增加查询字符串
    context: { minimize: true }, // 基础上下文之外的额外 loader 上下文
    readResource: fs.readFile.bind(fs), // 读取资源的函数
  },
  function (err, result) {
    // err: Error?
    // result.result: Buffer | String
  }
);

4、例子

a、自定义一个loader:src/raw-loader.js(将source转化为string)

module.exports = function (source) {
  const json = JSON.stringify(source)
    .replace(/\u2028/g, "\\u2028 ") // 为了安全起见, ES6模板字符串的问题
    .replace(/\u2029/g, "\\u2029");
  return `export default ${json}`;
};

b、新建一个资源为:src/demo.txt 内容为一个文本 foobar

c、使用 loader-runner 调试 loader(新建一个run-loader.js文件)

const fs = require("fs");
const path = require("path");
const { runLoaders } = require("loader-runner");
runLoaders(
  {
    resource: "./demo.txt",
    loaders: [path.resolve(__dirname, "./src/raw- loader")],
    readResource: fs.readFile.bind(fs),
  },
  (err, result) => (err ? console.error(err) : console.log(result))
);

 d、运行run-loader.js输出结果

 (2)loader 的参数获取

通过 loader-utils 的 getOptions 方法获取

const loaderUtils = require("loader-utils");
module.exports = function (content) {
  const { name } = loaderUtils.getOptions(this);
};

(3)loader 异常处理

1、loader 内直接通过 throw 抛出

2、通过 this.callback 传递错误

this.callback( 
     err: Error | null,
     content: string | Buffer, 
     sourceMap?: SourceMap, 
     meta?: any 
);

(4)loader 的异步处理

通过 this.async 来返回一个异步函数(第一个参数是 Error,第二个参数是处理的结果) 

module.exports = function (input) {
  const callback = this.async();
  // No callback -> return synchronous results
  // if (callback) { ... }
  callback(null, input + input);
};

 (5)在 loader 中使用缓存

1、webpack 中默认开启 loader 缓存,可以使用 this.cacheable(false) 关掉缓存。

2、缓存条件:

  • loader 的结果在相同的输入下有确定的输出
  • 有依赖的 loader 无法使用缓存 

(6)loader 文件输出 

通过 this.emitFile 进行文件写入

const loaderUtils = require("loader-utils");
module.exports = function (content) {
  const url = loaderUtils.interpolateName(this, "[hash].[ext]", { content });
  this.emitFile(url, content);
  const path = `__webpack_public_path__ + ${JSON.stringify(url)};`;
  return `export default ${path}`;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值