webpack loader详解和实现自定义loader

332 阅读1分钟

webpack loader 介绍及使用

webpack 只能处理 js 模块,其他文件如.css、.ts 它无法处理,所以需要 loader 来处理 js 外的文件使其转化为 js 模块,loader 的加载顺序是从右到左,从下到上。

比如下面对于.scss 文件的转化分别经历了 sass-loader、css-loader、style-loader,几个 loader 各司其职,缺一不可。除了下面配置的方式,也可通过import "style-loader!css-loader!sass-loader!./index.scss";来使用 loader 转化该 scss 文件。

module: {
    rules: [{
      test: /\.scss$/,
      use: [{
          loader: "style-loader" // 将 JS 字符串生成为 style 节点
      }, {
          loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
      }, {
          loader: "sass-loader" // 将 Sass 编译成 CSS
      }]
    }]
  }

常用 loader:

  1. url-loader/file-loader:将资源 copy 到 dist 中相应位置并重命名,url-loader 可配置小于多少 limit 时转化为 dataurl,如果大于 limit 默认走 file-loader,所以如果没有特殊需求单用 url-loader 即可;
  2. babel-loader:es 新标准转 es5;
  3. ts-loader:ts 转 js;
  4. postcss-loader:可对 css 处理,如自动加浏览器兼容性前缀等,一般在 sass-loader 上层,css-loader 下层。

自定义 loader

开发各类小程序时(微信/百度/头条等),基本都会对小程序的包大小限制在几兆左右,此时项目中就不能放太多的图片,否则很容易就会超过限制。所以很多时候我们会把小程序中用到的图片放到 oss 上面,程序直接引用 oss 的地址。
开发的时候得先把需要的图片事先上传到 oss 上面,然后获取到 oss 地址再放到 src 中,会显得格外繁琐。

此时我们需要自定义一个处理图片的 loader,主要是获取到图片的内容并上传到 oss,然后获取到 oss 地址并包装成 commonjs 模块返回;

我们的小程序是基于 uniapp 的,uniapp 除了可以使用hbuilderx开发之外,还可以通过老朋友vue-cli来开发,也就意味着可以自定义 loader 了。要修改vue-cli的配置,需要新建一个vue.config.js文件:

module.exports = {
    chainWebpack: (config) => {
        // 获取到images的配置规则,怎么知道处理图片的rule名称是images而不是img或者image呢,vue-cli下执行vue inspect > output.js可以看到所有的规则名及具体配置
        const imagesRule = config.module.rule('images');
        // 清楚改rule下所有的loader
        imagesRule.uses.clear();
        // 使用自定义的loader
        imagesRule.use('./img-upload-loader.js').loader('./img-upload-loader.js').end();
    }
};

主要做了 2 个操作:

  1. 清除图片处理的所有 loaders,其实就是url-loaderfile-loader这两兄弟;
  2. 配置我们自定义的 loader。

这里img-upload-loader就是负责上传图片的 loader 了,看下实现代码:

module.exports = function (source) {
    const callback = this.async();
    upload(source, () => {
      callback(null, `module.exports = '${url}'`);
    })
};
function upload(source, cb) {
  // 此处模拟上传操作
  req.upload('https://xxx', source).then((url) => {
    cb(url);
  })
};

通过异步 loader 实现上传,上传成功后导出地址;
打包时会把小程序中 image 组件的 rc 转为线上地址,比如:

<image :src="require('@/assets/images/logo.png')" />

将转为:

<image src="https://xxx.com/logo.png" />

这里需要注意的是 src 的地址必须是通过 require 引入的,否则不生效,因为 uniapp 默认不会把 image 中的 src 当作模块处理,这里和开发 h5 时有差别。