WebPack | Loader处理非JavaScript模块机制详解

312 阅读1分钟

这是我参与更文挑战的第24天,活动详情查看:更文挑战

[webpack系列文章连载中...]

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

Loader本质

loader只是一个导出为函数的JavaScript模块。有同步loader和异步loader。

  1. 同步loader
// 同步loader
module.exports = function(content,map,meta){
   return someSyncOperation(content)
}
  1. 异步loader
// 异步loader:使用 this.async 来获取 callback 函数
module.exports = function(content,map,meta){
  var callback = this.async()
  someAsyncOperation(content, function(err, result) {
    if (err) return callback(err);
    callback(null, result, map, meta);
  });
}

Loader的执行顺序

首先看看loader的配置:

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

多个loader是串行执行,顺序从后到前执行,这是因为Webpack采用的是Compose的函数组合方式:

// 将前一个函数的返回值作为下一个函数的入参,依次往下执行
var compose = (...fns)=>{
    return (arg)=>fns.reduce((promise,fn)=>promise.then(fn),Promise.resolve(arg))
}

// 用法如下:
var item1 = (x)=>{return new Promise((resolve)=>{
    setTimeout(()=>{
        console.log('1')
        resolve(x)
    },1000)
})}
var item2 = (y)=>{return new Promise((resolve)=>{
    setTimeout(()=>{
       console.log('2')
       resolve(y+3)
   },5000)
})}
var a = compose(item1,item2)
a(5).then(console.log)
// 依次输出:1,2,8

Row-loader的实现

// scr/raw-loader.js
module.exports = function(source){
  const json = JSON.stringify(source)
  // 为了安全,处理ES6模板字符串问题
  .replace(/\u2028/g,'\\u2028')
  .replace(/\u2029/g,'\\u2029')
  return `export default ${json}`
}

Loader-Runner调试工具

loader不同于plugin,可以独立于webpack进行开发调试。

  1. 安装
npm install loader-runner --save-dev

使用例子

// test-loader.js 调试 raw-loader.js
import { runLoaders } from "loader-runner";
const fs = require('fs')
const path = require('path')

runLoaders({
  // 资源的绝对路径(可以增加查询字符串,如?query)
  resource:'./test.txt',
  // loader的绝对路径可以增加查询字符串)
  loaders:[path.resolve(__dirname,'./loader/raw-loader.js')],   
  // 基础上下文之外的额外loader上下文
  context:{     
    minimize:true
  },
  // 读取资源函数
  readResource:fs.readFile.bind(fs) 
},function(err,result){
  err?console.log(err):console.log(result)
})

// 运行测试
node test-loader.js

loader中如何进行异常处理,使用缓存,产生一个文件,详细可以查看:loader api 官方文档

总结

至此我们学习了Loader的使用。