Как разделить код js на несколько файлов

Обновлено: 02.07.2024

На этом этапе мы продолжим рефакторинг нашего приложения, мы будем использовать модули TypeScript для организации нашего проекта и обеспечения его поддержки.

Одно важное замечание о терминологии из официальной документации TypeScript: > Важно отметить, что в TypeScript 1.5 номенклатура изменилась. «Внутренние модули» теперь являются «пространствами имен». «Внешние модули» теперь просто «модули», в соответствии с терминологией ECMAScript 2015 (а именно, что модуль X < эквивалентен предпочитаемому теперь пространству имен X <).

Также обратите внимание на примечание из TypeScript Deep Dive: > Для большинства проектов мы рекомендуем использовать внешние модули и пространство имен для быстрых демонстраций и переноса старого кода JavaScript.

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

Я. Создание папок, установка jQuery и изменение tsconfig.json

Начнем с создания двух папок: - src - будут содержать исходные файлы на TypeScript; - dist - сюда будут помещаться скомпилированные файлы JavaScript.

Также вместо CDN мы установим jQuery как пакет npm. Эта консольная команда установит jQuery 2.x в папку node_modules:

Далее мы внесем изменения в файл tsconfig.json. Мы удалим раздел files и заменим его на include , также добавим две опции для включения jQuery в проект - baseUrl и paths наконец добавим опцию outDir и укажем куда выводить скомпилированные файлы.

Наш файл tsconfig.json будет выглядеть следующим образом:

II. Разделить index.ts на несколько файлов

Затем мы разделим наш index.ts на несколько файлов и поместим их в папку src.

Сначала извлеките функцию DOMElement и поместите ее в файл dom-element.ts :

Как видите, мы добавили экспорт по умолчанию перед именем функции, чтобы сообщить компилятору, что мы экспортируем его как компонент модуля по умолчанию.

Далее мы извлечем классы Task и HighPriorityTask в файл tasks.ts :

Обратите внимание, что мы добавили оператор импорта вверху файла, чтобы указать, что мы импортируем DOMElement .

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

Даже если бы все модули были слабо связаны и управлялись событиями, я все еще не знаю, как разделить это на несколько файлов, учитывая, что каждый модуль зависит от общих функций/переменных. У кого-нибудь есть предложения?

@nnnnnn Спасибо за статью. Идеально. Вы должны отправить ответ со ссылкой, чтобы я мог его принять.

5 ответов 5

Основная идея заключается в том, что отдельные файлы JS добавляются в один и тот же модуль с помощью следующего кода:

Где в каждом файле JS, если МОДУЛЬ уже определен (из другого файла JS), вы добавляете к нему, в противном случае вы создаете его. Вы можете настроить его так, чтобы (в основном) не имело значения, в каком порядке включены различные файлы.

В статье подробно описаны несколько вариантов, и, конечно, вы, вероятно, придумаете свои собственные настройки.

не для того, чтобы добавить путаницы, но исходя из фона C++, я попытался создать нечто, напоминающее что-то вроде пространства имен C++, как описано ниже. это работает, но я хотел бы знать, является ли это приемлемым шаблоном для ОП?

Зачем объявлять namespacename пустой функцией, которая никогда не вызывается? Вы можете объявить его как пустой объект с помощью var namespacename = <>, и остальной код не нужно будет менять.

Если вы пришли с C++, вы должны знать, что в итоге у вас будут десятки, а иногда и сотни или тысячи файлов. Вы не хотите запрашивать столько js-скриптов с сервера. Вместо этого лучше запаковать все файлы в 1 скрипт (например, с помощью webpack)

Да, я разбирался, но пока не знаю, как решить проблему с общими функциями.

Вы создаете файл для каждого класса (отсюда и тег ООП), а запускаемый скрипт просто извлекает эти классы для использования. Если вы покажете мне код, я помогу вам распределить его по файлам.

@fabianhjr Код структурирован точно так же, как образец, включенный в вопрос. Как бы вы разделили этот образец? Спасибо.

@fabianhjr Угадывать не нужно. Вопрос в структуре, как и в конкретных проблемах, которые у меня возникают с этой структурой. Мой вопрос просит предложений о том, как исправить эти проблемы. Если у вас нет другого предложения, кроме как назвать его "проблемой дизайна", зачем вообще отвечать?

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

Затем вы можете определить их в любом файле и использовать в любом файле.

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

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

Для проектов с большим объемом JavaScript это может существенно повысить производительность.

Он был популяризирован сообществом React, где такие инструменты, как Webpack, используются для управления тем, какие пакеты и когда загружаются. Но установка Webpack может быть… сложной.

Сегодня я хотел рассказать о простом, ванильном JS-подходе к разделению кода.

Секретный соус: loadJS()

Он весит всего 500 байт (полкилобайта!) в минимизированном виде (даже меньше после сжатия) и позволяет асинхронно загружать файлы JavaScript.

Вот как работает этот метод.

  1. Используйте querySelector() и оператор if, чтобы проверить, существует ли условие DOM, необходимое для загрузки файла.
  2. Если это правда, используйте loadJS() для загрузки.

Пример разделения кода

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

Вы бы сделали это:

Что нужно разделить

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

  1. Поместите все общие компоненты JavaScript, загруженные на всех или большинстве страниц, в один файл main.js.
  2. Сохраняйте все загруженное на одной или нескольких страницах как отдельный файл.

Для моего веб-сайта main.js прямо сейчас включает скрипты для обработки формы подписки на новостную рассылку, добавления якорных ссылок в каждый из заголовков на странице и загрузки специальных сообщений о распродажах и скидках на мои продукты. Они используются почти на каждой странице моего сайта, поэтому я загружаю их все вместе.

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

Как разделить код на пакеты

Вспомогательная функция loadJS() отлично подходит для фактической загрузки пакетов кода, но… как их вообще связать?

Сегодня я использую пользовательскую сборку Gulp (вот мой шаблон Gulp), чтобы объединить отдельные компоненты в связанные файлы.

До того, как я изучил командную строку, я использовал CodeKit и до сих пор рекомендую его всем, кто более нагляден или не знает/не любит командную строку. Это Mac для компьютеров, но если вы используете Windows, я слышал, что Prepros также является отличным вариантом с аналогичным набором функций.

🔥 Присоединяйтесь к Vanilla JS Academy! Новая сессия начнется 4 апреля. Нажмите здесь, чтобы узнать больше.

Ненавидите сложность современной клиентской веб-разработки? Каждый будний день я рассылаю короткие электронные письма о том, как создать более простую и устойчивую сеть. Присоединяйтесь к более чем 13 000 других пользователей.

Сделано с ❤️ в Массачусетсе. Если не указано иное, весь код можно использовать бесплатно в соответствии с лицензией MIT. Я также очень нерегулярно делюсь мыслями, не связанными с программированием.

Это руководство расширяет пример, представленный в разделе "Начало работы". Убедитесь, что вы хотя бы знакомы с приведенным там примером и главой «Управление выводом».

Разделение кода – одна из самых привлекательных функций webpack. Эта функция позволяет разделить код на различные пакеты, которые затем можно загружать по запросу или параллельно. Его можно использовать для создания небольших пакетов и управления приоритетами загрузки ресурсов, что при правильном использовании может существенно повлиять на время загрузки.

Существует три основных подхода к разделению кода:

  • Точки входа: разделение кода вручную с использованием конфигурации входа.
  • Предотвращение дублирования: используйте зависимости Entry или плагин SplitChunksPlugin для дедупликации и разделения фрагментов.
  • Динамический импорт: разделение кода с помощью встроенных вызовов функций внутри модулей.

Точки входа

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

проект

другой-модуль.js

webpack.config.js

Это даст следующий результат сборки:

Как уже упоминалось, в этом подходе есть несколько подводных камней:

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

Первый из этих двух моментов определенно является проблемой для нашего примера, так как lodash также импортируется в ./src/index.js и поэтому будет дублироваться в обоих пакетах. Давайте удалим это дублирование в следующем разделе.

Предотвратить дублирование

Зависимости ввода

Опция dependOn позволяет разделять модули между чанками:

webpack.config.js

webpack.config.js

А вот результат сборки:

Как вы видите, кроме shared.bundle.js , index.bundle.js и Another.bundle.js сгенерирован еще один файл runtime.bundle.js .

Хотя в веб-пакете разрешено использование нескольких точек входа на странице, по возможности этого следует избегать в пользу точки входа с несколькими импортами: entry: < page: ['./analytics', './app'] > . Это приводит к лучшей оптимизации и согласованному порядку выполнения при использовании тегов асинхронного скрипта.

Плагин SplitChunks

Подключаемый модуль SplitChunksPlugin позволяет извлекать общие зависимости в существующий фрагмент записи или совершенно новый фрагмент. Давайте воспользуемся этим для дедупликации зависимости lodash из предыдущего примера:

webpack.config.js

С опцией конфигурации optimization.splitChunks теперь мы должны увидеть удаление повторяющейся зависимости из index.bundle.js и other.bundle.js . Плагин должен заметить, что мы выделили lodash в отдельный фрагмент и удалили мертвый груз из нашего основного пакета. Давайте выполним сборку npm run, чтобы проверить, сработало ли это:

Вот некоторые другие полезные плагины и загрузчики, предоставленные сообществом для разделения кода:

    : полезно для отделения CSS от основного приложения.

Динамический импорт

Для динамического разделения кода webpack поддерживает два похожих метода. Первый и рекомендуемый подход — использовать синтаксис import(), который соответствует предложению ECMAScript для динамического импорта. Устаревший подход, специфичный для веб-пакетов, заключается в использовании require.ensure . Давайте попробуем использовать первый из этих двух подходов.

предупреждение

Вызовы import() используют обещания для внутреннего использования. Если вы используете import() со старыми браузерами (например, IE 11), не забудьте замаскировать Promise с помощью полифилла, такого как es6-promise или promise-polyfill.

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

webpack.config.js

Мы также обновим наш проект, чтобы удалить неиспользуемые файлы:

проект

Теперь вместо статического импорта lodash мы будем использовать динамический импорт для разделения фрагмента:

источник/index.js

Причина, по которой нам нужно значение по умолчанию, заключается в том, что, начиная с версии webpack 4, при импорте модуля CommonJS импорт больше не будет разрешаться в значение module.exports , а вместо этого будет создаваться искусственный объект пространства имен для модуля CommonJS. Для получения дополнительной информации о причине этого прочитайте веб-пакет 4: import() и CommonJs.

Давайте запустим webpack, чтобы увидеть, как lodash выделяется в отдельный пакет:

Поскольку функция import() возвращает обещание, ее можно использовать с асинхронными функциями. Вот как это упростит код:

источник/index.js

Можно предоставить динамическое выражение для import(), когда вам может понадобиться позже импортировать конкретный модуль на основе вычисляемой переменной.

Предварительная выборка/предварительная загрузка модулей

В Webpack 4.6.0+ добавлена ​​поддержка предварительной выборки и предварительной загрузки.

Использование этих встроенных директив при объявлении вашего импорта позволяет веб-пакету выводить «Подсказку ресурса», которая сообщает браузеру, что для:

  • предварительная выборка: ресурс, вероятно, потребуется для навигации в будущем
  • предварительная загрузка: ресурс также потребуется во время текущей навигации

Примером этого является наличие компонента HomePage, который отображает компонент LoginButton, который затем по требованию загружает компонент LoginModal после нажатия.

Кнопка входа.js

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

Директива предварительной загрузки имеет ряд отличий от предварительной выборки:

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

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

Давайте представим компонент ChartComponent, которому нужна огромная библиотека ChartingLibrary. Он отображает LoadingIndicator при рендеринге и мгновенно выполняет импорт ChartingLibrary по запросу:

ChartComponent.js

Неправильное использование webpackPreload может снизить производительность, поэтому будьте осторожны при его использовании.

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

Если загрузка скрипта завершится неудачно до того, как веб-пакет начнет загрузку этого скрипта сам по себе (веб-пакет просто создает тег скрипта для загрузки своего кода, если этого скрипта нет на странице), этот обработчик catch не запустится до тех пор, пока не будет установлено значение chunkLoadTimeout. не передается. Такое поведение может быть неожиданным. Но это объяснимо — вебпак не может выдать никакой ошибки, потому что вебпак не знает, что скрипт не сработал. Webpack добавит обработчик onerror в скрипт сразу после возникновения ошибки.

Чтобы предотвратить такую ​​проблему, вы можете добавить свой собственный обработчик onerror, который удаляет скрипт в случае какой-либо ошибки:

В этом случае скрипт с ошибкой будет удален. Webpack создаст собственный скрипт, и любая ошибка будет обработана без тайм-аутов.

Анализ пакетов

Когда вы начнете разбивать свой код, может быть полезно проанализировать вывод, чтобы проверить, где оказались модули. Официальный инструмент анализа — хорошее место для начала. Есть и другие варианты, поддерживаемые сообществом:

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

Дальнейшие шаги

См. "Отложенная загрузка" для более конкретного примера использования import() в реальном приложении и "Кэширование" для более эффективного разделения кода.

Читайте также: