【javascript】手写一个webpack loder
手写一个loader
为什么需要loader?
webpack 实际上只能处理js文件,那么对于除了js文件的其他类型的文件 比如 css sass 等。。我们不能直接用webpack来处理。
我们需要一个翻译员(loader)来帮我们的文件处理一下。有时候我们不只需要一个翻译员来工作,比如要把文言文翻译成外语,首先要转换成白话文,然后转换为外语。
Loader就像一个翻译员,能将源文件经过转化后输出新的结果,并且一个文件还可以链式的经过多个翻译员翻译。
以scss文件为例:
 先将scss源代码交给sass-loader,将scss转换成css;
 将sass-loader输出的css提交给css-loader处理,找出css中依赖的资源,压缩css;
 将css-loader输出的css提交给style-loader处理,转换成通过脚本加载的javascript代码。
最终的结果一定是javascript代码。
编写loader的原则
  职责单一: 一个loader只做一件事情。优点:容易维护且能够链式调用
  模块化:
保证输出模块化,loader生成的模块与普通块遵循的相同的设计原则
  无状态:
确保loader在不同模块转换之间,不保存状态。每次运行都应该独立于其他编译模块以及相同模块之前的编译结果。
动手写一个基础loader
webpack运行在Node上,一个loader其实就是一个node模块,这个模块需要导出一个函数。这个导出的函数输入的就是处理前的原内容,对内容处理之后 然后输出被处理过的内容。
我们现在写一个简单的javascript loader,这个loader的作用就是将js文件内容里面的字符串 like 转换成 ❤ 。
直接上代码,我们将这个loader命名为k-loader.js
module.exports = function (source) {
  const regExp = new RegExp("like", "ig")
  const result = source.replace(regExp, "❤")
  return result
}
怎么调试我们的loader,我们使用官方推荐的一种:
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: path.resolve('path/to/loader.js'),
            options: {/* ... */}
          }
        ]
      }
    ]
  }
};
 定义好loader然后找到该loader相对于webpack配置文件的路径即可。
 我们看k-loader.js 做了什么,获取到source,source就是加载的js文件的文件内容,然后做了一次数据清洗,将like全部转换成❤,然后返回字符串。 完事儿!这就ok了,你可以做一个测试,你会发现js文件里面的like全部被替换成了❤,是不是很简单。。是的。
基础loader上增加一些配置
 我们一般的loader是有option配置的,就是说用户需要自定义一些功能。
我们把这个like 和 ❤,配置在option里面,这样的话,就可以灵活的使用了,我们只需要改like 和 ❤的值即可。我们使用到了webpack提供的辅助模块loder-util,我们通过getOPtions(this)方法可以获取到webpack配置loader的时候传递的option值。
代码长这样:
const loaderUtils = require("loader-utils")
module.exports = function (source) {
  
  const options = loaderUtils.getOptions(this) || {}
  // 假定option只有一个key value
  // 这里应该对option做一次验证。
  const key = Object.keys(options)[0]
  const value = options[key]
  const regExp = new RegExp(key, "ig")
  const result = source.replace(regExp, value)
  return result
}
这样我们将options的值配置成{ like: "❤" }就会和我们上面的hard code 达到一样的效果,我们将options改成{like: "love"}这样就会把所有js文件里面的like替换成love。
到这一步,我们就已经可以自己编写一个loader了。
返回多个值
我们上面写的loader只是返回了一个值,在一些特殊的场景我们需要用this.callback()返回多个值。
以用babel-loader转换ES6代码为例,它需要输出转换后的ES5代码对应的SourceMap 以方便调试源码。 为了将Source Map也一起随着ES5代码返回给Webpack,还可以这样写:
module.exports = function(source) {
    this.callback(null, source, sourceMaps)
    return; // 当调用 callback() 时总是返回 undefined
}
this.callback API长下面的样子:
this.callback(
  err: Error | null,
  content: string | Buffer,
  sourceMap?: SourceMap,
  meta?: any
);
同步或异步
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);
  });
};
参考:
完整demo 见这里:
https://github.com/aeolusheath/webpack-certain-features/tree/master/write-loader-diy
 
                     
                    
                 
                    
                 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号