Hi everyone, I'm Kasong.
JS
talk about how to implement a modern 0613978c2efd2c module packager with 90 lines of code.
Although our packer is mini, it implements the core function webpack
Moreover, I know that you have a headache when you see a large section of code, so this article is all pictures. After reading interested, here is the warehouse address the complete code of , only 90 lines of code oh.
Let's start happily.
Generate dependency graph
If the application is a ball of yarn, then the entry file is the thread head. The first thing the packer has to do is:
Follow the end of the thread and start filtering the direction of the entire line
Assuming that the entry file is entry.js
:
// entry.js
import a from './a.js';
import b from './b.js';
console.log(a, b);
He relied on a.js
and b.js
.
em... It's a bit too crude, let's expand on a.js
and b.js
:
// a.js
import c from './c.js';
// ...
// b.js
import d from './d.js';
import e from './e.js';
// ...
So the whole dependency is like this:
The packager will start from the entry file and try to establish js
file), which is the we just talked about and start to filter the whole line along the line to .
The dependencies between modules can be learned import statement in the module code.
In order to analyze Import declaration can be used
babel
other compiler tools module code into AST
(abstract syntax tree).
Traverse AST
, the node of ImportDeclaration
import statement.
Finally, we re-convert AST
target code. It may be necessary to perform some conversions on the code according to the host environment (usually a browser) where the code will be executed.
For example, the browser does not support import './a.js'
syntax such as ESM
, then we need to convert all ESM
syntax to CJS
syntax.
// 源代码
import './a.js';
// 转换后
require('./a.js');
Therefore, for any module ( js
file), you will experience:
The data structure on the right containing the dependency between object code and the
asset
.
Each asset
can find dependent modules through the dependencies between modules, repeat this process to generate a new
asset
, and finally form all the dependencies between asset
The complete dependency of the application is called dependency graph (dependency graph).
Package code
Next, need only to traverse dependency FIG , all asset
the packaged object code together on the line.
All the code will be packed in a immediate execution function :
(function(modules) {
// 打包好的代码
})(modules)
modules
saves all asset
and their dependencies.
If you modules
, you can go to the warehouse at the end of the article to read the code
Just said, asset
of object code is
CJS
specification, like this:
// entry.js
require('./a.js');
require('./b.js');
This means we need to achieve:
require
object code
asset
used to introduce dependencies)module
object (used to save the data exported after the execution of theasset
object code of
At the same time, in order to prevent the variables in target codes
asset
from polluting each other, each target code needs an independent scope.
We wrap the target code
function:
// 我们操作的是字符串模版
`function (require, module, exports) {
${asset.code}
}`
Therefore, the final packaged result is:
(function(modules) {
function require() {// ...}
require(入口asset的ID)
})(modules)
This string is wrapped in the browser <script>
tag and will be executed in sequence:
require (the ID of the entry asset), execute the
object code of the
entry asset
target internal code calls
require
perform otherasset
ofobject code
- Go on step by step...
Summarize
The working principle of the packer is divided into two steps:
- Start traversing from the entry file, and generate dependency graph
- According to the dependency graph, package the code into a to execute the function
This packager is still very immature, missing many necessary functions, such as:
- Resolve circular dependencies
- Cache
But don’t you hide your flaws~
Welcome to join the human high-quality front-end framework research group , take flight
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。