React не используется, но все еще включен в пакет

тлдр; что мешает устранению мертвого кода React в моем проекте? Он не используется, за исключением неиспользуемой утилиты в библиотеке, которую я использую.


Недавно я создал небольшую библиотеку для повторной попытки динамического импорта . Я сделал модуль, не зависящий от реакции, и еще один, который просто обрабатывал особенности реакции. Это было сделано для того, чтобы потребитель не использовал React, если не использовал утилиты реагирования. Затем я создал индексный модуль, который вставил два и экспортировал именованные константы.

import _createDynamicImportWithRetry from "./dynamic-import-with-retry";
import _reactLazy from "./react-lazy";

export const reactLazyWithRetry = _reactLazy;
export const createDynamicImportWithRetry = _createDynamicImportWithRetry;

Это поле main в package.json.

Из предыдущего чтения я предположил, что когда Vite использует эту библиотеку, он должен быть в состоянии понять, что простой импорт именованного экспорта createDynamicImportWithRetry потребует только извлечения «./dynamic-import-with-retry», а не другого импорта. К сожалению, похоже, это не так: я опубликовал проект, который показывает, что это не так: транзитивная зависимость React собирается в комплекте, независимо от того, не используется ли она.

Я бы предположил, что анализ мертвого кода во время сборки для производства обрезал бы неиспользуемые импорты дерева. Как я могу обеспечить обрезку мертвых ветвей?

Воспроизведение:

mkdir tmp-proj && cd tmp-proj && mkdir src

echo '<script type = "module" src = "./src/index.js"></script>' > index.html

echo "import * as o from '@fatso83/retry-dynamic-import'
o.reactLazyWithRetry( () => Promise.resolve())" > src/index.js

npm i @fatso83/retry-dynamic-import; npm i vite

npx vite build 

vite v4.3.5 building for production...
✓ 13 modules transformed.
dist/index.html                0.08 kB │ gzip: 0.09 kB
dist/assets/index-4e07d3ed.js  9.27 kB │ gzip: 3.89 kB

9КБ есть проблема. Он должен быть ближе к 500 байтам, но он включает всю производственную сборку React :/

Семантика этого всегда довольно неловкая. Попробуйте изменить индексный модуль, чтобы использовать export { default as createDynamicImportWithRetry } from "./dynamic-import-with-retry" и export { default as reactLazyWithRetry } from "./react-lazy";. Больше ничего, только эти две строчки. См. github.com/rollup/rollup/pull/4867. Также связан moduleSideEffects накопительный пакет, который Vite использует для производственных сборок.

adsy 13.05.2023 18:35
moduleSideEffects только что создал пустые файлы в репродукции, независимо от того, что я делал и независимо от содержимого :( Теперь смотрим на экспорт.
oligofren 13.05.2023 18:59

Попытался напрямую заменить содержимое node_modules/@fatso83/retry-dynamic-import/dist/index.js этими двумя строками (даже если это означает, что один экспорт потерян), но React по-прежнему включен в результирующий пакет. Только при удалении реагирующего экспорта он не включается.

oligofren 13.05.2023 19:05

Если я правильно понимаю, вы переносите логику повторного импорта в React для тех, кто использует вашу библиотеку в своих проектах React, верно? Почему бы вам не вывести React вовне и не позволить вашим пользователям вместо этого предоставлять его? Я вижу, вы не делаете никакой упаковки в своем build:dist, только компиляцию TS. Вы можете использовать Vite, чтобы упаковать его (а также сделать композицию TS) и внедрить React с вашего дистрибутива.

Dan Macak 19.05.2023 17:20

Привет, Дэн. Хорошая точка зрения. На самом деле я пытался внедрить его в более позднюю версию, но я вытащил эти более поздние версии (из-за других проблем, связанных с экспортом/упаковкой), поэтому я просто указал на опубликованную версию (в которую входит React). Увидев, что это дало мне ложные срабатывания, я обновил ссылку, указав на версию 1.1.1 (вместо 1.0.7). Тем не менее, если встряхивание дерева сработало, потребитель должен был иметь возможность обрезать эту ветвь, поскольку экспорт с использованием React не используется.

oligofren 22.05.2023 11:23
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
2
5
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы должны указать, какой бы упаковщик вы ни использовали, эта реакция должна быть предоставлена ​​​​клиентом вашей библиотеки, т.е. что он не должен быть связан с вашей библиотекой. Это называется экстернализация.

Чтобы получить общее представление о том, как это делается как в package.json, так и в вашем сборщике, вы можете проверить мой предыдущий ответ, используя Rollup . Если вы используете Vite, экстернализация настроить еще проще.

На самом деле, я уже внедрил библиотеку в более позднюю версию, поэтому сама библиотека больше не объединяет React. Я обновил ссылку сейчас, чтобы указать на версию, созданную Vite, которая экстернализует React. Тем не менее, тот же результат применим: именно Vite в потребителях этой библиотеки не может встряхнуть неиспользуемый экспорт.

oligofren 22.05.2023 11:15
Ответ принят как подходящий

Я разместил это на доске обсуждений Vite , и оказалось, что Rollup еще не может отслеживать задания (на май 2023 года).

Сведение не может отслеживать назначения (пока), поэтому мы деоптимизируем все свойства правой части назначений. Кроме того, деоптимизация Rollup не знает потока выполнения, поэтому предполагается, что lazy может быть сеттером в require$$0.lazy=()=>{}, и это побочный эффект. Вероятно, это не так просто исправить.

Поскольку Vite полагается на Rollup для объединения, ничего не нужно делать, кроме перемещения экспорта из корневого модуля и его экспорта как своего собственного экспорта в package.json (что я и сделал для версии 2).

Другие вопросы по теме