Preface
In today's increasingly complex front-end engineering, module packaging tools have played an increasingly important role in our development, and webpack
is one of the most popular packaging tools.
Speaking of webpack
, many friends may feel both familiar and unfamiliar. Familiar is because we will use it in almost every project, and because of the complex configuration and variety of functions of webpack
Especially when we use umi.js
help us package the webpack configuration by another layer, webpack
seems to be farther away and unfathomable.
When the interviewer asks if you know about webpack
, maybe you can webpack loader
and plugin
, and even a series of plug-ins and a series of configurations for on-demand loading and packaging optimization, then do you know his operating mechanism and the realization of the principle of it, then we will explore together today webpack
ability borders, try to understand webpack
some of the implementation process and principles, refused to do API
engineer.
Do you know what webpack does?
From the description on the official website, it is not difficult to understand that webpack
actually has the following functions:
- Module packaging. You can package and integrate the files of different modules together, and ensure that the references between them are correct and the execution is orderly. Using packaging, we can freely divide the file modules according to our own business during development to ensure the clarity and readability of the project structure.
- Compilation compatible. In front of the "ancient times", a bunch of handwritten browser compatible code that has been the front-end engineers make things scalp tingling, but today this problem is greatly weakened by
webpack
ofLoader
mechanism, not only can help us code Dopolyfill
, you can also compile and convert.less, .vue, .jsx
that are not recognized by the browser, so that we can use new features and new syntax for development during development, and improve development efficiency. - Capacity expansion. By
webpack
ofPlugin
mechanism, the basis of our modular compiler compatible packaging and on-demand loading can be further achieved, such as code compression and a series of functions to help us further improve the quality automation, engineering efficiency and output of the package.
Tell me about the operating principle of module packaging?
If the interviewer asks you Webpack
these modules together and ensures that they work properly, do you understand it?
First of all, we should briefly understand the entire packaging process of webpack
- 1. Read the configuration parameters of
webpack
- 2. Start
webpack
, create aCompiler
object and start parsing the project; - 3. Start parsing from the entry file (
entry
), and find the imported dependent modules, recursively traverse the analysis, and form a dependency tree; Loader
to compile the dependent module files of different file types, and finally convert them toJavascript
files;- 5. In the whole process,
webpack
hooks
through the publish and subscribe mode, andwebpack
can monitor these key event nodes and execute the plug-in task to achieve the purpose of intervening in the output result.
The parsing and construction of the file is a relatively complicated process. The webpack
source code mainly relies on the compilation
two core objects, compiler
and 06081f1ebaafd7.
compiler
object is a global singleton, which is responsible for controlling the entire webpack
package construction process. compilation
object is the context object for each construction. It contains all the information needed for the current construction. Every hot update and rebuild, compiler
will regenerate a new compilation
object, which is responsible for the construction process of this update.
The dependency between each module depends on the AST
syntax tree. Each module file by Loader
after parsing is complete, by acorn
library generation module code AST
syntax tree, by the syntax tree can also analyze whether this module dependent module, and then continue to execute the next cycle to compile a module resolution.
The final Webpack
packed out bundle
file is a IIFE
execution of the function.
// webpack 5 打包的bundle文件内容
(() => { // webpackBootstrap
var __webpack_modules__ = ({
'file-A-path': ((modules) => { // ... })
'index-file-path': ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { // ... })
})
// The module cache
var __webpack_module_cache__ = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// Create a new module (and put it into the cache)
var module = __webpack_module_cache__[moduleId] = {
// no module.id needed
// no module.loaded needed
exports: {}
};
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__ "moduleId");
// Return the exports of the module
return module.exports;
}
// startup
// Load entry module and return exports
// This entry module can't be inlined because the eval devtool is used.
var __webpack_exports__ = __webpack_require__("./src/index.js");
})
And webpack4
comparison, webpack5
pack out of the bundle made considerable streamlining. In the above package demo
, there are only three variables and one function method in the entire immediate execution function. __webpack_modules__
stores the JS content of each file module after compilation, __webpack_module_cache__
used as a module cache, and __webpack_require__
is a set of dependencies implemented within Webpack
Introduce functions. The last sentence is the starting point for the code to run, starting from the entry file to start the entire project.
Among them, it is worth mentioning that the __webpack_require__
module introduces functions. When we develop modularity, we usually use the ES Module
or CommonJS
specification to export/import dependent modules. When webpack
packaged and compiled, it will be replaced with its own __webpack_require__
to realize the introduction of the module. And export, so as to realize the module caching mechanism, and smooth out some differences between different module specifications.
Do you know what sourceMap is?
Speaking of sourceMap
, many friends may immediately think of the devtool
Webpack
configuration, as well as the corresponding eval
, eval-cheap-source-map
and other optional values and their meanings. In addition to knowing the difference between different parameters and the difference in performance, we can also learn about the implementation of sourceMap
sourceMap
is a technology that maps compiled, packaged, and compressed code back to the source code. Since the packaged and compressed code is not readable at all, once an error or problem is encountered during development, the problem of debug
It will bring a very bad experience. sourceMap
can help us quickly locate the source code and improve our development efficiency. sourceMap
is not Webpack
unique function of Webpack
, but 06081f1ebab14e supports sourceMap
, like JQuery
also supports souceMap
.
Since it is a source code mapping, it is necessary to have a mapping file to mark the location of the corresponding source code in the obfuscated code. Usually this mapping file .map
, and the data structure inside is roughly like this:
{
"version" : 3, // Source Map版本
"file": "out.js", // 输出文件(可选)
"sourceRoot": "", // 源文件根目录(可选)
"sources": ["foo.js", "bar.js"], // 源文件列表
"sourcesContent": [null, null], // 源内容列表(可选,和源文件列表顺序一致)
"names": ["src", "maps", "are", "fun"], // mappings使用的符号名称列表
"mappings": "A,AAAB;;ABCDE;" // 带有编码映射数据的字符串
}
The mappings
data has the following rules:
- Each group of a line in the generated file is separated by ";";
- Each paragraph is separated by ",";
- Each segment consists of 1, 4 or 5 variable length fields;
With this mapping file, we only need to add this comment at the end of our compression code to make the sourceMap effective:
//# sourceURL=/path/to/file.js.map
With this comment, the browser will sourceURL
, and after parsing it through the interpreter, the mapping between the source code and the obfuscated code will be realized. Therefore, sourceMap is actually a technology that requires browser support.
If we look closely at the bundle file packaged by webpack, we can find that in the default development
development mode, _webpack_modules__
will be added to the end of the code of each 06081f1ebab23a file module to support //# sourceURL=webpack://file-path?
There is a set of more complicated rules for the generation of sourceMap mapping table. Interested friends can read the following articles to help understand the principle of soucrMap:
The principle of [1]
Source Maps under the hood – VLQ, Base64 and Yoda[2]
Have you written Loader? Briefly describe the idea of writing loader?
From the above we can actually know the code package, Webpack
final result is a packed out Javascript
code actually Webpack
internal default only able to handle JS
module code, in the packaging process, by default all files are encountered It JavaScript
code, so when there is a non- JS
type file in the project, we need to perform the necessary conversion to continue the packaging task, which is also the meaning of the existence of the Loader
We should already be very familiar with the configuration and use of Loader
// webpack.config.js
module.exports = {
// ...other config
module: {
rules: [
{
test: /^your-regExp$/,
use: [
{
loader: 'loader-name-A',
},
{
loader: 'loader-name-B',
}
]
},
]
}
}
It can be seen from the configuration that for each file type, loader
supports multiple configurations in the form of an array. Therefore, when Webpack
converts the file type, each loader
will be called in sequence and the previous loader
returned. The content will be used as an loader
for the next 06081f1ebab324. Therefore loader
needs to follow some specifications. For example, the return value must be a standard JS
code string to ensure that the next loader
can work normally. At the same time, the development needs to strictly follow the "single responsibility" and only care about the output of loader
Output.
loader
function of this
context consists webpack
provided by this
relevant properties of the object, thus providing a current loader
various information data required, in fact, this this
points to a called loaderContext
of loader-runner
specific object. Interested friends can read the source code by themselves.
module.exports = function(source) {
const content = doSomeThing2JsString(source);
// 如果 loader 配置了 options 对象,那么this.query将指向 options
const options = this.query;
// 可以用作解析其他模块路径的上下文
console.log('this.context');
/*
* this.callback 参数:
* error:Error | null,当 loader 出错时向外抛出一个 error
* content:String | Buffer,经过 loader 编译后需要导出的内容
* sourceMap:为方便调试生成的编译后内容的 source map
* ast:本次编译生成的 AST 静态语法树,之后执行的 loader 可以直接使用这个 AST,进而省去重复生成 AST 的过程
*/
this.callback(null, content);
// or return content;
}
For more detailed development documents, you can directly view Loader API [3] on the official website.
Have you written Plugin? Briefly describe the idea of writing plugin?
If Loader
is responsible for file conversion, then Plugin
is responsible for function expansion. Loader
and Plugin
as Webpack
, bear two different responsibilities.
As mentioned above, webpack
based on the publish-subscribe model and broadcasts many events during its running life cycle. The plug-in can perform its own plug-in tasks at a specific stage by listening to these events, so as to achieve the functions they want.
Since the release based on a subscription model, then know Webpack
in the end what events provide a hook for plug-in developers to use is very important, mentioned above had compiler
and compilation
is Webpack
two very core of the object, which compiler
exposed and Webpack
entire life Cycle-related hooks ( compiler-hooks [4]), while compilation
exposes event hooks with smaller granularity related to modules and dependencies ( Compilation Hooks [5]).
Webpack
event mechanism based webpack
set themselves to achieve Tapable
event flow scheme ( GitHub [6])
// Tapable的简单使用
const { SyncHook } = require("tapable");
class Car {
constructor() {
// 在this.hooks中定义所有的钩子事件
this.hooks = {
accelerate: new SyncHook(["newSpeed"]),
brake: new SyncHook(),
calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"])
};
}
/* ... */
}
const myCar = new Car();
// 通过调用tap方法即可增加一个消费者,订阅对应的钩子事件了
myCar.hooks.brake.tap("WarningLampPlugin", () => warningLamp.on());
Plugin
development and development Loader
, the need to follow some of the norms and principles on the development of:
- The plug-in must be a function or an
apply
method in order to access thecompiler
instance; compiler
andcompilation
objects passed to each plug-in are the same reference. If the attributes on them are modified in one plug-in, the subsequent plug-ins will be affected;- Asynchronous events need to call the callback function to notify
Webpack
enter the next process when the plug-in finishes processing the task, otherwise it will get stuck;
After understanding the above content, it is not difficult Webpack Plugin
class MyPlugin {
apply (compiler) {
// 找到合适的事件钩子,实现自己的插件功能
compiler.hooks.emit.tap('MyPlugin', compilation => {
// compilation: 当前打包构建流程的上下文
console.log(compilation);
// do something...
})
}
}
For more detailed development documents, you can directly view Plugin API [7] on the official website.
At last
This article also combines some excellent articles and webpack
itself, and roughly talks about a few relatively important concepts and processes. The implementation details and design ideas need to be read and understood slowly in combination with the source code.
Webpack
has changed the traditional front-end development model and is the cornerstone of modern front-end development. Such an excellent open source project has many excellent design ideas and concepts that can be used for reference. Naturally, we should not just stop at API
, try to read the source code with questions, understand the implementation process and principles, and let us learn More knowledge and deeper understanding can be used in projects with ease.
Reference
[1] Explore the principle of Source Map: https://blog.fundebug.com/2018/10/12/understanding_frontend_source_map/
[2]Source Maps under the hood – VLQ, Base64 and Yoda: *https://docs.microsoft.com/zh-cn/archive/blogs/davidni/source-maps-under-the-hood-vlq-base64-and-yoda.
[3]Loader API: *https://www.webpackjs.com/api/loaders/.
[4]compiler-hooks: https://webpack.js.org/api/compiler-hooks/
[5]Compilation Hooks: https://webpack.js.org/api/compilation-hooks/.
[6]github: https://github.com/webpack/tapable.
[7]Plugin API: https://www.webpackjs.com/api/plugins/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。