Webpack 内部原理与插件开发

/**
 * webpack插件开发采用'动态原型模式'
 * 插件开发,最重要的两个对象:compiler、compilation
 * @param options
 * @constructor
 */
function MyPlugin(options) {
  // 根据 options 配置你的插件
}
// 我们可以在原型上添加一些方法
MyPlugin.prototype.someFunc = function() {
  /*something*/
};

// apply方法是必须要有的,因为当我们使用一个插件时(new somePlugins({})),webpack会去寻找插件的apply方法并执行
MyPlugin.prototype.apply = function(compiler) {
  // compiler是什么?compiler是webpack的'编译器'引用

  // compiler.plugin('***')和compilation.plugin('***')代表什么?
  // document.addEventListener熟悉吧?其实是类似的
  // compiler.plugin('***')就相当于给compiler设置了事件监听
  // 所以compiler.plugin('compile')就代表:当编译器监听到compile事件时,我们应该做些什么

  // compile('编译器'对'开始编译'这个事件的监听)
  compiler.plugin('compile', function(params) {
    console.log('The compiler is starting to compile...');
  });

  // compilation('编译器'对'编译ing'这个事件的监听)
  compiler.plugin('compilation', function(compilation) {
    console.log('The compiler is starting a new compilation...');
    // 在compilation事件监听中,我们可以访问compilation引用,它是一个代表编译过程的对象引用
    // 我们一定要区分compiler和compilation,一个代表编译器实体,另一个代表编译过程
    // optimize('编译过程'对'优化文件'这个事件的监听)
    compilation.plugin('optimize', function() {
      console.log('The compilation is starting to optimize files...');
    });
  });

  // emit('编译器'对'生成最终资源'这个事件的监听)
  compiler.plugin('emit', function(compilation, callback) {
    console.log('The compilation is going to emit files...');

    // compilation.chunks是块的集合(构建后将要输出的文件,即编译之后得到的结果)
    compilation.chunks.forEach(function(chunk) {
      // chunk.modules是模块的集合(构建时webpack梳理出的依赖,即import、require的module)
      // 形象一点说:chunk.modules是原材料,下面的chunk.files才是最终的成品
      chunk.modules.forEach(function(module) {
        // module.fileDependencies就是具体的文件,最真实的资源【举例,在css中@import("reset.css"),这里的reset.css就是fileDependencie】
        module.fileDependencies.forEach(function(filepath) {
          // 到这一步,就可以操作源文件了
        });
      });

      // 最终生成的文件的集合
      chunk.files.forEach(function(filename) {
        // source()可以得到每个文件的源码
        var source = compilation.assets[filename].source();
      });
    });

    // callback在最后必须调用
    callback();
  });
};

// 以上compiler和compilation的事件监听只是一小部分,详细API可见该链接http://www.css88.com/doc/webpack2/api/plugins/

module.exports = MyPlugin;

Webpack  自定义加载器与插件

Webpack 核心的两个模块即是 Loaders 与 Plugins,Loaders 负责载入各种各样的资源、样式与脚本文件,而 Plugins 负责对载入的文件进行处理并且最终输出到编译后的文件中。加载器可以串联。可以通过管道来处理资源,最后一个加载器需要输出为 JavaScript,中间别的加载器可以输出任意的格式到下一个加载器。

Loaders

加载器是把一个资源文件作为入参转换为另一个资源文件的 node.js 函数。例如,可以通过加载器来让 webpack 来加载 CoffeeScript 或 JSX 文件。加载器的基本使用如下所示:

module.exports = {
    entry: "./entry.js",
    output: {
        path: __dirname,
        filename: "bundle.js"
    },
    module: {
        loaders: [
            {test: /\.css$/, loader: "style!css" },
            {test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'}
        ]
    }
};

安装加载器一般直接可以通过npm进行安装:

$ npm install xxx-loader --save

$ npm install xxx-loader --save-dev

虽然不是强制要求,但惯例把加载器命名为 XXX-loader,其中 XXX 就是加载器的名字,如:json-loader。可以通过全称(实际的名称)来引用加载器(如 json-loader),也可以通过简略名来引用(如 json)。加载器的命名惯例和搜索优先级是通过 webpack 的配置 api resolveLoader.moduleTemplate 来指定的。

通过 require 引用

可以通过 require 语句(或 define、require.ensure 等)来指定加载器,只需要用 ! 来分隔资源即可,每部分都是相对当前目录。

require("./loader!./dir/file.txt");
// => 使用当前目录下的 "loader.js" 来转换 "dir" 目录下的 "file.txt" 文件

require("jade!./template.jade");
// => 使用 "jade-loader" (通过 npm 安装在 "node_modules" 目录)来转换 "template.jade" 文件

require("style!css!less!bootstrap/less/bootstrap.less");
// => 通过 github 来安装在 "node_modules" 目录下的 "bootstrap" 模块里的 "less" 目录里的 "bootstrap.less"
//    文件会先被 "less-loader" 转换,然后再被 "css-loader" 转换,最后被 "style-loader" 转换

通过配置文件使用

可以通过正则来在配置文件中绑定加载器:

{
  module: {
    loaders: [
      { test: /\.jade$/, loader: "jade" },
      // => "jade" loader is used for ".jade" files

      { test: /\.css$/, loader: "style!css" },
      // => "style" and "css" loader is used for ".css" files
      // Alternative syntax:
      { test: /\.css$/, loaders: ["style", "css"] }
    ]
  }
}

通过命令行使用

通过 CLI 扩展参数来绑定加载器

$ webpack --module-bind jade --module-bind 'css=style!css'

上述命令给 .jade 文件绑定了 jade 加载器,给 .css 文件班定了 style 和 css 加载器。

查询参数

在使用 Loader 时可以通过查询 web 风格的字符串来指定查询参数,如:url-loader?mimetype=image/png。查询字符串的格式要看加载器,具体格式参考加载器文档。大多数的加载器支持普通的查询格式(?key=value&key2=value2)和 JSON 对象(?{"key":"value","key2":"value2"})。

  • require
require("url-loader?mimetype=image/png!./file.png");
  • 配置文件
{ test: /\.png$/, loader: "url-loader?mimetype=image/png" }

{
    test: /\.png$/,
    loader: "url-loader"
    query: { mimetype: "image/png" }
}
  • 命令行
webpack --module-bind "png=url-loader?mimetype=image/png"

Scripts(脚本)

  • babel-6-0-20-modules-feature-not-work-in-ie8 基本上我们会使用 Babel 来进行 JS 的语法编译,安装时只要用如下的 npm 命令:
npm install babel-loader --save-dev

在配置文件中:

  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        exclude: /(node_modules|bower_components)/,
        loader: 'babel'
      }
    ]
  }

Style-Checker

ESLint-Loader

直接使用 npm 命令安装即可:

$ npm install eslint-loader

在 Webpack 的配置文件中,需要进行以下修正:

module.exports = {
  // ...
  module: {
    loaders: [
      { test: /\.js$/, loader: 'eslint-loader', exclude: /node_modules/ }
    ]
  } // ...
};

如果使用了其他的编译器,譬如  babel-loader时候,务必要注意 loaders 中的顺序,否则文件可能会在经过 Babel 处理之后再进行格式检查。

module.exports = {
  // ...
  module: {
    loaders: [
      {
        test: /\.js$/,
        loaders: ['babel-loader', 'eslint-loader'],
        exclude: /node_modules/
      }
    ]
  } // ...
};

安全起见,你可以考虑在preLoaders部分来进行源文件的检查,而不会影响其他的加载器:

module.exports = {
  // ...
  module: {
    preLoaders: [
      { test: /\.js$/, loader: 'eslint-loader', exclude: /node_modules/ }
    ]
  } // ...
};

Custom

Custom Loaders(自定义加载器)

Custom Plugins(自定义组件)

你可能感兴趣的:(webpack)