Что ожидает Webpack 4 от пакета С sideEffects: false

Webpack 4 добавили новую функцию: теперь он поддерживает sideEffects флаг в package.json модулей он спакетировал.

Из Webpack 4: выпущен сегодня

За последние 30 дней мы тесно сотрудничали с каждой из фреймворков, чтобы гарантировать, что они готовы поддерживать webpack 4 в их соответствующих кликах и т.д. Даже популярные библиотеки, такие как lodash-es, RxJS, поддерживают флаг sideEffects, поэтому, используя их последние версии вы увидите, что размер мгновенного пакета уменьшается из коробки.

Из документов Webpack

"SideEffects": флаговый флаг в big-module package.json указывает, что модули пакета не имеют побочных эффектов (при оценке) и только экспортируют экспорт. Это позволяет инструментам, таким как webpack, оптимизировать реэкспорт.

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

В контексте sideEffects то, что модуль должен избегать использовать sideEffects:false без проблем или, наоборот, что должен делать модуль, чтобы использовать sideEffects:false без проблем.

Для полноты, несмотря на @SeanLarkin солидный ответ ниже, я хотел бы получить разъяснения по следующему:

  1. Очевидно, что побочные эффекты означают что-то конкретное в fp и включают в себя ведение журнала (консоль или в другом месте) и бросание ошибок. Я предполагаю, что в этом контексте они вполне приемлемы?

  2. Может ли модуль содержать циклические ссылки и по-прежнему использовать sideEffects: false?

  3. Есть ли способ проверить или что модуль может проверить, что модуль может sideEffects: false кроме попытки отследить ошибки, вызванные его неправильным использованием?

  4. Существуют ли какие-либо другие факторы, которые препятствовали бы тому, чтобы модуль мог использовать sideEffects: false?

Ответы

Ответ 1

Шон из команды webpack! Я сделаю все возможное, чтобы наша документация продолжалась, чтобы ответить на ваш вопрос здесь!

Согласно спецификации модуля ECMA (я не собираюсь пытаться найти ссылку, поэтому вам придется доверять мне здесь, потому что ее похоронили),

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

Например, я создал небольшой сценарий с фотографией, чтобы лучше визуализировать случай:

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

Example of No Side Effects from Reexported Modules

Вы можете видеть здесь, что ни один из реэкспортов не производится каждым из них, поэтому (если веб-пакет получил сигнал), мы могли бы опустить экспорт b и c из даже отслеживаемых или используемых (размер и время выполнения производительности).

enter image description here

Однако в этом случае мы видим, что экспорт c "осуществляется" локальными изменениями штата, поскольку он переназначен суммированием b и a. Таким образом, (почему спецификация требует для этого), мы должны были бы включать в себя как b и a, и любой из его зависимостей в связке.

Мы выбрали "sideEffects: false" как способ сохранить как время компиляции, так и размер сборки, поскольку это позволяет нам мгновенно обрезать (явно) экспорт, который разработчики/авторы библиотеки знают, свободны от побочных эффектов (за счет свойства в package.json, или еще 2-3 строки конфигурации).

Хотя технически этот пример очень примитивен, когда вы начинаете работать с Frameworks или Libraries, которые реэкспортируют кучу модулей на более высокий уровень для Developer Experience (Three.js, Angular, lodash-es и т.д.), Тогда прирост производительности является значительным, когда (если они являются сторонними экспортными модулями экспорта), вы отмечаете их таким образом.

Дополнительные разъяснения:

  1. Очевидно, что побочные эффекты означают что-то конкретное в fp и включают в себя ведение журнала (консоль или в другом месте) и бросание ошибок. Я предполагаю, что в этом контексте они вполне приемлемы?

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

  1. Может ли модуль содержать циклические ссылки и по-прежнему использовать sideEffects: false?

Это должно теоретически.

  1. Есть ли способ проверить или что модуль может использовать sideEffects: false кроме попытки отследить ошибки, вызванные его неправильным использованием?

Не то, чтобы я знал, но это было бы отличным инструментом.

  1. Существуют ли какие-либо другие факторы, которые препятствовали бы тому, чтобы модуль мог использовать sideEffects: false?

Если свойство не находится в package.json или определено в module.rules или в mode: production не задано (которое использует оптимизацию).

Ответ 2

Этот параметр sideEffects очень расплывчатый и недостаточно описан в документах. Документы в основном напоминают "там флаг sideEffects для модулей, свободных от каких-либо побочных эффектов".

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

Мое настоящее понимание заключается в том, что этот флаг sideEffects предназначен только для "реэкспорта", "реэкспорта":

export { a } from './lib/a'
export { b } from './lib/b'

где-то в <npm-package>/index.js (или любой другой файл внутри <npm-package>).

Если Webpack обнаруживает, что приложение импортирует только из a <npm-package>, и не импортирует b где - нибудь, то Webpack может просто уронить export { b } from './lib/b' линии от <npm-package>/index.js приводит к <npm-package>/index.js что в результирующий <npm-package>/index.js не './lib/b.js' файл './lib/b.js' (что делает его меньшим размером файла './lib/b.js').

Теперь, если './lib/b.js' имел некоторые строки верхнего уровня кода, делающие некоторые "побочные эффекты", то есть если './lib/b.js' сделал что-то вроде:

  • window.jQuery =...
  • if (!global.Set) global.Set = require('babel-polyfill').Set
  • new XmlHttpRequest().post('/analytics', data)

то './lib/b.js', как говорят, имеет "побочные эффекты", потому что его код верхнего уровня (который выполняется при import './lib/b') влияет на что-то, выходящее за рамки './lib/b.js'.

В то же время, если './lib/b.js' верхнего уровня './lib/b.js' не выходит за пределы этого *.js файла, то он не имеет никаких "побочных эффектов":

let a = 1
a = a + 1 + computeSomeValue()
export default a
export const b = a + 1
export const c = b + 1

все это не "побочные эффекты".

И есть окончательный результат: если в пакете npm есть любые *.css файлы, которые пользователь может import эти *.css файлы являются "побочными эффектами", потому что:

import 'npm-package/style.css'

не имеет переменной, назначенной для этого import что фактически означает, что "этот импортированный модуль не используется нигде в приложении" для Webpack. И поэтому Webpack просто отбрасывает файл 'npm-package/style.css' из пакета как часть процесса "дрожания дерева", если в npm-package есть sideEffects: false. Поэтому вместо написания sideEffects: false всегда записывают "sideEffects": ["*.css"]. Даже если ваш пакет npm не экспортирует файлы CSS, он может сделать это в будущем, и это защитит от вышеупомянутой ошибки "CSS файл не включен".