웹 팩 시리즈 - 제3 편 프로 세 스 잡기
웹 팩 시리즈 - 첫 번 째 기초 잡기 웹 팩 시리즈 - 두 번 째 플러그 인 메커니즘 잡기 웹 팩 시리즈 - 세 번 째 프로 세 스 잡기
머리말
이 글 은 개인 적 으로 웹 팩 프로 세 스 를 정리 하기 위해 내부 의 많은 세부 사항 에 관심 을 가지 지 않 았 습 니 다. 오류 가 있 으 면 가볍게 뿌 려 주세요 ~
디 버 깅
1. 다음 명령 으로 프로젝트 를 실행 합 니 다.
./scripts/build.js
디 버 깅 을 시작 하고 싶 은 곳 입 니 다.node --inspect-brk ./scripts/build.js --inline --progress
2. 오픈
chrome://inspect/#devices
하면 디 버 깅 가능흐름 도
입구
입구
bulid.js
에 있 습 니 다. 그 중의 코드 는 먼저 웹 팩 을 예화 한 다음 에 호출 compiler run
하 는 것 을 볼 수 있 습 니 다.function build(previousFileSizes) {
let compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
...
});
}
entry-option(compiler)
webpack.js
웹 팩 은 nodemoduls 아래
\webpack\lib\webpack.js
(이 앞 에 입구 매개 변수 가 합 쳐 져 있 음) 이 파일 을 찾 으 면 다음 과 같은 코드 를 볼 수 있 습 니 다.const webpack = (options, callback) => {
......
let compiler;
//
if (Array.isArray(options)) {
compiler = new MultiCompiler(options.map(options => webpack(options)));
} else if (typeof options === "object") {
// webpack
options = new WebpackOptionsDefaulter().process(options);
console.log(options) //
// compiler
compiler = new Compiler(options.context);
compiler.options = options;
// webpack
new NodeEnvironmentPlugin().apply(compiler);
// tabpable
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
plugin.apply(compiler);
}
}
// environment/afterEnviroment
compiler.hooks.environment.call();
compiler.hooks.afterEnvironment.call();
// compiler , entry-option
compiler.options = new WebpackOptionsApply().process(options, compiler);
} else {
throw new Error("Invalid argument: options");
}
if (callback) {
......
compiler.run(callback);
}
return compiler;
};
options 가 저장 한 것 은 이번 웹 팩 의 일부 설정 매개 변수 이 며, 그 중
plugins webpack
입 니 다.new WebpackOptionsApply().process
process(options, compiler) {
let ExternalsPlugin;
compiler.outputPath = options.output.path;
compiler.recordsInputPath = options.recordsInputPath || options.recordsPath;
compiler.recordsOutputPath =
options.recordsOutputPath || options.recordsPath;
compiler.name = options.name;
compiler.dependencies = options.dependencies;
if (typeof options.target === "string") {
let JsonpTemplatePlugin;
let FetchCompileWasmTemplatePlugin;
let ReadFileCompileWasmTemplatePlugin;
let NodeSourcePlugin;
let NodeTargetPlugin;
let NodeTemplatePlugin;
switch (options.target) {
case "web":
JsonpTemplatePlugin = require("./web/JsonpTemplatePlugin");
FetchCompileWasmTemplatePlugin = require("./web/FetchCompileWasmTemplatePlugin");
NodeSourcePlugin = require("./node/NodeSourcePlugin");
new JsonpTemplatePlugin().apply(compiler);
new FetchCompileWasmTemplatePlugin({
mangleImports: options.optimization.mangleWasmImports
}).apply(compiler);
new FunctionModulePlugin().apply(compiler);
new NodeSourcePlugin(options.node).apply(compiler);
new LoaderTargetPlugin(options.target).apply(compiler);
break;
case "webworker":......
......
}
}
new JavascriptModulesPlugin().apply(compiler);
new JsonModulesPlugin().apply(compiler);
new WebAssemblyModulesPlugin({
mangleImports: options.optimization.mangleWasmImports
}).apply(compiler);
new EntryOptionPlugin().apply(compiler);
// entry-options context entry
compiler.hooks.entryOption.call(options.context, options.entry);
new CompatibilityPlugin().apply(compiler);
......
new ImportPlugin(options.module).apply(compiler);
new SystemPlugin(options.module).apply(compiler);
}
run(compiler)
run 을 호출 할 때 내부 트리거
beforeRun
를 한 다음 읽 기 recodes
(records 에 대해 서 는 이 문 서 를 참고 할 수 있 습 니 다) 전에 run 이벤트 점 을 터치 합 니 다. 이 두 사건 은 모두 비동기 형식 입 니 다. 주의 run webpack
.마지막 으로 호출 된 것 은 compile
방법 이 며, 동시에 들 어 오 는 것 은 onCompiled 함수 입 니 다.run(callback) {
if (this.running) return callback(new ConcurrentCompilationError());
const finalCallback = (err, stats) => {
......
};
this.running = true;
const onCompiled = (err, compilation) => {
....
};
this.hooks.beforeRun.callAsync(this, err => {
if (err) return finalCallback(err);
this.hooks.run.callAsync(this, err => {
if (err) return finalCallback(err);
this.readRecords(err => {
if (err) return finalCallback(err);
this.compile(onCompiled);
});
});
});
}
compile(compiler)
copile 방법 은 주로 트리거
beforeCompile、compile、make
와 compilation
를 볼 수 있 습 니 다. 여기 서 copile 에 전 달 된 newCompilationParams
매개 변 수 를 볼 수 있 습 니 다. 이 매개 변 수 는 뒤의 상대 적 인 절차 에서 도 비교적 중요 합 니 다. 여기 서 먼저 볼 수 있 습 니 다.compile(callback) {
const params = this.newCompilationParams();
// beforeCompile, CompilationParams
this.hooks.beforeCompile.callAsync(params, err => {
if (err) return callback(err);
// compile, CompilationParams
this.hooks.compile.call(params);
// compilation
const compilation = this.newCompilation(params);
// make
this.hooks.make.callAsync(compilation, err => {
....
});
});
}
new Compilation Params 가 돌아 오 는 매개 변 수 는 각각 두 공장 함수 와 하나의 set 집합 이다.
newCompilationParams() {
const params = {
normalModuleFactory: this.createNormalModuleFactory(),
contextModuleFactory: this.createContextModuleFactory(),
compilationDependencies: new Set()
};
return params;
}
compilation(compiler)
위의 copile 방법 을 보면 compilation 는 new Compilation 방법 으로 호출 되 어 생 성 된 것 입 니 다. 그리고 이벤트 포인트
thisCompilation compilation
를 터치 하면 알 수 있 습 니 다 compilation
. 플러그 인 을 작성 할 때 이 대상 을 빨리 사용 해 야 한다 면 이 두 이벤트 에서 진행 해 야 합 니 다.createCompilation() {
return new Compilation(this);
}
newCompilation(params) {
const compilation = this.createCompilation();
compilation.fileTimestamps = this.fileTimestamps;
compilation.contextTimestamps = this.contextTimestamps;
compilation.name = this.name;
compilation.records = this.records;
compilation.compilationDependencies = params.compilationDependencies;
// thisCompilation compilation, compilation params
this.hooks.thisCompilation.call(compilation, params);
this.hooks.compilation.call(compilation, params);
return compilation;
}
다음은 인쇄 된 compilation 속성 입 니 다.
여기
thisCompilation (childCompiler)
에 대해 이 글 을 참고 하여 요약 하면:하위 컴 파 일 러 는 완전한 모듈 해석 과 chunk 생 성 단 계 를 가지 고 있 지만, "make", "compile", "emit", "after - emit", "invalid", "done", "this - compilation" 와 같은 이벤트 점 이 적 습 니 다.즉, 우 리 는 하위 컴 파일 러 를 이용 하여 독립 적 으로 (부모 컴 파일 러) 핵심 구축 절 차 를 마치 고 필요 한 모듈 이나 chunk 를 추가 로 생 성 할 수 있다.
make(compiler)
위의 copile 방법 을 통 해 알 수 있 듯 이 Compilation 를 예화 하면
make
됩 니 다.make 를 실행 할 때 웹 팩 이 앞 에 있 기 때문에 SingleEntryPlugin MultleEntryPlugin
, SingleEntry Plugin 은 apply 방법 에 make 이 벤트 를 등록 하 였 습 니 다.apply(compiler) {
compiler.hooks.compilation.tap(
"SingleEntryPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
SingleEntryDependency,
normalModuleFactory // , compilation dependencyFactories
);
}
);
compiler.hooks.make.tapAsync(
"SingleEntryPlugin",
(compilation, callback) => {
const { entry, name, context } = this;
const dep = SingleEntryPlugin.createDependency(entry, name);
// addEntry
compilation.addEntry(context, dep, name, callback);
}
);
}
사실 addEntry 호출 은
Comilation._addModuleChain
, acquire 함수 가 간단 합 니 다. 주로 module 을 처리 할 때 작업 이 너무 많 으 면 moduleFactory. create 를 대기 열 에 저장 합 니 다._addModuleChain(context, dependency, onModule, callback) {
......
// Factory
const Dep = /** @type {DepConstructor} */ (dependency.constructor);
const moduleFactory = this.dependencyFactories.get(Dep);
......
this.semaphore.acquire(() => {
moduleFactory.create(
{
contextInfo: {
issuer: "",
compiler: this.compiler.name
},
context: context,
dependencies: [dependency]
},
(err, module) => {
......
}
);
});
}
moduleFactory. create 는
module
buildModule(compilation) 리 셋 함수 주요 상 집행
buildModule
방법this.buildModule(module, false, null, null, err => {
......
afterBuild();
});
buildModule(module, optional, origin, dependencies, thisCallback) {
//
let callbackList = this._buildingModules.get(module);
if (callbackList) {
callbackList.push(thisCallback);
return;
}
this._buildingModules.set(module, (callbackList = [thisCallback]));
const callback = err => {
this._buildingModules.delete(module);
for (const cb of callbackList) {
cb(err);
}
};
// buildModule
this.hooks.buildModule.call(module);
module.build(
this.options,
this,
this.resolverFactory.get("normal", module.resolveOptions),
this.inputFileSystem,
error => {
......
}
);
}
build doBuild,doBuild runLoaders loader webpack js , doBuild parse , Dependency
return this.doBuild(options, compilation, resolver, fs, err => {
// createLoaderContext normal-module-loader
const loaderContext = this.createLoaderContext(
resolver,
options,
compilation,
fs
);
.....
const handleParseResult = result => {
this._lastSuccessfulBuildMeta = this.buildMeta;
this._initBuildHash(compilation);
return callback();
};
try {
// parser.parse
const result = this.parser.parse(
this._ast || this._source.source(),
{
current: this,
module: this,
compilation: compilation,
options: options
},
(err, result) => {
if (err) {
handleParseError(err);
} else {
handleParseResult(result);
}
}
);
if (result !== undefined) {
// parse is sync
handleParseResult(result);
}
} catch (e) {
handleParseError(e);
}
});
ast
。 succeedModule(compilation)
마지막 으로 module. build 의 리 셋 함 수 를 실 행 했 습 니 다. 트리거
succeedModule
를 실 행 했 고 Compilation. buildModule 함수 의 리 셋 함수 로 돌 아 왔 습 니 다.module.build(
this.options,
this,
this.resolverFactory.get("normal", module.resolveOptions),
this.inputFileSystem,
error => {
......
succeedModule
this.hooks.succeedModule.call(module);
return callback();
}
);
this.buildModule(module, false, null, null, err => {
......
// afterBuild
afterBuild();
});
현재 모듈 에 여러 개의 의존 모듈 이 존재 할 수 있 습 니 다.현재 모듈 은 모듈 에 의존 하 는 배열 을 열 고
AST , require() addDependency() 。 ,webpack processModuleDependencies module
다음 에 이전의 구축 절 차 를 반복 합 니 다. Compilation.prototype.addModuleDependencies = function(module, dependencies, bail, cacheGroup, recursive, callback) {
// (dependencies)
var factories = [];
for (var i = 0; i < dependencies.length; i++) {
var factory = _this.dependencyFactories.get(dependencies[i][0].constructor);
factories[i] = [factory, dependencies[i]];
}
...
//
}
마지막 으로 모든 모듈 은
Compilation modules
에 넣 습 니 다. 다음 과 같 습 니 다.요약:
module 은 webpack 이 구축 한 핵심 실체 이자 모든 module 의 부모 클래스 입 니 다. 몇 가지 서로 다른 하위 클래스 가 있 습 니 다. Normal Module, MultiModule, ContextModule, Delegated Module 등 의존 대상 입 니 다.(Dependency, 모듈 인 스 턴 스 의 의존 대상 으로 해석 되 지 않 았 습 니 다. 예 를 들 어 웹 팩 을 실행 할 때 들 어 오 는 입구 모듈 이나 모듈 이 의존 하 는 다른 모듈 은 Dependency 대상 이 됩 니 다.) 해당 하 는 공장 대상 (Factory) 을 통 해 생 성 된 모듈 인 스 턴 스 (Module) 를 생 성 할 수 있 습 니 다.
seal(compilation)
module , Compilation.seal, seal, chunk
모든 chunks 가 생 성 된 후에 webpack 은 chunks 와 modules 에 대해 최적화 와 관련 된 조작 을 할 것 이다. 예 를 들 어 분배 id, 정렬 등 이 있 고 일련의 관련 사건 점 을 촉발 할 것 이다.seal(callback) {
// seal
this.hooks.seal.call();
//
......
this.hooks.afterOptimizeDependencies.call(this.modules);
this.hooks.beforeChunks.call();
// chunk
for (const preparedEntrypoint of this._preparedEntrypoints) {
const module = preparedEntrypoint.module;
const name = preparedEntrypoint.name;
// Module chunk, chunk 。
const chunk = this.addChunk(name);
const entrypoint = new Entrypoint(name);
entrypoint.setRuntimeChunk(chunk);
entrypoint.addOrigin(null, name, preparedEntrypoint.request);
this.namedChunkGroups.set(name, entrypoint);
this.entrypoints.set(name, entrypoint);
this.chunkGroups.push(entrypoint);
GraphHelpers.connectChunkGroupAndChunk(entrypoint, chunk);
GraphHelpers.connectChunkAndModule(chunk, module);
chunk.entryModule = module;
chunk.name = name;
this.assignDepth(module);
}
this.processDependenciesBlocksForChunkGroups(this.chunkGroups.slice());
this.sortModules(this.modules);
this.hooks.afterChunks.call(this.chunks);
this.hooks.optimize.call();
......
this.hooks.afterOptimizeModules.call(this.modules);
......
this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => {
......
this.hooks.beforeChunkAssets.call();
this.createChunkAssets(); // Assets
this.hooks.additionalAssets.callAsync(...)
});
}
각 chunk 의 생 성 은 포함 할 modules 를 찾 는 것 입 니 다. chunk 의 생 성 알고리즘 을 대충 설명 합 니 다.
1. webpack 은 entry 에 대응 하 는 module 을 먼저 새로운 chunk 로 생 성 합 니 다.
2. module 의 의존 목록 을 옮 겨 다 니 며 의존 하 는 module 도 chunk 에 추가
3. module 에 의존 하 는 모듈 이 동적 으로 도 입 된 모듈 이 라면 이 module 에 따라 새로운 chunk 를 만 들 고 의존 을 계속 할 것 입 니 다.
4. 위의 과정 을 반복 하여 모든 chunks 를 얻 을 때 까지
chunk 속성 도
beforeChunkAssets && additionalChunkAssets(Compilation)
이 두 사건 점 을 촉발 하 는 중간 에
Compilation.createCHunkAssets assets
,createChunkAssets() {
......
// chunk
for (let i = 0; i < this.chunks.length; i++) {
const chunk = this.chunks[i];
chunk.files = [];
let source;
let file;
let filenameTemplate;
try {
// Template
const template = chunk.hasRuntime()
? this.mainTemplate
: this.chunkTemplate;
const manifest = template.getRenderManifest({
chunk,
hash: this.hash,
fullHash: this.fullHash,
outputOptions,
moduleTemplates: this.moduleTemplates,
dependencyTemplates: this.dependencyTemplates
}); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
for (const fileManifest of manifest) {
.....
}
.....
// assets
this.assets[file] = source;
chunk.files.push(file);
this.hooks.chunkAsset.call(chunk, file);
alreadyWrittenFiles.set(file, {
hash: usedHash,
source,
chunk
});
}
} catch (err) {
......
}
}
}
createChunkAssets 는 파일 이름과 해당 하 는 파일 내용 을 생 성하 여 넣 습 니 다
Compilation.assets
. 여기 에는 네 개의 Template 하위 클래스 가 있 습 니 다. 각각 MainTemplate.js , ChunkTemplate.js ,ModuleTemplate.js , HotUpdateChunkTemplate.js
MainTemplate.prototype.requireFn = "__webpack_require__";
MainTemplate.prototype.render = function(hash, chunk, moduleTemplate, dependencyTemplates) {
var buf = [];
// module moduleId, 。
buf.push("function " + this.requireFn + "(moduleId) {");
buf.push(this.indent(this.applyPluginsWaterfall("require", "", chunk, hash)));
buf.push("}");
buf.push("");
... //
};
마지막 으로 Compilation. assets 대상 보기
done(Compiler)
마지막 으로 웹 팩 은 Compiler 의 emitAssets () 를 호출 하여 output 의 설정 항목 에 따라 파일 을 해당 하 는 path 에 출력 하여 웹 팩 의 전체 포장 과정 을 끝 냅 니 다. 결 과 를 처리 하려 면 emit 트리거 후 사용자 정의 플러그 인 을 확장 해 야 합 니 다.
총결산
웹 팩 의 내부 핵심 은 compilation copilemodulechunk 등 대상 이나 인 스 턴 스 입 니 다. 이 글 을 쓰 는 것 도 자신의 생각 을 정리 하 는 데 도움 이 되 고 배 움 의 바다 가 끝 이 없습니다 ~ ~
인용 하 다.
웹 팩 돌리 기 (1): 웹 팩 의 기본 구조 와 구축 프로 세 스 돌리 기 웹 팩 (2): 웹 팩 의 핵심 대상 웹 팩 의 프로 세 스 편
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
nginx 전단 상용 설정window 중국어 파일 이름 이 Liux 에 올 라 온 nginx 서비스 난 장 판 을 해결 합 니 다. 2: NGINX 프로필 의 기본 인 코딩 은 utf - 8 로 설정 되 어 있 습 니 다. 3. UTF - ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.