Принципы и практика Webpack (1): процесс упаковки

внешний интерфейс Webpack

слова, написанные впереди

чтениеwebpack4.xВ процессе исходного кода я ссылаюсь на книгу «Углубленный веб-пакет» и множество статей великих богов, в сочетании с моим собственным опытом, резюме выглядит следующим образом.

Обзор

webpackКак и производственная линия, есть серия шагов обработки до того, как исходный файл может быть преобразован в выходной сигнал. Ответственность за каждый процесс обработки на этой производственной линии является одинокой, и существует взаимосвязь зависимости между несколькими процессами. Только после завершения текущей обработки его можно передавать следующему процессу обработки. Плагин - это как функция, вставленная в производственную линию, ресурсы обработки на производственной линии в определенное время.webpackпройти черезTapableорганизовать эту сложную производственную линию.webpackСобытия транслируются во время рабочего процесса, и плагину нужно только прослушивать те события, о которых он заботится, а затем его можно добавить в производственную линию, чтобы изменить работу производственной линии.webpackМеханизм потока событий обеспечивает упорядоченность плагинов, делая всю систему очень масштабируемой. --Wu Haolin "Объяснение веб-пакета простыми словами"

основные концепции

entry,loader,plugin,module,chunkДокументов и предисловий к ним очень много, не буду вдаваться в подробности, если есть сомнения, переходите к документу.

процесс сборки

webpackТекущий процесс является последовательным процессом, и следующие процессы будут выполняться последовательно от начала до конца:

  1. Параметры инициализации: из конфигурационного файла иShellПрочитайте и объедините параметры в операторе, чтобы получить окончательный параметр;
  2. Начать компиляцию: инициализировать с параметрами, полученными на предыдущем шагеCompilerобъекта, загружает все настроенные плагины, выполняетrunМетод начинает выполнять компиляцию;
  3. Определите запись: Найдите все файлы записей в соответствии с записью в конфигурации.
  4. Скомпилируйте модуль: начиная с файла записи, вызовите все настроенные загрузчики для преобразования модуля, затем найдите модули, от которых зависит модуль, а затем повторите этот шаг, пока все файлы, зависящие от записи, не будут обработаны на этом шаге;
  5. Завершение компиляции модуля: После перевода всех модулей с помощью Loader на шаге 4 получается финальное содержимое каждого модуля после перевода и зависимости между ними;
  6. Выходные ресурсы: в соответствии с зависимостями между записью и модулем соберите их в несколько модулей один за другим.Chunk, затем поставьте каждыйChunkПреобразуйте его в отдельный файл и добавьте в список вывода.Этот шаг — последний шанс изменить содержимое вывода;
  7. Вывод завершен: после определения содержимого вывода определите путь вывода и имя файла в соответствии с конфигурацией и запишите содержимое файла в файловую систему. В ходе вышеуказанного процессаwebpackКонкретное событие будет транслироваться в определенный момент времени, плагин будет выполнять определенную логику после прослушивания интересующего события, и плагин может вызыватьwebpackПредоставленные изменения APIwebpackрезультат операции.

Сравните два основных объекта в webpack

  • CompileОбъект: Отвечает за мониторинг файлов и инициацию компиляции.CompilerПример содержит полныйwebpackКонфигурация, только одна глобальноCompilerпример.
  • compilationОбъект: когдаwebpackПри работе в режиме разработки всякий раз, когда обнаруживается изменение файла, создается новыйCompilationбудет создан. ОдинCompilationОбъект содержит текущие ресурсы модуля, скомпилированные ресурсы, измененные файлы и т. д.CompilationОбъект также предоставляет множество обратных вызовов событий для расширений плагинов.
  • Оба объекта наследуются отTapable. кCompileНапример
const {
	Tapable,
	SyncHook,
	SyncBailHook,
	AsyncParallelHook,
	AsyncSeriesHook
} = require("tapable");

class Compiler extends Tapable {
	constructor(context) {
		super();
		this.hooks = {
			/** @type {SyncBailHook<Compilation>} */
			//所有需要输出的文件已经生成好,询问插件哪些文件需要输出,哪些不需要。
			shouldEmit: new SyncBailHook(["compilation"]),
			/** @type {AsyncSeriesHook<Stats>} */
			//成功完成一次完成的编译和输出流程。
			done: new AsyncSeriesHook(["stats"]),
			/** @type {AsyncSeriesHook<>} */
			additionalPass: new AsyncSeriesHook([]),
			/** @type {AsyncSeriesHook<Compiler>} */
			beforeRun: new AsyncSeriesHook(["compiler"]),
			/** @type {AsyncSeriesHook<Compiler>} */
			//启动一次新的编译
			run: new AsyncSeriesHook(["compiler"]),
			/** @type {AsyncSeriesHook<Compilation>} */
			// 确定好要输出哪些文件后,执行文件输出,可以在这里获取和修改输出内容。
			emit: new AsyncSeriesHook(["compilation"]),
			/** @type {AsyncSeriesHook<Compilation>} */
			// 输出完毕
			afterEmit: new AsyncSeriesHook(["compilation"]),
                         // 以上几个事件(除了run,beforerun为编译阶段)其余为输出阶段的事件
			/** @type {SyncHook<Compilation, CompilationParams>} */
			// compilation 创建之前挂载插件的过程
			thisCompilation: new SyncHook(["compilation", "params"]),
			/** @type {SyncHook<Compilation, CompilationParams>} */
			// 创建compilation对象
			compilation: new SyncHook(["compilation", "params"]),
			/** @type {SyncHook<NormalModuleFactory>} */
		    // 初始化阶段:初始化compilation参数
			normalModuleFactory: new SyncHook(["normalModuleFactory"]),
			/** @type {SyncHook<ContextModuleFactory>}  */
		    // 初始化阶段:初始化compilation参数
			contextModuleFactory: new SyncHook(["contextModulefactory"]),

			/** @type {AsyncSeriesHook<CompilationParams>} */
			beforeCompile: new AsyncSeriesHook(["params"]),
			/** @type {SyncHook<CompilationParams>} */
			// 该事件是为了告诉插件一次新的编译将要启动,同时会给插件带上 compiler 对象
			compile: new SyncHook(["params"]),
			/** @type {AsyncParallelHook<Compilation>} */
			//一个新的 Compilation 创建完毕,即将从 Entry 开始读取文件,根据文件类型和配置的 Loader 对文件进行编译,编译完后再找出该文件依赖的文件,递归的编译和解析。
			make: new AsyncParallelHook(["compilation"]),
			/** @type {AsyncSeriesHook<Compilation>} */
		    // 一次Compilation执行完成
			afterCompile: new AsyncSeriesHook(["compilation"]),

			/** @type {AsyncSeriesHook<Compiler>} */
			//监听模式下启动编译(常用于开发阶段)
			watchRun: new AsyncSeriesHook(["compiler"]),
			/** @type {SyncHook<Error>} */
			failed: new SyncHook(["error"]),
			/** @type {SyncHook<string, string>} */
			invalid: new SyncHook(["filename", "changeTime"]),
			/** @type {SyncHook} */
			// 如名字所述
			watchClose: new SyncHook([]),

			// TODO the following hooks are weirdly located here
			// TODO move them for webpack 5
			/** @type {SyncHook} */
			//初始化阶段:开始应用 Node.js 风格的文件系统到compiler 对象,以方便后续的文件寻找和读取。
			environment: new SyncHook([]),
			/** @type {SyncHook} */
			// 参照上文
			afterEnvironment: new SyncHook([]),
			/** @type {SyncHook<Compiler>} */
			// 调用完内置插件以及配置引入插件的apply方法,完成了事件订阅
			afterPlugins: new SyncHook(["compiler"]),
			/** @type {SyncHook<Compiler>} */
			afterResolvers: new SyncHook(["compiler"]),
			/** @type {SyncBailHook<string, EntryOptions>} */
			// 读取配置的 Entrys,为每个 Entry 实例化一个对应的 EntryPlugin,为后面该 Entry 的递归解析工作做准备。
			entryOption: new SyncBailHook(["context", "entry"])
		};

существуетwebpackВо время выполнения последовательно транслируется серия событий --this.hooksСерия событий в (аналогично жизненному циклу в нашем общем фреймворке), и в каком порядке подписчики этих событий должны быть организованы, выполнены и переданы параметры... ЭтоTapableсписок задач.
оTapableЛучше порекомендовать всем (но объем чтения не сильно похож на 2333)Популярная наука

Детали процесса

Для получения подробной информации о процессе, пожалуйста, обратитесь к тому, на что я ссылаюсь.CompileКомментарии в объектах, одно замечание, авторhooksПорядок записи не является порядком вызова. Есть несколько ситуаций, которые не аннотированы:

  1. Менее важные или известные по названию и контексту события
  2. В основном потому, что я еще не знаю (2333, я добавлю новое понимание позже, побег...)
  3. Конечно, самые важные события в основном освещаются Вот картинка из справочной статьи.

Введение в процесс компиляции

compilationНа самом деле, позвонив в соответствующийloaderГенерация файла процессаchunksи к этимchunksпроцесс оптимизации. Несколько ключевых событий (объект компиляции в this.hooks):

  1. buildModuleиспользуйте соответствующийLoaderпреобразовать модуль;
  2. normalModuleLoaderС использованиемLoaderПосле преобразования модуля используйтеacornПроанализируйте преобразованный контент и выведите соответствующее абстрактное синтаксическое дерево (AST) для удобства.webpackАнализ кода позже.
  3. sealВсе модули и зависимые от них модули проходят черезLoaderПосле завершения преобразования начните генерировать на основе зависимостейChunk.

Наконец, изображение взято из справочной статьи, чтобы иметь более четкое представление обо всем процессе.

image

Ссылаться на

  1. Таобао Fed.org/blog/2016/0…
  2. IM Web.IO/topic/5 Wipe…
  3. "Углубленный веб-пакет"

Широкая реклама

Эта статья была опубликована вЕженедельный выпуск Mint Front End, Добро пожаловать в Watch & Star ★, пожалуйста, указывайте источник при перепечатке.

Добро пожаловать, чтобы обсудить, поставить лайк и перейти 。◕‿◕。~