tgoop.com/smelukov_dev/21
Create:
Last Update:
Last Update:
📝 Мои мысли по поводу поиска неиспользуемого кода (dead code) и его устранения (DCE - dead code elimination).
Для начала, давайте определимся в каких ситуациях код может никогда не исполняться:
Что-то пошло не так
function foo() {
console.log(1); // сработает
return true;
console.log(2); // никогда не сработает
}
Второй
console.log
- это недостижимый кодДа, бывает и такое, но чаще всего это недосмотр разработчика.
В таких ситуациях обычно помогает любой минификатор:
uglifyjs
, terser
, closure compiler
Недостижимые ветки
Представим, что есть переменная
foo
, которая может принимать одно из трех значений - bar
, baz
или quux
и эти значения мы хотим как-то обрабатывать:switch (foo) {
case 'bar': ...
case 'baz': ...
case 'quux': ...
}
После рефакторинга мы избавились от значения
quux
и foo
теперь никогда не сможет его принимать, следовательно, последняя ветка будет недостижимой. Есть вероятность, что мы забудем убрать его обработку из свитча. В таком случае минификаторы бесполезны. В этом случае поможет типизация:enum Foo {
bar, baz
}
switch (foo) {
case Foo.bar: ...
case Foo.baz: ...
case Foo.quux: ... // получим ошибку
}
Неиспользуемые экспорты
Представим, что есть модуль, которых что-то экспортирует, но это никому не нужно и никто это не импортирует (полностью или частично):
app.js
import { bar } from './lib.js';
lib.js
export function foo() { ... } // неиспользуемый экспорт
export function bar() { ... }
Такая ситуация может возникнуть, когда вы используете только часть какой-то библиотеки.
С такими кейсами справляется tree shaking - бандлер (например
webpack`) отслеживает все экспорты, которые нигде не используются (не импортируются) и вырезает их. Далее, js-компрессор (например `uglifyjs
или `tercer`) вырезает весь код, который был завязан на эти неиспользуемые экспорты (например функции который были экспортированы).Этот метод работает по нижней границе - то есть вырезает код, который точно не используется.
Почему по нижней? Потому что не всегда можно точно определить используется экспорт или нет. Например, в webpack 4 - очень слабая поддержка tree shaking для
commonjs
, но она намного лучше реализована в webpack 5 (учтено больше кейсов, но все еще в разработке). Плюс, если в модуле используется eval
или new Function
, то модуль сразу помечается как исключенный из оптимизации, т.к. в этом случае вообще ничего нельзя гаратировать (кто его знает что там за код внутри eval).Неиспользуемый CSS
Для CSS история похожа на
commonjs
тем, что там тоже много вариантов того, как можно использовать класс.Есть оптимизаторы, которые вырезают неиспользуемый CSS, например
CSSO
. Но CSSO основывается не на анализе подключения CSS, а на внешнем списке используемых классов. То есть на вход CSSO поступает список классов, которые точно используются а на выходе получается CSS, который содержит декларации только для этих классов. Проблема тут в том, что надо как-то получить список классов, которые точно используются. Но использование классов в коде совершенно не означает, что сам этот код где-то используется.Неиспользуемые файлы
В кодовой базе могут быть файлы, которые нигде не подключаются. Если вы используете сборщик, то можно использовать плагин, который получает список всех файлов в директории с проектом и удаляет из этого списка те файлы, которые были использованы при сборке. В итоге получается список файлов, которые не были использованы при сборке и их, можно считать неиспользуемыми. Главное здесь соблюдать осторожность при удалении файлов, т.к. какие-то файлы могут не проходить через сборщик и подключаться на страницу просто по ссылке. Но такие файлы лучше держать в отдельной директории.
Пример плагина для вебпака: https://www.npmjs.com/package/webpack-deadcode-plugin
BY Сергей Мелюков

Share with your friend now:
tgoop.com/smelukov_dev/21