手写webpack——增加loader和plugin

本文中,“webpackSimple”指项目,而“localWebpack”或者“demo-start'”均指手写的webpack工具
上一节,我做了一个简单的webpack,虽然解决了模块化引入的问题,但它目前只支持js模块引入。这一节,我准备给这个低配版的webpack增加loader和plugin,让它可以引入css之类的文件,并且完成一些别的操作。言归正传,下边开始

  • 首先,我们先写两个简化版loader——style-loader和less-loader
    首先在项目“webpackSimple”中安装插件less(为将less文件转化为css),然后根目录下新建文件夹loader,在其中新建style-loader.js和less-loader.js两个文件
// less-loader.js
let less = require("less")
function loader(source) {
  let css = "";
  less.render(source, function(err, c) {  // 这是less插件提供的解析方法
    css = c.css;
  })
  css = css.replace(/\n/g, "\\n");  // 这里\n是换行,但转换成字符串后,不能正确解析
  return css;
}
module.exports = loader;

//style-loader.js
function loader(source) {
  let style = `
    let style = document.createElement('style');
    style.innerHTML = ${JSON.stringify(source)}; // 将css文件的内容字符串化
    document.head.appendChild(style)
  `;
  return style;
}
module.exports = loader;

以上,我们新建了两个loader。webpack.config.js新增对less文件的处理

 module: {
    rules: [
      {
        test: /\.less$/,
        use: [
          path.resolve(__dirname, "loader", "style-loader"), //这里,使用自己创建的loader
          path.resolve(__dirname, "loader", "less-loader")
        ]
      }
    ]
  }

接下来,我们就需要在哪个手写的webpack中添加对loader的处理了,处理loader,其实就是解析文件的方式与之前纯js不同。js就只是得到文件内容。而别的文件需要loader先处理一遍。所以,此处修改Compiler.js中的getSource方法

getSource(modulePath) {
    // 拿到匹配规则
    let rules = this.config.module.rules;
    // 拿到文件内容
    let content = fs.readFileSync(modulePath, "utf8");
    // 分别处理不同规则
    rules.forEach((rule, index) => {
      let { test, use } = rule;
      let len = use.length - 1;
      if (test.test(modulePath)) {
        // 如果匹配到了,也就是说这个模块需要用loader来转换
        function normalLoader() {
          let loader = require(use[len--]); // 因为loader规则是从下至上,从右至左的
          // 递归调用loader,实现转化功能
          // loader获取对应的loader函数
          content = loader(content);
          if (len >= 0) {
            normalLoader(); // 如果还有loader,继续执行
          }
        }
        normalLoader();
      }
    });
    return content;
  }

好了,接下来,我们测试下结果。首先,在webpackSimple项目的新建一个index.less文件并且在index.js中引入。然后在dist目录下新建index.html,并且引入bundle.js,文件目录如下:

menu.png

//index.less
body {
  background: red;
}

然后再项目中运行npx demo-start
在浏览器中打开index.html

image.png


可以看到,less文件被成功的解析并导入了。

添加plugin

  • 首先在我们的“webpack”插件中安装“tapable”(方便使用钩子函数)。这里我们只用SyncHook举例,首先在Compiler.js中引入SyncHook
    在编译文件时,先拿到webpack.config.js文件,看其中是否有plugins字段,有的话,就挨个执行,插件的内部其实就是注册一个个钩子函数
//Compiler.js
let { SyncHook } = require("tapable");
class Compiler {
  constructor(config) {
    ...
    this.hooks = {
      entryOption: new SyncHook(),
      compile: new SyncHook(),
      afterCompile: new SyncHook(),
      afterPlugins: new SyncHook(),
      run: new SyncHook(),
      emit: new SyncHook(),
      done: new SyncHook()
    };
    // 如果传递了plugins参数
    let plugins = this.config.plugins;
    if (Array.isArray(plugins)) {
      plugins.forEach(plugin => {
        plugin.apply(this);  //插件内部都由apply方法
      });
    }
    // 插件装载完成
    this.hooks.afterPlugins.call();  //调用钩子
  }
  ...

项目代码:

//webpack.config.js

class P {   // 模拟一个plugin
  apply(complier) {
    complier.hooks.emit.tap("emit", function() { // 注册钩子“emit”
      console.log("emit");
    });
  }
}
module.exports = {
  entry: './src/index.js',
  ...
  plugins: [new P()]
}

2人点赞

日记本



作者:成熟稳重的李先生
链接:https://www.jianshu.com/p/02e3f743659b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/sd19871122/article/details/108227815