Давайте узнаем, как работают пакеты модулей, а затем напишем их сами

1656575171 davajte uznaem kak rabotayut pakety modulej a zatem napishem ih

от Адама Келли

1*oxAMv8OXwMUxyk8c9ZnPUA

Привет! Добро пожаловать, рады, что вы здесь! Сегодня мы собираемся построить очень простой пакет JavaScript.

Прежде чем мы начнем, я хочу выразить несколько благодарностей. Эта статья в значительной степени опирается на следующие ресурсы:

Ладно, давайте начнем с того, что на самом деле такое пакет модулей.

Что такое пакетный модуль?

Комплектовщик модулей — это инструмент, который берет фрагменты JavaScript и их зависимости и объединяет их в один файл обычно для использования в браузере. Возможно, вы использовали такие инструменты как Browserify, Webpack, Rollup или один из многих других.

Обычно он начинается с входного файла, а оттуда он объединяет весь код, необходимый для этого файла входа.

0*WwDTeWwIRxVPg5jK

Существует два основных этапа комплектования:

  1. Решение зависимости
  2. Упаковка

Начиная с точки входа (например app.js выше) целью решения зависимостей является поиск всех зависимостей вашего кода (другие части кода, которые ему нужны для функционирования) и построение графика (так называемый график зависимостей).

Как только это будет сделано, вы можете запаковать или превратить свой график зависимостей в один файл, который может использовать приложение.

Давайте начнем наш код с некоторых импортов (я расскажу о причине позже).

Решение зависимости

Первое, что нам нужно сделать это придумать, как мы хотим представить модуль на этапе решения зависимостей.

Представление модуля

Нам понадобятся четыре вещи:

  • Имя и идентификатор файла
  • Откуда взялся файл (в файловой системе)
  • Код в файле
  • Какие зависимости нужны этому файлу

Структура графика создается путём рекурсивной проверки зависимостей в каждом файле.

В JavaScript простым способом представить такой набор данных был бы объект.

Глядя на createModuleObject функции выше, примечательной частью является вызов вызванной функции detective.

Детектив – это библиотека, которая может найти все вызовы require() независимо от того, насколько глубоко вложеныи его использование означает, что мы можем избежать собственного обхода AST!

Одна вещь, которую следует отметить (и это то же самое во многих пакетах модулей), это то, что если вы попытаетесь сделать что-то странное, например:

const libName="lodash"const lib = require(libName)

Он не сможет его найти (потому что это означало бы исполнение кода).

Итак, что дает запуск этой функции с пути к модулю?

0*5gAnBAhQ3_4cn5oq

Что дальше? Решение зависимости.

Ладно, еще не совсем. Сначала я хочу поговорить о вещи, которая называется картой модулей.

Карта модуля

При импорте модулей в Node можно выполнять относительный импорт, например require('./utils'). Итак, когда ваш код вызывает это, откуда пакетник знает, что правильно ./utils файл, когда все упаковано?

Это проблема, которую решает карта модуля.

Наш модульный объект имеет уникальность id ключ, который будет нашим «источником истины». Поэтому, когда мы производим решение зависимостей, для каждого модуля мы будем сохранять список имен того, что нужно, вместе с их идентификатором. Таким образом, мы можем получить правильный модуль при выполнении.

Это также означает, что мы можем хранить все модули в неуложенном объекте, используя id в качестве ключа.

0*1LBQSrDoGoQrbE3t

Решение зависимости

Ладно, значит, в ней происходит немало getModules функция. Его главная цель – начать с модуля root/entry, а также рекурсивно искать и решать зависимости.

Что я имею в виду под «разрешение зависимостей»? В Node есть вещь, которая называется require.resolve, и именно так Node определяет, где находится нужный вам файл. Это потому, что мы можем импортировать относительно или из a node_modules папку.

К нашему счастью, есть модуль npm с именем resolve которая реализует нам этот алгоритм. Нам просто нужно передать аргументы зависимости и базового URL-адреса, и это сделает всю трудную работу за нас.

Нам нужно выполнить решение для каждой зависимости каждого модуля в проекте.

Мы также создаем карту модуля с названием map что я упоминал раньше.

В конце функции нам остается массив с именем modules который будет содержать объекты модуля для каждого модуля/зависимости в нашем проекте.

Теперь, когда у нас это есть, мы можем перейти к последнему шагу: упаковка!

Упаковка

В браузере нет такой вещи, как модули (типа). Но это значит, что нет функции require, и нет module.exports. Поэтому, несмотря на то, что у нас есть все наши зависимости, мы не можем использовать их в качестве модулей.

Заводская функция модуля

Введите заводскую функцию.

Фабричная функция – это функция (это не конструктор), возвращающая объект. Это шаблон по объектно-ориентированному программированию, и одно из его применений — инкапсуляция и инъекция зависимостей.

Звук хороший?

Используя заводскую функцию, мы оба можем ввести нашу собственную require функция и module.exports объект, который можно использовать в нашем пакетном коде и предоставить модулю собственную область.

Упаковка

Ниже приведена функция pack, используемая для упаковки.

Большинство из них — это шаблонные литералы JavaScript, поэтому давайте обсудим, что он делает.

Первое – это modulesSource. Здесь мы проходим по каждому из модулей и превращаем их в строчку источников.

Итак, как выглядит выход для объекта модуля?

0*dJtsT5gsI2_heqtL

Теперь это немного тяжело читать, но вы можете увидеть, что источник инкапсулирован. Мы предоставляем modules и require использование заводской функции, как я упоминал ранее.

Мы также включаем карту модулей, созданную при решении зависимостей.

Далее в функции мы объединяем все это для создания большого объекта всех зависимостей.

Следующая строка кода – это IIFE, что означает, что при запуске этого кода в браузере (или в любом другом месте) функция запускается немедленно. IIFE – это еще один шаблон для инкапсуляции области видимости, который используется здесь, чтобы мы не загрязняли глобальную область require и модули

Вы можете видеть, что мы определяем две нужные функции, require и localRequire.

Require принимает идентификатор объекта модуля, но, конечно, исходный код не записывается с помощью идентификаторов. Вместо этого мы используем другую функцию localRequire чтобы принять любые аргументы, требующие модули и превратить их в правильный идентификатор. Это использует эти карты модулей.

После этого мы определяем а module object что модуль может заполнить, и передача обеих функций в фабрику, после чего мы возвращаемся module.exports.

Наконец-то мы звоним require(0) чтобы потребовать модуль с идентификатором 0, являющимся нашим файлом входа.

И все это! Наш пакет модулей готов на 100%!

Поздравляю! ?

Теперь у нас есть рабочий пакет модулей.

Это, вероятно, не следует использовать в производстве, потому что в нем отсутствует масса функций (например, управление круговыми зависимостями, убеждение, что каждый файл анализируется только один раз, es-модули и т.д.), но, надеемся, это дало вам хорошее представление о то, как пакетники модулей действительно работают.

На самом деле это работает примерно в 60 строках, если удалить весь исходный код.

Спасибо, что прочли, и я надеюсь, что вам понравилось ознакомиться с работой нашего простого пакета модулей. Если да, то обязательно похлопайте? и поделитесь.

Первоначально эта статья была опубликована в моем блоге.
Проверьте источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *