Написано 2018.08.30
Webpack 2.0 начал внедрять технологию встряхивания дерева. Перед введением технологии вводятся несколько связанных понятий:
-
AST - это синтаксическое дерево (абстрактное синтаксическое дерево), полученное путем анализа кода JS. Сантаксическое дерево AST может преобразовать каждое утверждение куска кода JS в узел в дереве.
-
Устранение мертвого кода DCE, основанное на сохранении неизменного результата выполнения кода, удаляет бесполезный код. Преимущества этого:
- Уменьшить размер программы
- Сокращение времени выполнения программы
- Содействовать будущей оптимизации архитектуры программы
Так называемый Dead Code в основном включает в себя:
- Код, который не выполняется в программе (например, невозможные переходы, операторы после возврата и т. д.)
- Код, который вызывает мертвую переменную (код, который больше не читается после записи в переменную)
Встряхивание дерева — это способ DCE, который игнорирует неиспользуемый код при связывании.
Краткое описание механизма
Встряхивание дерева было впервые предложено авторами роллапа. Вот аналогия:
Если вы сравните упаковку кода с приготовлением торта. Традиционный способ — бросить все яйца (со скорлупой) и перемешать, затем поставить их в духовку и, наконец, выбрать и удалить все (бесполезные) скорлупы. И встряхивание дерева заключается в том, чтобы сначала положить полезный яичный белок и яичный желток в перемешивание, а, наконец, сделать торт непосредственно.
Поэтому по сравнению сИсключить неиспользуемый код, тряска дерева на самом делеУзнать используемый код.
на основеES6
статические ссылки, встряхивание дерева работает путем сканирования всех ES6export
, Найтиimport
содержание и добавлено в окончательный код. Реализация веб-пакета заключается в том, чтобы поставить всеimport
Отмечается как использование/неиспользование двух, различающихся при последующем процессе сжатия. Потому что, как метафора того, что перед печью (компрессия запуталась) перед снятием оболочки (без использованияimport
), кладем только полезные яичные белки (использованныеimport
)
инструкции
Во-первых, исходный код должен соответствовать спецификации модуля ES6 (import
&export
), если спецификация CommonJS (require
) не доступен.
Согласно советам на официальном веб-сайте Webpack, webpack2 поддерживает встряхивание дерева, и вам необходимо изменить файл конфигурации, чтобы указать, что babel не должен преобразовывать модули ES6 в модули CommonJS при обработке js-файлов Конкретный метод:
Установите для модулей babel-preset-es2015 значение fasle в .babelrc, что означает, что модули ES6 не будут обрабатываться.
// .babelrc
{
"presets": [
["es2015", {"modules": false}]
]
}
После тестирования веб-пакеты 3 и 4 могут нормально встряхивать дерево без добавления этого файла .babelrc.
Дерево качает два шага
webpack отвечает за тегирование кода, размещениеimport
&export
Отмечено как 3 категории:
- все
import
отметить как/* harmony import */
- использовал
export
отметить как/* harmony export ([type]) */
,в[type]
Возможно, связано с внутренними компонентами веб-пакета.binding
,immutable
и т.п. - не используется
export
отметить как/* unused harmony export [FuncName] */
,в[FuncName]
дляexport
имя метода
После этого на шаге Uglifyjs (или другого подобного инструмента) код упрощается, а бесполезное удаляется.
Анализ случая
Все примеры кодов находятся вкаталог demo/webpack
метод обработки
// index.js
import {hello, bye} from './util'
let result1 = hello()
console.log(result1)
// util.js
export function hello () {
return 'hello'
}
export function bye () {
return 'bye'
}
Скомпилированный файл bundle.js выглядит следующим образом:
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util__ = __webpack_require__(1);
let result1 = Object(__WEBPACK_IMPORTED_MODULE_0__util__["a" /* hello */])()
console.log(result1)
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = hello;
/* unused harmony export bye */
function hello () {
return 'hello'
}
function bye () {
return 'bye'
}
Примечание: опущеноbundle.js
Приведенный выше код загрузки пользовательского модуля webpack исправлен.
за неиспользованныйbye
метод, веб-пакет помечен какunused harmony export bye
, но код остается. а такжеhello
это нормальноharmony export (immutable)
.
использовать послеUglifyJSPlugin
второй шаг, т.bye
Полностью зачистил, результат такой:
Толькоhello
определение и призыв.
Обработка классов
// index.js
import Util from './util'
let util = new Util()
let result1 = util.hello()
console.log(result1)
// util.js
export default class Util {
hello () {
return 'hello'
}
bye () {
return 'bye'
}
}
Скомпилированный файл bundle.js выглядит следующим образом:
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util__ = __webpack_require__(1);
let util = new __WEBPACK_IMPORTED_MODULE_0__util__["a" /* default */]()
let result1 = util.hello()
console.log(result1)
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
class Util {
hello () {
return 'hello'
}
bye () {
return 'bye'
}
}
/* harmony export (immutable) */ __webpack_exports__["a"] = Util;
Обратите внимание, что веб-пакет правUtil
Класс помечен в целом (помечен как используемый), а не для двух методов по отдельности. Таким образом, окончательный упакованный код по-прежнему будет содержатьbye
метод. Это указываетВстряхивание дерева веб-пакетов обрабатывает только контент верхнего уровня, такие как внутренние компоненты класса и объекта, больше не обрабатываются отдельно.
Это также в основном связано с динамической языковой природой JS. если поставитьbye()
Удалить, рассмотрим следующий код:
// index.js
import Util from './util'
let util = new Util()
let result1 = util[Math.random() > 0.5 ? 'hello', 'bye']()
console.log(result1)
Компилятор не распознает, что имя метода на самом деле имеет форму прямого вызова (util.hello()
) или в виде строки (util['hello']()
) или каким-то другим более причудливым способом. Поэтому удаление метода по ошибке приведет только к ошибке в работе, которая не стоит потерь.
побочный эффект
Побочные эффекты означают, что после выполнения метода или файла это также повлияет на другое глобальное содержимое кода. Например полифилл в различныхprototype
Добавление методов — типичный пример побочных эффектов. (Также видно, что процедура отличается от приема лекарств, и не все побочные эффекты уничижительны)
Существует две формы побочных эффектов, которые необходимо учитывать при оптимизации кода.Когда мы обычно ревертируем код, мы также должны делать это с аналогичным мышлением, в противном случае всегда будет день, когда мы будем наступить на яму.
Побочные эффекты внедрения модуля
// index.js
import Util from './util'
console.log('Util unused')
// util.js
console.log('This is Util class')
export default class Util {
hello () {
return 'hello'
}
bye () {
return 'bye'
}
}
Array.prototype.hello = () => 'hello'
Приведенный выше код проходит черезwebpack + uglify
После обработки он станет таким:
несмотря на то чтоUtil
Класс импортируется без какого-либо использования, но его нельзя удалить, как если бы на него не было ссылки. В смешанном коде вы можете увидетьUtil
онтология класса (export
содержание) нет, но до и послеconsole.log
и правильноArray.prototype
расширения остаются. Это компромисс, сделанный компилятором, чтобы убедиться, что эффект выполнения кода остается неизменным, потому что он не знает, что делают эти две строки кода, поэтому он принимает все коды по умолчанию.обепобочный эффект.
побочные эффекты вызовов методов
// index.js
import {hello, bye} from './util'
let result1 = hello()
let result2 = bye()
console.log(result1)
// util.js
export function hello () {
return 'hello'
}
export function bye () {
return 'bye'
}
Мы импортируем и вызываемbye()
, но не использует возвращаемое значениеresult2
, этот код можно удалить? (Спросите себя, если вы проводите рефакторинг кода, существует ли 90-процентная вероятность удаления этой строки кода напрямую?)
webpack не удалил эту строку кода, по крайней мере, не всю. это удаляетresult2
, но сохраняетbye()
вызов (сжатый код выглядит какObject(r.a)()
)так же какbye()
Определение.
Это также потому, что компилятор не знаетbye()
Что происходит внутри. если он содержитArray.prototye
расширение, то его удаление снова вызовет проблемы.
Как решить побочные эффекты?
Мы ценим то, что webpack такой строгий, но если метод не имеет побочных эффектов, как мы можем сказать webpack, чтобы он позволил ему удалить его с уверенностью?
Есть 3 метода, подходящие для разных ситуаций.
pure_funcs
// index.js
import {hello, bye} from './util'
let result1 = hello()
let a = 1
let b = 2
let result2 = Math.floor(a / b)
console.log(result1)
util.js такой же, как и раньше, и повторяться не будет. Отличие в webpack.config.js, нужно добавить параметрыpure_funcs
,Рассказыватьwebpack Math.floor
Побочных эффектов нет, можно смело удалять:
plugins: [
new UglifyJSPlugin({
uglifyOptions: {
compress: {
pure_funcs: ['Math.floor']
}
}
})
],
в добавленномpure_funcs
После настройки оригинал зарезервированMath.floor(.5)
был удален, и он работал, как мы ожидали.
Но у этого метода есть большое ограничение: если мы объединим webpack и uglify, а имя метода кода, прошедшего через webpack, будет переименовано, то настройка исходного имени метода здесь потеряет смысл. а напримерMath.floor
Такие глобальные методы не будут переименованы, чтобы они вступили в силу. Так что применимость не слишком сильная.
побочные эффекты package.json
webpack 4 добавляет новый элемент конфигурации в package.json с именемsideEffects
, значениеfalse
Указывает, что весь пакет не имеет побочных эффектов, или массив со списком модулей с побочными эффектами. Подробные примеры можно найти в официальном веб-пакете.пример.
По результатам, еслиsideEffects
значениеfalse
, текущий пакетexport
Методов 5, а мы используем 2, а остальные 3 не будут запакованы, что и ожидается. Но это требует сознательного добавления автора пакета, так что в текущем запуске webpack 4 ограничения не маленькие.
concatenateModule
добавлен вебпак 3webpack.optimize.ModuleConcatenateModulePlugin()
, непосредственно в webpack 4 в качестве конфигурации по умолчанию для `mode = 'production'. Это оптимизация пакета веб-пакетов, которая оптимизирует ситуацию «каждый модуль заключен в замыкание» в ситуацию «все модули заключены в одно и то же замыкание». Это значительно улучшило размер самого кода, и здесь также может быть решена проблема побочных эффектов.
По-прежнему выберите следующие 2 файла в качестве примеров:
// index.js
import {hello, bye} from './util'
let result1 = hello()
let result2 = bye()
console.log(result1)
// util.js
export function hello () {
return 'hello'
}
export function bye () {
return 'bye'
}
После включения функции concatenateModule упакованный код выглядит следующим образом:
первый,bye()
Вызовы методов и тела исключены.
Второй,hello()
Вызовы методов и определения синтезируются вместе в прямойconsole.log('hello')
В-третьих, изначальная цель этой функции: уменьшение объема кода.
Первоначальное намерение этой функции состоит в том, чтобы, наконец, вывести все модули в один и тот же метод, тем самым объединив вызов и определение вместе. такbye()
Таким образом, методы без побочных эффектов могут быть легко идентифицированы и удалены после слияния. Для более подробного ознакомления с этой функцией см. эту статью
Суммировать
- Напишите код, используя синтаксис модуля ES6.
- Функции служебного класса должны быть максимально выведены в отдельном виде, а не концентрироваться в одном объекте или классе.
- объявить побочные эффекты
- Помните о побочных эффектах при самостоятельном рефакторинге кода