тлдр; что мешает устранению мертвого кода 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 :/
moduleSideEffects только что создал пустые файлы в репродукции, независимо от того, что я делал и независимо от содержимого :( Теперь смотрим на экспорт.
Попытался напрямую заменить содержимое node_modules/@fatso83/retry-dynamic-import/dist/index.js этими двумя строками (даже если это означает, что один экспорт потерян), но React по-прежнему включен в результирующий пакет. Только при удалении реагирующего экспорта он не включается.
Если я правильно понимаю, вы переносите логику повторного импорта в React для тех, кто использует вашу библиотеку в своих проектах React, верно? Почему бы вам не вывести React вовне и не позволить вашим пользователям вместо этого предоставлять его? Я вижу, вы не делаете никакой упаковки в своем build:dist, только компиляцию TS. Вы можете использовать Vite, чтобы упаковать его (а также сделать композицию TS) и внедрить React с вашего дистрибутива.
Привет, Дэн. Хорошая точка зрения. На самом деле я пытался внедрить его в более позднюю версию, но я вытащил эти более поздние версии (из-за других проблем, связанных с экспортом/упаковкой), поэтому я просто указал на опубликованную версию (в которую входит React). Увидев, что это дало мне ложные срабатывания, я обновил ссылку, указав на версию 1.1.1 (вместо 1.0.7). Тем не менее, если встряхивание дерева сработало, потребитель должен был иметь возможность обрезать эту ветвь, поскольку экспорт с использованием React не используется.





Вы должны указать, какой бы упаковщик вы ни использовали, эта реакция должна быть предоставлена клиентом вашей библиотеки, т.е. что он не должен быть связан с вашей библиотекой. Это называется экстернализация.
Чтобы получить общее представление о том, как это делается как в package.json, так и в вашем сборщике, вы можете проверить мой предыдущий ответ, используя Rollup . Если вы используете Vite, экстернализация настроить еще проще.
На самом деле, я уже внедрил библиотеку в более позднюю версию, поэтому сама библиотека больше не объединяет React. Я обновил ссылку сейчас, чтобы указать на версию, созданную Vite, которая экстернализует React. Тем не менее, тот же результат применим: именно Vite в потребителях этой библиотеки не может встряхнуть неиспользуемый экспорт.
Я разместил это на доске обсуждений Vite , и оказалось, что Rollup еще не может отслеживать задания (на май 2023 года).
Сведение не может отслеживать назначения (пока), поэтому мы деоптимизируем все свойства правой части назначений. Кроме того, деоптимизация Rollup не знает потока выполнения, поэтому предполагается, что lazy может быть сеттером в require$$0.lazy=()=>{}, и это побочный эффект. Вероятно, это не так просто исправить.
Поскольку Vite полагается на Rollup для объединения, ничего не нужно делать, кроме перемещения экспорта из корневого модуля и его экспорта как своего собственного экспорта в package.json (что я и сделал для версии 2).
Семантика этого всегда довольно неловкая. Попробуйте изменить индексный модуль, чтобы использовать
export { default as createDynamicImportWithRetry } from "./dynamic-import-with-retry"иexport { default as reactLazyWithRetry } from "./react-lazy";. Больше ничего, только эти две строчки. См. github.com/rollup/rollup/pull/4867. Также связанmoduleSideEffectsнакопительный пакет, который Vite использует для производственных сборок.