Webpack, как «осадный лев» внешнего интерфейса, слишком хорошо знаком. Он может упаковывать все ресурсы (включая JS, TS, JSX, изображения, шрифты, CSS и т. д.) и помещать их в зависимости. . , что позволяет вам ссылаться на зависимости для использования ресурсов в соответствии с вашими потребностями. Webpack проделал отличную работу по трансляции нескольких файловых ресурсов во внешнем интерфейсе и анализу сложных зависимостей модулей. Мы также можем настраивать загрузчик и свободно загружать собственные ресурсы. Так как же Webpack реализует упаковку? Приходите и посмотрите сегодня.
1. Что требуется?
Когда дело доходит до require, первое, что приходит на ум, — это import — это синтаксический стандарт es6.
require — это вызов во время выполнения, поэтому теоретически require можно использовать в любом месте кода
— import — это время компиляции. call, поэтому его необходимо поместить в начало файла ;
Когда мы используем Webpack для компиляции, мы будем использовать Babel для перевода импорта в require. В CommonJS есть глобальный метод require(), который используется для загрузки модулей. AMD и CMD также используют метод require для ссылки.
Например:
var add = require('./a.js');Проще говоря,
add(1,2)
на самом деле является функцией, а указанный ./a.js
— это просто параметр функции.
2. Что такое экспорт?
Здесь мы можем думать об экспорте как об объекте. Вы можете увидеть конкретное использование экспорта MDN.
Давайте сначала посмотрим на структуру кода после упаковки. Мы можем обнаружить, что после упаковки появится запрос и экспорт.
Не все браузеры могут выполнять экспорт require. Вы должны реализовать require и экспорт самостоятельно, чтобы обеспечить нормальную работу кода. Упакованный код представляет собой самовыполняющуюся функцию. Параметры содержат информацию о зависимостях и код файла. Тело исполняемой функции выполняет код посредством eval.
Общий чертеж конструкции выглядит следующим образом:
Шаг 1. Напишите наш файл конфигурации.
Файл конфигурации настраивает упакованный входной и выходной выходные данные для подготовки к последующим сгенерированным файлам.
константный путь = требуется («путь»); модуль.экспорт = { запись: "./src/index.js", выход: { path: path.resolve(__dirname, "./dist"),//Вывод адреса файла после упаковки требует абсолютного пути, поэтому требуется указать путь имя файла: "main.js" }, режим: «разработка»
Шаг 2. Общая идея анализа модуля
: вкратце, это использование файла fs для чтения входного файла и получения пути к импортируемому файлу через AST. Если зависимый файл все еще. имеет зависимости, продолжайте рекурсию до тех пор, пока анализ зависимостей не станет ясным и не будет сохранен на карте.
Подробная разбивка : некоторые люди могут задаться вопросом, почему используется AST, поскольку AST изначально содержит эту функцию. Его ImportDeclaration может помочь нам быстро отфильтровать синтаксис импорта. В конце концов, файл представляет собой файл. строка после прочтения. Путем записи. Удивительное регулярное выражение полезно для получения путей зависимости файлов, но оно недостаточно элегантно.
файл index.js
import { str } from "./a.js"; console.log(`${str} Webpack`)
импорт
файла a.js
{ b} из "./b.js"Export const str = "hello"
файл b.js
Export const b="bbb"
анализ модуля Webpack: используйте @babel/parser AST для преобразования строки, считанной из файла, в дерево AST и @babel/traverse для синтаксис Анализируйте и используйте ImportDeclaration для фильтрации импорта и поиска зависимостей файлов.
const content = fs.readFileSync(entryFile, "utf-8"); const ast = parser.parse(content, {sourceType: "module" }); const имя_каталога = path.dirname(entryFile); константные иждивенцы = {}; траверс(ast, { ImportDeclaration({узел}) { //Отфильтровываем импорт const newPathName = "./" + path.join(dirname, node.source.value); зависимые [узел.источник.значение] = newPathName; } }) const {код} = TransformFromAst(ast, null, { пресеты: ["@babel/preset-env"] }) возвращаться { записьФайл, иждивенцы, код }
Результаты следующие:
Используйте рекурсию или цикл для импорта файлов один за другим для анализа зависимостей. Обратите внимание, что мы используем цикл for для анализа всех зависимостей. Причина, по которой циклы могут анализировать все зависимости, заключается в том, что длина модулей изменяется. новые зависимости, Modules.length изменится.
for (пусть я = 0; я < this.modules.length; i++) { const item = this.modules[i]; const {зависимые} = элемент; если (иждивенцы) { for (пусть j в зависимых) { this.modules.push(this.parse(dependents[j])); } } }
Шаг 3. Напишите функцию WebpackBootstrap + сгенерируйте выходной файл.
Напишите функцию WebpackBootstrap . Первое, что нам нужно сделать, — это функция WebpackBootstrap. После компиляции импорт нашего исходного кода будет проанализирован в require. не распознает require, то мы должны сначала объявить его. В конце концов, require — это метод. При написании функций также необходимо уделять внимание изоляции области видимости, чтобы предотвратить загрязнение переменных. Нам также необходимо объявить экспорт в нашем коде, чтобы гарантировать, что экспорт уже существует на момент выполнения кода.
Создайте выходной файл : мы уже записали адрес сгенерированного файла в файл конфигурации, а затем используем fs.writeFileSync, чтобы записать его в выходную папку.
файл(код) { const filePath = path.join(this.output.path, this.output.filename) const newCode = JSON.stringify(код); // Генерируем содержимое файла пакета const Bundle = `(function(modules){ функция требует (модуль) { функция pathRequire(relativePath){ return require(modules[module].dependents[relativePath]) } константный экспорт = {}; (функция(требуется,экспорт,код){ оценка (код) })(pathRequire,exports,modules[module].code); возврат экспорта } require('${this.entry}') })(${newCode})`; // ВебпакБустрап // Генерируем файл. Поместите его в каталог dist fs.writeFileSync(filePath,bundle,'utf-8') }
Шаг 4. Проанализируйте последовательность выполнения
Мы можем запустить упакованный результат в консоли браузера. Если все работает нормально, должен быть напечатан hello Webpack.
Благодаря приведенному выше анализу мы должны иметь общее представление об общем процессе работы Webpack. Использование AST для анализа кода — это всего лишь способ демонстрации, а не реальная реализация Webpack, имеющая собственный метод анализа AST. Экосистема Webpack очень полна. Заинтересованные дети могут рассмотреть следующие три вопроса: