Skip to content

Commit 23987f6

Browse files
committed
update
1 parent 60b6c4f commit 23987f6

18 files changed

+975
-339
lines changed

docs/js-manual/asyncPool.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## 最大并发任务执行
2+
3+
### async 函数和 promise实现
4+
<<< ./scripts/asyncPool1.js
5+
6+
## async函数和generator函数
7+
<<< ./scripts/asyncPool2.js

docs/js-manual/config/catalog.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const catalog: SidebarItem = {
88
items: [
99
{ text: '中间件', link: '/js-manual/middleware' },
1010
{text: '节流', link: '/js-manual/throttle'},
11-
{ text: '防抖', link: '/js-manual/debounce'}
11+
{ text: '防抖', link: '/js-manual/debounce'},
12+
{text: '最大并发', link: '/js-manual/asyncPool'},
1213
]
1314
}

docs/js-manual/scripts/asyncPool1.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
async function asyncPool(poolLimit, tasks) {
2+
let ret = [];
3+
let executing = new Set();
4+
for (const taskItem of tasks) {
5+
const p = Promise.resolve().then(() => taskItem);
6+
ret.push(p);
7+
executing.add(p);
8+
const clean = () => executing.delete(p);
9+
p.then(clean).catch(clean);
10+
if (executing.size >= poolLimit) {
11+
await Promise.race(executing);
12+
}
13+
}
14+
return Promise.all(ret);
15+
}
16+
17+
const sleep = (time) => new Promise((resolve) => setTimeout(() => resolve(time), time));
18+
19+
const tasks = [
20+
sleep(1000),
21+
sleep(6000),
22+
sleep(3000),
23+
sleep(2000),
24+
]
25+
26+
asyncPool(2, tasks).then(res => {
27+
console.log(res)
28+
})

docs/js-manual/scripts/asyncPool2.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
async function* asyncPool(concurrency,tasks) {
2+
const executing = new Set();
3+
async function consume() {
4+
const [promise, value] = await Promise.race(executing);
5+
executing.delete(promise);
6+
return value;
7+
}
8+
for (const task of tasks) {
9+
const promise = Promise.resolve(task).then(value => [promise, value]);
10+
executing.add(promise);
11+
if (executing.size >= concurrency) {
12+
yield await consume();
13+
}
14+
}
15+
while (executing.size) {
16+
yield await consume();
17+
}
18+
}
19+
20+
21+
const sleep = (time) => new Promise((resolve) => setTimeout(() => resolve(time), time));
22+
23+
const tasks = [
24+
sleep(1000),
25+
sleep(6000),
26+
sleep(3000),
27+
sleep(2000),
28+
]
29+
30+
for await (const item of asyncPool(2, tasks)) {
31+
console.log(item);
32+
}

docs/webpack/code1.md

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# 源码解析1(创建Compiler对象)
2+
webpack方法运行时,首先会通过createCompiler方法创建Compiler对象,参数`rawOptions`通过命令行或者`webpack.config.js`配置
3+
文件传入的原始配置对象
4+
5+
![init](./images/create_compiler.png)
6+
7+
## 1.标准化webpack配置
8+
`getNormalizedWebpackOptions`方法的作用就是对用户传入的配置标准化成webpack规定的配置,对于用户传入的,就使用用户的,用户没有
9+
传入的就用一些初始值替代,比如`undefined`,`[]`,`{}`,最终生成的配置类似于下面这样:
10+
11+
![normal](./images/normal.png)
12+
13+
## 2.对`infrastructureLogging`选项和`context`选项设置默认值
14+
`applyWebpackOptionsBaseDefaults`方法的代码如下,对`infrastructureLogging`选项和`context`选项设置
15+
::: tip
16+
其实后面`applyWebpackOptionsDefaults`方法会对所有的options设置默认值,
17+
这里先对`context``infrastructureLogging`选项设置默认值,是因为在后面的
18+
初始化`Compiler``NodeEnvironmentPlugin`插件的调用中使用了`context``infrastructureLogging`
19+
:::
20+
```js
21+
const applyWebpackOptionsBaseDefaults = options => {
22+
F(options, "context", () => process.cwd());
23+
applyInfrastructureLoggingDefaults(options.infrastructureLogging);
24+
};
25+
```
26+
最终`options对象`如下:
27+
![normal2](./images/normal2.png)
28+
29+
## 3.创建`Compiler`对象
30+
`Compiler`构造函数主要初始化了一些hooks和一些变量,TODO:
31+
32+
## 4.调用`NodeEnvironmentPlugin`内置插件为`Compiler`设置文件系统
33+
`NodeEnvironmentPlugin`是webpack的一个内置插件,主要为`Compiler`对象设置文件系统
34+
主要包含四部分
35+
- inputFileSystem 读文件
36+
- outputFileSystem 写文件
37+
- intermediateFileSystem TODO:
38+
- watchFileSystem 对文件变动监控
39+
40+
![file](./images/file.png)
41+
42+
## 5.遍历调用所有配置文件中传入的插件
43+
44+
用户配置文件的`plugins`可能会传入一些插件,这里集中对插件进行调用
45+
webpack插件可以是一个function也可以是一个带有apply方法的类,所以这里进行了一次判断
46+
::: code-group
47+
```js [function插件]
48+
function myPlugin(compiler) {
49+
//do something
50+
}
51+
```
52+
```js [class插件]
53+
class MyPlugin{
54+
apply(compiler) {
55+
//do something
56+
}
57+
}
58+
```
59+
:::
60+
## 6.设置其他默认选项
61+
`applyWebpackOptionsDefaults`方法为选项对象设置默认值,最终的结果如下
62+
63+
![options2](./images/options2.png)
64+
65+
## 7.`environment``afterEnvironment`钩子函数调用
66+
代码如下:
67+
```js
68+
compiler.hooks.environment.call();
69+
compiler.hooks.afterEnvironment.call();
70+
```
71+
两个钩子函数接连调用,主要是在编译器准备环境时调用,
72+
时机就在配置文件中初始化插件之后。
73+
::: tip
74+
比如 webpack内置插件`WatchIgnorePlugin`就注册了`afterEnvironment`钩子函数,在环境准备完成后
75+
改变`compiler`对象的`watchFileSystem`属性
76+
```js{16}
77+
class WatchIgnorePlugin {
78+
/**
79+
* @param {WatchIgnorePluginOptions} options options
80+
*/
81+
constructor(options) {
82+
validate(options);
83+
this.paths = options.paths;
84+
}
85+
86+
/**
87+
* Apply the plugin
88+
* @param {Compiler} compiler the compiler instance
89+
* @returns {void}
90+
*/
91+
apply(compiler) {
92+
compiler.hooks.afterEnvironment.tap("WatchIgnorePlugin", () => {
93+
compiler.watchFileSystem = new IgnoringWatchFileSystem(
94+
compiler.watchFileSystem,
95+
this.paths
96+
);
97+
});
98+
}
99+
}
100+
```
101+
:::
102+
## 8.WebpackOptionsApply 调用
103+
`WebpackOptionsApply``process`方法根据不同的options选项初始化了webpack内置的插件
104+
并且执行了三个钩子函数
105+
1. entryOption,在 webpack 选项中的 entry 被处理过之后调用。
106+
```js
107+
new EntryOptionPlugin().apply(compiler);
108+
compiler.hooks.entryOption.call(options.context, options.entry);
109+
```
110+
::: tip
111+
`EntryOptionPlugin``DllPlugin`插件注册了entryOption钩子函数
112+
:::
113+
- EntryOptionPlugin 代码如下
114+
```js {14-20}
115+
class EntryOptionPlugin {
116+
apply(compiler) {
117+
compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => {
118+
EntryOptionPlugin.applyEntryOption(compiler, context, entry);
119+
return true;
120+
});
121+
}
122+
static applyEntryOption(compiler, context, entry) {
123+
if (typeof entry === "function") {
124+
const DynamicEntryPlugin = require("./DynamicEntryPlugin");
125+
new DynamicEntryPlugin(context, entry).apply(compiler);
126+
} else {
127+
const EntryPlugin = require("./EntryPlugin");
128+
for (const name of Object.keys(entry)) {
129+
const desc = entry[name];
130+
const options = EntryOptionPlugin.entryDescriptionToOptions(
131+
compiler,
132+
name,
133+
desc
134+
);
135+
for (const entry of desc.import) {
136+
new EntryPlugin(context, entry, options).apply(compiler);
137+
}
138+
}
139+
}
140+
}
141+
}
142+
```
143+
首先在apply方法中注册一个entryOption钩子函数,entryOption钩子函数被调用时
144+
首先对entry对象做一个标准化,生成的options如下
145+
146+
![entry_option](./images/entry_option.png)
147+
148+
然后调用了EntryPlugin的apply方法,如下
149+
```js
150+
class EntryPlugin {
151+
constructor(context, entry, options) {
152+
this.context = context;
153+
this.entry = entry;
154+
this.options = options || "";
155+
}
156+
157+
apply(compiler) {
158+
//注册compilation钩子函数
159+
compiler.hooks.compilation.tap(
160+
"EntryPlugin",
161+
(compilation, { normalModuleFactory }) => {
162+
compilation.dependencyFactories.set(
163+
EntryDependency,
164+
normalModuleFactory
165+
);
166+
}
167+
);
168+
169+
const { entry, options, context } = this;
170+
const dep = EntryPlugin.createDependency(entry, options);
171+
//注册make钩子函数
172+
compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => {
173+
compilation.addEntry(context, dep, options, err => {
174+
callback(err);
175+
});
176+
});
177+
}
178+
179+
static createDependency(entry, options) {
180+
const dep = new EntryDependency(entry);
181+
dep.loc = { name: typeof options === "object" ? options.name : options };
182+
return dep;
183+
}
184+
}
185+
```
186+
首先注册了compilation钩子函数,然后生成entry依赖,最后注册make钩子
187+
188+
2. afterPlugins 在初始化内部插件集合完成设置之后调用。
189+
```js
190+
compiler.hooks.afterPlugins.call(compiler);
191+
```
192+
::: tip
193+
`ModuleFederationPlugin`插件注册了afterPlugins钩子函数
194+
:::
195+
3. afterResolvers resolver 设置完成之后触发。
196+
```js
197+
compiler.hooks.afterResolvers.call(compiler);
198+
```
199+
200+
201+
## 9.`initialize`钩子函数调用
202+
当编译器对象被初始化时调用。
203+
```js
204+
compiler.hooks.initialize.call();
205+
```
206+
207+
这样就完成了Compiler对象的创建

docs/webpack/code2.md

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# 源码解析2(Compiler.run)
2+
3+
## 1. 调用beforeRun和run钩子函数
4+
如下:
5+
6+
![run](./images/run.png)
7+
8+
在 run钩子函数调用完毕后,
9+
首先调用了`readRecords`方法,其作用是读取webpack配置中`recordsInputPath`的路径,用来初始化`Compiler.record`,
10+
大致逻辑如下
11+
``` js {15}
12+
_readRecords(callback) {
13+
if (!this.recordsInputPath) {
14+
this.records = {};
15+
return callback();
16+
}
17+
this.inputFileSystem.stat(this.recordsInputPath, err => {
18+
// It doesn't exist
19+
// We can ignore this.
20+
if (err) return callback();
21+
22+
this.inputFileSystem.readFile(this.recordsInputPath, (err, content) => {
23+
if (err) return callback(err);
24+
25+
try {
26+
this.records = parseJson(content.toString("utf-8"));
27+
} catch (e) {
28+
return callback(new Error(`Cannot parse records: ${e.message}`));
29+
}
30+
31+
return callback();
32+
});
33+
});
34+
}
35+
```
36+
37+
后面接着调用了`this.compile方法`
38+
39+
## compile方法的调用
40+
41+
compile方法大致做了
42+
- 创建编译参数
43+
- 调用beforeCompile钩子
44+
- 调用compile钩子
45+
- 创建compilation
46+
- make阶段
47+
- 调用finishMake钩子
48+
``` js
49+
//去掉了不重要的代码
50+
compile(callback) {
51+
//创建编译参数
52+
const params = this.newCompilationParams();
53+
//调用beforeCompile钩子函数
54+
this.hooks.beforeCompile.callAsync(params, err => {
55+
if (err) return callback(err);
56+
//调用compile钩子函数
57+
this.hooks.compile.call(params);
58+
//创建compilation对象
59+
const compilation = this.newCompilation(params);
60+
//调用make钩子
61+
this.hooks.make.callAsync(compilation, err => {
62+
if (err) return callback(err);
63+
//调用finish钩子
64+
this.hooks.finishMake.callAsync(compilation, err => {
65+
if (err) return callback(err);
66+
67+
process.nextTick(() => {
68+
compilation.finish(err => {
69+
if (err) return callback(err);
70+
compilation.seal(err => {
71+
if (err) return callback(err);
72+
this.hooks.afterCompile.callAsync(compilation, err => {
73+
if (err) return callback(err);
74+
return callback(null, compilation);
75+
});
76+
});
77+
});
78+
});
79+
});
80+
});
81+
});
82+
}
83+
```
84+
85+
compile方法的逻辑比较复杂,下面我们继续它的执行逻辑

0 commit comments

Comments
 (0)