-
Notifications
You must be signed in to change notification settings - Fork 0
plugins.cn
For a high-level introduction to writing plugins, start with How to write a plugin. 关于编写插件的高层次介绍,请从How to write a plugin开始。
Many objects in Webpack extend the Tapable class, which exposes a plugin method. And with the plugin method, plugins can inject custom build steps. You will see compiler.plugin and compilation.plugin used a lot. Essentially, each one of these plugin calls binds a callback to fire at specific steps throughout the build process.
webpack 中很多对象都扩展自Tapable类,它暴露了一个plugin方法。利用plugin方法,插件可以注入自定义构建步骤。你会看到compiler.plugin和compilcation.plugin被大量使用。关键的是,每一个这样的 plugin 的调用绑定了一个回调以在特定步骤触发,贯穿整个构建流程。
A plugin is installed once as Webpack starts up. Webpack installs a plugin by calling its apply method, and passes a reference to the Webpack compiler object. You may then call compiler.plugin to access asset compilations and their individual build steps. An example would look like this:
在 webpack 启动时,一个插件会被安装一次。webpack 是通过调用插件的apply方法来安装插件的,并传递其引用至 webpack 的compiler对象。然后你可以调用compiler.plugin来访问资源的编译过程及其各自的构建步骤。实例可能是这样子的:
// MyPlugin.js
function MyPlugin(options) {
// Configure your plugin with options...
}
MyPlugin.prototype.apply = function(compiler) {
compiler.plugin("compile", function(params) {
console.log("The compiler is starting to compile...");
});
compiler.plugin("compilation", function(compilation) {
console.log("The compiler is starting a new compilation...");
compilation.plugin("optimize", function() {
console.log("The compilation is starting to optimize files...");
});
});
compiler.plugin("emit", function(compilation, callback) {
console.log("The compilation is going to emit files...");
callback();
});
};
module.exports = MyPlugin;Then in webpack.config.js
然后在webpack.config.js配置
plugins: [
new MyPlugin({options: 'nada'})
]There are two types of plugin interfaces. 插件接口有两种分类 方式。
-
Timing based
-
基于时序
-
sync (default): As seen above. Use return.
-
同步(默认):如上所见,使用返回值。
-
async: Last parameter is a callback. Signature: function(err, result)
-
异步:最后一个参数是一个回调。函数签名:
function(err, result) -
parallel: The handlers are invoked parallel (async).
-
并行:处理程序被并行调用(异步)
-
-
Return value
-
基于返回值
-
not bailing (default): No return value.
-
没输出(默认):没有返回值。
-
bailing: The handlers are invoked in order until one handler returns something.
-
有输出:处理程序按顺序执行,直到有一个有返回值。
-
parallel bailing: The handlers are invoked in parallel (async). The first returned value (by order) is significant.
-
并行输出:处理程序是并行(异步)调用的。第一个返回值(按顺序)是重要的
-
waterfall: Each handler gets the result value of the last handler as an argument.
-
瀑布流: 每个处理程序的结果成为下一个处理程序的输入参数。
-
Plugins need to have the apply method on their prototype chain (or bound to) in order to have access to the compiler instance.
为了得以访问编译器的实例,插件需要在其原型链上存在(或绑定)apply方法。
//MyPlugin.js
function MyPlugin() {};
MyPlugin.prototype.apply = function (compiler) {
//now you have access to all the compiler instance methods
}
module.exports = MyPlugin;Something like this should also work
//MyFunction.js
function apply(options, compiler) {
//now you have access to the compiler instance
//and options
}
//this little trick makes it easier to pass and check options to the plugin
module.exports = function(options) {
if (options instanceof Array) {
options = {
include: options
};
}
if (!Array.isArray(options.include)) {
options.include = [ options.include ];
}
return {
apply: apply.bind(this, options)
};
};The run method of the Compiler is used to start a compilation. This is not called in watch mode.
编译器的run方法是用来开始一次编译的,在监视模式下不会调用。
The watch method of the Compiler is used to start a watching compilation. This is not called in normal mode.
编译器的watch方法是用来开始一次监视型的编译的。在普通模式下不会被调用。
A Compilation is created. A plugin can use this to obtain a reference to the Compilation object. The params object contains useful references.
一个Compilation对象被创建。插件可以使用这个来获取Compilation对象的引用。params对象包含了有用的数据引用。
A NormalModuleFactory is created. A plugin can use this to obtain a reference to the NormalModuleFactory object.
创建一个NormalModuleFactory。插件可以使用这个来获取NormalModuleFactory对象的引用
compiler.plugin("normal-module-factory", function(nmf) {
nmf.plugin("after-resolve", function(data) {
data.loaders.unshift(path.join(__dirname, "postloader.js"));
});
});A ContextModuleFactory is created. A plugin can use this to obtain a reference to the ContextModuleFactory object.
创建一个ContextModuleFactory。插件可以使用这个来获取ContextModuleFactory对象的引用
The Compiler starts compiling. This is used in normal and watch mode. Plugins can use this point to modify the params object (i. e. to decorate the factories).
编译器开始编译。这个扩展点在普通及监视模式下均使用。插件可以利用这个来改变params对象。(如,去装饰那些工厂对象)
compiler.plugin("compile", function(params) {
//you are now in the "compile" phase
});Plugins can use this point to add entries to the compilation or prefetch modules. They can do this by calling addEntry(context, entry, name, callback) or prefetch(context, dependency, callback) on the Compilation.
插件可以使用这个扩展点来向 compilation 添加入口模块或者预获取模块
The compile process is finished and the modules are sealed. The next step is to emit the generated stuff. Here modules can use the results in some cool ways. 编译过程已经完成,模块都被冻结(无法修改)。下一步就是输出生成的东西。在此,各模块可以灵活使用那些结果。
The handlers are not copied to child compilers. 处理程序是不会被复制到子编译器的。
The Compiler begins with emitting the generated assets. Here plugins have the last chance to add assets to the c.assets array.
编译器开始发送/输出生成的资源。这时是插件向c.assets数组加入资源的最后机会。
The Compiler has emitted all assets. 编译器已输出所有资源。
All is done. 所有操作都完成了。
The Compiler is in watch mode and a compilation has failed hard. 编译器在监视模式下,编译期间出现严重错误,无法继续。
The Compiler is in watch mode and a file change is detected. The compilation will be begin shortly (options.watchDelay).
编译器在监视模式下,检测到一个文件变更事件。编译会在短时间内开始。
All plugins extracted from the options object are added to the compiler. 所有从配置对象提取的插件,都被安装到编译器。
All plugins extracted from the options object are added to the resolvers. 所有从配置对象提取的插件,都被安装到解析器。
The Compilation instance extends from the compiler. ie. compiler.compilation It is the literal compilation of all the objects in the require graph. This object has access to all the modules and their dependencies (most of which are circular references). In the compilation phase, modules are loaded, sealed, optimized, chunked, hashed and restored, etc. This would be the main lifecycle of any operations of the compilation.
compiler.plugin("compilation", function(compilation) {
//the main compilation instance
//all subsequent methods are derived from compilation.plugin
});The normal module loader, is the function that actually loads all the modules in the module graph (one-by-one). 普通模块的 loader,是那个真正从模块图谱中加载所有模块的函数。
compilation.plugin('normal-module-loader', function(loaderContext, module) {
//this is where all the modules are loaded
//one by one, no dependencies are created yet
});The sealing of the compilation has started. 开始 compilation 的冻结
compilation.plugin('seal', function() {
//you are not accepting any more modules
//no arguments
});Optimize the compilation. 优化 compilation 的结果
compilation.plugin('optimize', function() {
//webpack is begining the optimization phase
// no arguments
});Async optimization of the tree. 异步优化模块树
compilation.plugin('optimize-tree', function(chunks, modules) {
});Optimize the modules. 优化所有模块
compilation.plugin('optimize-modules', function(modules) {
//handle to the modules array during tree optimization
});Optimizing the modules has finished. 模块的优化已经完成
Optimize the chunks. 优化所有 chunk
//optimize chunks may be run several times in a compilation
compilation.plugin('optimize-chunks', function(chunks) {
//unless you specified multiple entries in your config
//there's only one chunk at this point
chunks.forEach(function (chunk) {
//chunks have circular references to their modules
chunk.modules.forEach(function (module){
//module.loaders, module.rawRequest, module.dependencies, etc.
});
});
});Optimizing the chunks has finished. chunk 的优化已完成
Restore module info from records. 从缓存记录里恢复模块的数据
Sort the modules in order of importance. The first is the most important module. It will get the smallest id. 按重要性排序模块。第一个是最重要的模块,其会获得最小的id。
Optimize the module ids. 优化模块的 id
Optimizing the module ids has finished. 模块 id 的优化已完成
Store module info to the records. 将模块的信息存储到缓存记录里
Restore chunk info from records. 从缓存记录里恢复 chunk 的信息。
Sort the chunks in order of importance. The first is the most important chunk. It will get the smallest id. 按重要性排序 chunk。第一个是最重要的 chunk,其会获得最小的id。
Optimize the chunk ids. 优化 chunk 的 id
Optimizing the chunk ids has finished. chunk id 的优化已完成
Store chunk info to the records. 将 chunk 的信息存储到缓存记录里
Before the compilation is hashed. 在 compilation 进行 hash 前。
After the compilation is hashed. 在 compilation 完成 hash 后。
Before creating the chunk assets. 在创建 chunk 资源前。
Create additional assets for the chunks. 给 chunk 创建额外的资源。
Store info about the compilation to the records 将 compilation 相关信息存储到缓存记录
Optimize the assets for the chunks. 优化 chunk 的 资源
The assets are stored in this.assets, but not all of them are chunk assets. A Chunk has a property files which points to all files created by this chunk. The additional chunk assets are stored in this.additionalChunkAssets.
那些资源存放在this.assets中,但不是所有都是 chunk 资源。一个Chunk有个属性files存有所有由其创建的文件的引用。额外的 chunk 资源引用是存放在this.addtionalChunkAssets。
Here's an example that simply adds a banner to each chunk. 下例简单地为每个 chunk 添加一个 banner
compilation.plugin("optimize-chunk-assets", function(chunks, callback) {
chunks.forEach(function(chunk) {
chunk.files.forEach(function(file) {
compilation.assets[file] = new ConcatSource("\/**Sweet Banner**\/", "\n", compilation.assets[file]);
});
});
callback();
});The chunk assets have been optimized. Here's an example plugin from @boopathi that outputs exactly what went into each chunk. chunk 的资源优化已完成。下面来自@boopathi的样例插件,输出了真正写入每个 chunk 的东西。
var PrintChunksPlugin = function() {};
PrintChunksPlugin.prototype.apply = function(compiler) {
compiler.plugin('compilation', function(compilation, params) {
compilation.plugin('after-optimize-chunk-assets', function(chunks) {
console.log(chunks.map(function(c) {
return {
id: c.id,
name: c.name,
includes: c.modules.map(function(m) {
return m.request;
})
};
}));
});
});
};Optimize all assets. 优化所有资源。
The assets are stored in this.assets.
资源存放在this.assets。
The assets has been optimized. 资源优化已完成。
Before a module build has started. 一个模块构建开始之前。
compilation.plugin('build-module', function(module){
console.log('build module');
console.log(module);
});A module has been built successfully. 一个模块构建成功。
compilation.plugin('succeed-module', function(module){
console.log('succeed module');
console.log(module);
});The module build has failed. 模块构建失败。
compilation.plugin('failed-module', function(module){
console.log('failed module');
console.log(module);
});An asset from a module was added to the compilation. 来自模块的一个资源被添加到 compilation 中。
An asset from a chunk was added to the compilation. 来自 chunk 的一个模块被添加到 compilation
compilation.mainTemplate.plugin('startup', function(source, module, hash) {
if (!module.chunks.length && source.indexOf('__ReactStyle__') === -1) {
var originName = module.origins && module.origins.length ? module.origins[0].name : 'main';
return ['if (typeof window !== "undefined") {',
' window.__ReactStyle__ = ' + JSON.stringify(classNames[originName]) + ';',
'}'
].join('\n') + source;
}
return source;
});The parser instance takes a String and callback and will return an expression when there's a match. parser 实例接收一个字符串及回调,如果存在匹配的话,会返回一个表达式。
compiler.parser.plugin("var rewire", function (expr) {
//if you original module has 'var rewire'
//you now have a handle on the expresssion object
return true;
});General purpose plugin interface for the AST of a code fragment. 访问一个代码片段的AST的通用插件接口。
General purpose plugin interface for the statements of the code fragment. 访问代码片段的语句的通用插件接口
abc(1) => call abc
a.b.c(1) => call a.b.c
abc => expression abc
a.b.c => expression a.b.c
(abc ? 1 : 2) => expression ?!
Return a boolean value to omit parsing of the wrong path. 返回一个布尔值来忽略解析错误的路径。
typeof a.b.c => typeof a.b.c
if(abc) {} => statement if
Return a boolean value to omit parsing of the wrong path. 返回一个布尔值来忽略解析错误的路径。
xyz: abc => label xyz
var abc, def => var abc + var def
Return false to not add the variable to the known definitions.
返回false来不把对应变量加入到已知定义中。
Evaluate an expression. 对一个表达式求值。
Evaluate the type of an identifier. 求值某个标识符的类型
Evaluate a identifier that is a free var. 对一个作为自由变量的标识符求值。
Evaluate a identifier that is a defined var. 对一个已定义变量的标识符求值。
Evaluate a call to a member function of a successfully evaluated expression. 对一个已成功求值的表达式的成员函数进行调用求值
Before the factory starts resolving. The data object has these properties:
在工厂开始解析前。data对象拥有下列属性:
-
contextThe absolute path of the directory for resolving. -
requestThe request of the expression. -
context用于解析的目录的绝对路径. -
request表达式的请求路径.
Plugins are allowed to modify the object or to pass a new similar object to the callback. 插件可以修改该对象或者传递一个新的相似的对象给回调函数
After the factory has resolved the request. The data object has these properties:
在工厂成功解析请求后。data对象拥有下列属性:
-
requestThe resolved request. It acts as an identifier for the NormalModule. -
userRequestThe request the user entered. It's resolved, but does not contain pre or post loaders. -
rawRequestThe unresolved request. -
loadersA array of resolved loaders. This is passed to the NormalModule and they will be executed. -
resourceThe resource. It will be loaded by the NormalModule. -
parserThe parser that will be used by the NormalModule. -
request解析了的请求,作为该模块的标识符。 -
userRequest用户输入的请求。已被解析,但不包含前置或后置 loader -
rawRequest尚未解析的请求 -
loaders一组已解析的 loader,这会被传递给 NormalModule 并被执行。 -
resource资源路径。会被 NormalModule 加载。 -
parser会被 NormalModule 使用的 parser
-
compiler.resolvers.normalResolver for a normal module -
compiler.resolvers.contextResolver for a context module -
compiler.resolvers.loaderResolver for a loader -
compiler.resolvers.normal普通模块的解析器 -
compiler.resolvers.context上下文模块的解析器 -
compiler.resolvers.loaderloader 的解析器
Any plugin should use this.fileSystem as fileSystem, as it's cached. It only has async named functions, but they may behave sync, if the user uses a sync file system implementation (i. e. in enhanced-require).
任何插件都应该使用this.fileSystem作为文件系统,因为其已被缓存。它只有异步方法。但可以表现为同步执行,如果用户使用了一个同步文件系统的实现(譬如,在 enhanced-require 中)
To join paths any plugin should use this.join. It normalizes the paths. There is a this.normalize too.
如果要合并路径,任何插件都应该使用this.join。其标准化了路径。也存在一个this.normalize方法。
A bailing async forEach implementation is available on this.forEachBail(array, iterator, callback).
一个 bailing ???异步遍历的方法:this.forEachBail(array, iterator, callback)
To pass the request to other resolving plugins, use the this.doResolve(types: String|String[], request: Request, callback) method. types are multiple possible request types that are tested in order of preference.
为了传递请求数据给其他解析插件,可使用方法this.doResolve(types: String|String[], request: Request, callback)
interface Request {
path: String // The current directory of the request
request: String // The current request string
query: String // The query string of the request, if any
module: boolean // The request begins with a module
directory: boolean // The request points to a directory
file: boolean // The request points to a file
resolved: boolean // The request is resolved/done
// undefined means false for boolean fields
}
// Examples
// from /home/user/project/file.js: require("../test?charset=ascii")
{
path: "/home/user/project",
request: "../test",
query: "?charset=ascii"
}
// from /home/user/project/file.js: require("test/test/")
{
path: "/home/user/project",
request: "test/test/",
module: true,
directory: true
}Before the resolving process starts. 在解析流程开始前
Before a single step in the resolving process starts. 在解析流程其中一步开始前。
A module request is found and should be resolved. 找到一个需要被解析的模块请求,
A directory request is found and should be resolved. 找到一个需要被解析的目录请求
A file request is found and should be resolved. 找到一个需要被解析的文件请求
Here is a list what the default plugins in webpack offer. They are all (request: Request) async waterfall.
下面列表是 webpack 默认的插件提供的。它们全都是(request: Request),异步瀑布流执行
The process for normal modules and contexts is module -> module-module -> directory -> file.
对普通模块和上下文模块的处理流程是:module -> module-module -> directory -> file
The process for loaders is module -> module-loader-module -> module-module -> directory -> file.
对 loader 的处理流程是module -> module-loader-module -> module-module -> directory -> file
A module should be looked up in a specified directory. path contains the directory.
一个模块应该在一个特定目录里寻找。path含有该目录。
Used before module templates are applied to the module name. The process continues with module-module.
使用在模块的模版被应用到模块名之前。流程以module-module继续。