关于webpack:深度解读Webpack中的loader原理

37次阅读

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

一、前言

webpack 是一个古代 JavaScript 利用的动态模块打包器。那么 webpack 是怎么实现不同品种资源模块加载的呢?

没错就是通过 loader。loader 用于对模块的源代码进行转换。loader 能够使你在 import 或加载模块时预处理文件。

咱们带着上面几个问题,彻底吃透 loader ~

二、为什么要应用 loader

webpack 是如何加载资源模块的呢?咱们先试着用 webpack 间接打包我的项目中的 css 文件。

初始化一个 webpack 我的项目,目录如下:

在 src 目录下新建了一个 index.css 文件,这里新建这个文件的目标就是以 css 文件为入口,尝试应用 webpack 独自打包它。

/* index.css */
body {
  margin: 0 auto;
  padding: 0 20px;
  width: 1000px;
  background-color: #ccc;
}

调整下 webpack 配置,让入口文件门路指定为 index.css 的门路。

// webpack.config.js
module.exports = {
  entry: "./src/index.css",
  output: {filename: "bundle.js",},
};

而后咱们到终端运行 npx webpack 命令,你会发现命令行会提醒 Module parse failed: Unexpected token (1:5) 模块解析谬误。

仔细的同学会发现前面还紧跟着一句解决方案:You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.

大抵的意思就是说,您可能须要适当的 loader 来解决此文件类型,目前没有配置 loader 来解决此文件。

这里,loader 的重要性就凸显进去了。

三、怎么配置 loader

还是接着方才打包 index.css 报错的问题。想加载 css 文件,咱们能够试试罕用的 css-loader。

yarn add css-loader -D

webpack 配置也同步改下:

// webpack.config.js
module.exports = {
  mode: "none", // 防止不指定打包模式时弹出正告
  ...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: "css-loader",
      },
    ],
  },
};

webpack 配置中 module 属性增加 rules 对象数组。这外面的 test 属性值为一个正则表达式,匹配以后 loader 对应解决文件的格局。use 属性值为 loader 名称。

再打包就不会报错了。

如果想要 index.css 模块在页面中失效,只须要额定增加一个 style-loader,款式就 OK 了。

style-loader 的作用能够了解为:建设了一个 style 标签,这个标签外面带入了 css 款式。标签最初追加到页面上。##### 参考 webpack 视频解说:进入学习

留神配置多个 loader 时,执行程序是从后往前执行的:

  • 最初的 loader 最早调用,将会传入原始资源内容
  • 第一个 loader 最初调用,期望值是传出 JavaScript 和 source map(可选)
  • 两头的 loader 执行时,会传入前一个 loader 传出的后果

所以 css-loader 放在最初。具体配置如下:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

如果你还要用到 less-loader,同理可知 rules 中 use 属性值应该为:["style-loader", "css-loader", "less-loader"]

四、怎么写一个 loader

想要实现的大抵流程:

接下来,咱们尝试实现上图 css-loader 和 style-loader 的简略版本。

4.1 创立 loader

咱们在我的项目根目录下创立好 css-loader.js 和 style-loader.js 文件。

次要代码如下:

├── src ····································· source dir
    │   ├── index.css ······················· css module
+   │   └── index.js ························ entry module
+   ├── css-loader.js ······················· css loader
    ├── package.json ························ package file
+   ├── style-loader.js ····················· style loader
    └── webpack.config.js ··················· webpack config file
/* index.css */
body {
  margin: 0 auto;
  padding: 0 20px;
  width: 1000px;
  background-color: #ccc;
}
// index.js
import "./index.css";
console.log("loader ok!");

每个 webpack 的 loader 都须要导出一个函数,这个函数就是咱们这个 loader 对资源的处理过程,它的 输出 就是加载到的资源文件内容,输入 就是咱们加工后的后果。咱们通过 source 参数接管输出,通过返回值输入。这里咱们先尝试打印一下 source,而后在函数的外部间接返回一个字符串 hello webpack css-loader!,具体代码如下所示:

// css-loader.js
module.exports = (source) => {console.log(source);
  return "hello webpack css-loader!";
};

咱们回到 webpack 配置文件中调整一下加载器规定,调整之后应用的加载器就是咱们刚刚编写的这个 css-loader.js 模块,具体代码如下:

// webpack.config.js
module.exports = {
  mode: "none",
  // 入口改为 index.js
  entry: "./src/index.js",
  output: {filename: "bundle.js",},
  module: {
    rules: [
      {
        test: /\.css$/,
        // 改下这里
        use: ["./css-loader"],
      },
    ],
  },
};

舒适提醒:这里的 use 中不仅能够应用模块名称,还能够应用模块文件门路,这点与 Node 中的 require 函数雷同。

配置实现后,咱们再次关上命令行终端运行打包命令,如下图所示:

从报错信息能够看出,loader 函数的 参数 就是文件的内容。

谬误提醒说:You may need an additional loader to handle the result of these loaders.(咱们可能还须要一个额定的加载器来解决以后加载器的后果)

舒适提醒:其实 webpack 加载资源文件的过程最初的后果必须是一段规范的 JS 代码字符串。

失常流程:

咱们当初应该想到是 css-loader 出了问题。

4.2 css-loader

css-loader 次要作用就是将多个 css 模块整合到一起。

module.exports = (source) => {// 匹配 url(xxx) 相似构造
  const reg = /url((.+?))/g;
  // 地位下标
  let pos = 0;
  // 以后匹配的代码片段
  let current;
  const arr = ["let list = []"];
  while ((current = reg.exec(source))) {const [matchUrl, g] = current;
    const lastPos = reg.lastIndex - matchUrl.length;
    arr.push(`list.push(${JSON.stringify(source.slice(pos, lastPos))})`);
    pos = reg.lastIndex;
    arr.push(`list.push('url(' + require('${g}') + ')')`);
  }
  arr.push(`list.push(${JSON.stringify(source.slice(pos))})`);
  arr.push(`module.exports = list.join('')`);
  // 每行代码之间减少一个回车
  return arr.join("\n");
};

大抵思路:

  • 整个 css 代码片段以 url(xxx) 相似构造为节点分成多个局部
  • url 里的门路改为 require 引入
  • 用数组的模式将 css 代码拼凑起来最初造成一个整体

loader 打包后果如下图:

这是输入的 bundle.js 的片段,就是把咱们刚刚返回的字符串间接拼接到了该模块中。这里也解释了方才打包语法报错的问题(loader 解决完必须返回 JS 代码)。

4.3 style-loader

style-loader 会把整合的 css 局部挂载到 head 标签中。

代码如下:

module.exports = function (source) {return `    const styleElement = document.createElement('style');    styleElement.innerHTML = ${JSON.stringify(source)}
    document.head.appendChild(styleElement);  `;
};

4.4 写 loader 之后的总结

loader 就是一个函数,一旦有模块被 import 或者 require 时它就会去拦挡这些模块的源码,对其进行革新,而后输入到另一个模块中,周而复始,最终输入到入口文件中,造成最终的代码。

也正是有了 loader 机制,咱们能力通过 webpack 去加载任何咱们想要加载的资源。

五、感激

  • 如果本文对你有帮忙,就点个赞反对下吧!感激浏览。

正文完
 0