在webpack-core的基础上实现webpack-loader

127 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

前言

身为一个前端小菜鸟,总是有一个飞高飞远的梦想,因此,每点小成长,我都想要让它变得更有意义,为了自己,也为了更多值得的人

开开心心学技术大法~~

开心

来了来了,他真的来了~

正文

之前用了六十行代码阐述webpack-core的思想,今天我们就在webapck-core的基础上再实现下webpack-loader。

明确webpack-loader的本质

想必一些小伙伴已经有自己实现webpack-loader的经验,所以应该知道webpack-loader其实就是一个抛出来的function,类似于下面这种。

module.exports = function(content,map,meat){
    // ...loader操作
    return newContent;
}

回顾webpack-core

因为接下来要实现的loader需要跟webpack-core结合,所以先简单回顾一下webapck-core中的关键方法。

  1. 通过fs模块加载入口资源,拿到文件的utf-8编码的内容
  2. 通过babel的parsertraverse拿到入口文件import的资源路径,也就是依赖模块的路径
  3. 通过拿到的依赖模块的路径信息做递归操作,将所有被引入模块的依赖关系做成一个依赖图
  4. 统一导入导出方法
  5. 将所有被依赖的文件资源build输出到一个指定文件

在以上流程中,我们大概总结了这几个关键方法

  • getModuleInfo方法来获取单个模块文件信息
  • getGraphInfo方法来获得最终的依赖图
  • build方法来生成最终的dist/built.js文件

思考loader应该被怎样实现

那首先思考下loader一般是用来处理什么?

实现在哪里

没错,loader就是为了处理文件资源的,将webapck不认识的资源转变成webpack认识的js资源。

再进一步,其实loader就是处理文件的。而我们在webpack-core中已经有了明确的getMouldeInfo方法,所以loader只需要被实现在getModuleInfo中。

怎样实现

因为上面我们已经知道了loader用来处理文件,且会return一个utf-8编码的资源内容,那我们只需要在getModuleInfo中拿到文件源代码source,然后将source传入到loader中,并且将返回结果重新赋值给source即可。


const selfLoader = require('./loader.js');
const JSONLoader = require('./json-loader.js');


const webpackConfig = {
  module: {
    rules: [
      {
        test: /\.json$/i,
        loader: JSONLoader,
      },
      {
        test: /\.js$/i,
        use: [selfLoader],
      },
    ],
  },
  plugins: [new OutputPlugin()]
}


getModuleInfo(filepath){
  let source = fs.readSync(filepath)
  // 因为loader会有this,this指向的就是loader的上下文,所以我们定义一个loaderContext
  const webpackLoaderContext = {
    data: 'loader-data',
    addDependency(dependency) {
      console.log('添加到了依赖', dependency);
    }
  }

  // 如果有loader的话,在这里加上loader的处理,因为loader基本都是对文件进行操作的,所以会在加载依赖图的时候进行loader的注册和触发
  webpackConfig.module.rules.map(item => {
    if (item.test.test(filepath)) {
      // 如果是单个loader
      if (item.loader) {
        // 将loader处理过的源代码替换掉原来的source
        // source = item.loader(source)
        // 因为webpack-loader中会有loader上下文,所以绑定this到loader山下文
        source = item.loader.call(webpackLoaderContext, source)
      } else if (item.use) {
        // 如果是多个loader
        // 因为loader是从后向前执行的,所以reverse一下
        item.use.reverse().map(singleLoader => {
          // 将loader处理过的源代码替换掉原来的source
          source = singleLoader.call(webpackLoaderContext, source)
        })
      }
    }
  })
  // ...其他的module操作
}

其中webpackConfig可以通过外部定义一个webpack.config.js来引入,我们这里静态模拟一个用户的配置。

其中两个loaderselfLoaderjson-loader是我自己实现的,用来验证loader功能是否ok

其中selfLoader的功能是在console.log前添加====我是zzz===!!!的字符串前缀

module.exports = function (content) {

  // 尝试访问loader的this
  this.addDependency('我是依赖的dependency')

  return content.replace(/console\.log\('/g,'console\.log\(\'====我是zzz===!!!')
}

json-loader实现了对json文件的支持,具体实现也很简单

 // json-loader.js
 module.exports = function (content) {
  return `module.exports = ${JSON.stringify(content)}`
}

以上,就实现了webpack-core中实现loader的功能

结语

往期好文推荐「我不推荐下,大家可能就错过了史上最牛逼vscode插件集合啦!!!(嘎嘎~)😄」