Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CodeFest 2019. Алексей Золотых (Infobip) — Сжимаем Javascript по-взрослому

16b6c87229eaf58768d25ed7b2bbbf52?s=47 CodeFest
April 07, 2019

CodeFest 2019. Алексей Золотых (Infobip) — Сжимаем Javascript по-взрослому

— Как влияет размер javascript бандла на общую производильность в 2019
— Как размер javascript бандла влияет на время старта вашего приложения
— Как правильно подключить javascript на страницу в 2019
— Как сжимают Javascript основные утилиты для сжатия
— Какие оптимизации и где применяются
— Какие реальные проблемы вызывают те или иные оптимизации SSA-формы
— Зачем нам типы для сжатия javascript

16b6c87229eaf58768d25ed7b2bbbf52?s=128

CodeFest

April 07, 2019
Tweet

Transcript

  1. СЖИМАЕМ JAVASCRIPT ПО- СЖИМАЕМ JAVASCRIPT ПО- ВЗРОСЛОМУ ВЗРОСЛОМУ Алексей Золотых

    1
  2. 2

  3. 3

  4. СЖИМАЕМ JAVASCRIPT ПО- СЖИМАЕМ JAVASCRIPT ПО- ВЗРОСЛОМУ ВЗРОСЛОМУ 4

  5. – ЗАЧЕМ? – ЗАЧЕМ? – БЫСТРОДЕЙСТВИЕ! – БЫСТРОДЕЙСТВИЕ! 5

  6. ЭКОНОМИЯ ЭКОНОМИЯ 6

  7. Экономия Время на сеть Процессор Память 7

  8. 8

  9. Пример 1 9

  10. 5mb <!DOCTYPE html> <html lang="en"> <head> </head> <body> <script src="./index.js"></script>

    </body> </html> 10
  11. Network — 100ms 11

  12. Parse — 300ms 12

  13. Evaluate — 500ms 13

  14. Пример 2 14

  15. class Point { constructor() { ... this.x = x; ...

    } 15
  16. Babel function _instanceof(left, right){...} function _classCallCheck(instance, Const function _defineProperties(target, props

    function _createClass(Constructor, proto 16
  17. 17

  18. КАК? КАК? Потоковое сжатие Сжатие на основе AST 18

  19. ПОТОКОВОЕ СЖАТИЕ ПОТОКОВОЕ СЖАТИЕ на уровне сервера 19

  20. npx create-react-app my-app 20

  21. Без кэша + Slow 3G 21

  22. Размер 120k Время всего запроса 4 сек 22

  23. Ожидание 2 сек Загрузка 2 сек 23

  24. Сжатие в потоке gzip1— 50–90% gzip9 — 15–45% от gzip1

    zopfli — 4–7% от gzip9 brotli11 — 8–65% от zopfli 24
  25. Размер Время Было 120 4 Стало 40 2,7 25

  26. 30% БЫСТРЕЕ 30% БЫСТРЕЕ ЗАГРУЗКА СТРАНИЦЫ ЗАГРУЗКА СТРАНИЦЫ * *

    * для медленных сетей и быстрых устройств 26
  27. 27

  28. 28

  29. ... gzip on; gzip_static on; gzip_vary on; gzip_types ... brotli

    on; brotli_comp_level 4; brotli_static on; brotli types 29
  30. ОПЫТ LINKEDIN ОПЫТ LINKEDIN http://bit.do/brotli 30

  31. GIST С ОПИСАНИЕМ GIST С ОПИСАНИЕМ http://bit.do/jsgzip 31

  32. Итого: Просто Для сети Не работает для маленьких файлов Почти

    всегда стоит включить 32
  33. «Для js файлов на фоне космического коэффициента сжатия brotli считаю

    ненужным минификацию» 33
  34. <!DOCTYPE html> <html lang="en"> <head> <script src="https://CDN/lodash.js"> </head> <body> </body>

    34
  35. 35

  36. lodash.js По сети В браузере 87k 527k 36

  37. Можем сжать? 37

  38. UglifyJS используется в lodash 38

  39. uglifyjs lodash.js > lodash.min.js 39

  40. По сети В браузере 35k 144k 40

  41. Убрали пробелы Убрали комменты 41

  42. Протянули константы (function() { const a = 1; const b

    = 2; console.log(a + b); }()); console.log(3); 42
  43. if(1 * 5){ console.log(1) } console.log(1); 43

  44. arrows, booleans, collapse_vars, comparisons, computed_props, conditionals, dead_code, drop_console, drop_debugger, ecma,

    evaluate, expression, global_defs, hoist_funs, hoist_props, hoist_vars, ie8, if_return, inline, join_vars, keep_classnames, keep_fargs, keep_fnames, keep_infinity, loops, negate_iife, passes, properties, pure_getters, pure_funcs, reduce_funcs, reduce_vars, sequences, side_effects, switches, top_retain,
  45. toplevel, typeofs, unsafe, unsafe_arrows, unsafe_comps, unsafe_Function, unsafe_math, unsafe_methods, unsafe_proto, unsafe_regexp,

    unsafe_undefined, unused, warnings, 44
  46. Можем ещё? 45

  47. uglifyjs lodash.js --mangle > lodash.min 46

  48. * кроме функций верхнего уровня function lognLongLongName(mySuperArgs){ return mySuperArgs[0]; }

    function n(i){ return i[0]; } 47
  49. По сети В браузере 23k 72k 48

  50. Как толстеет бандл? 49

  51. от 23к до 576к import * as _ from 'lodash'

    if (_.has([1, 2, 3], 2)) { console.log("debug"); } 50
  52. от 3к до 10к import * as _ from 'lodash/has'

    if (_.has([1, 2, 3], 2)) { console.log("debug"); } 51
  53. IMPORT COST IMPORT COST JetBrains VSCode bit.do/jcost bit.do/vcost 52

  54. 53

  55. Как это скомпилит UglifyJS? import * as _ from 'lodash/has'

    if (_.has([1, 2, 3], 2)) { console.log("debug"); } 54
  56. import * as _ from 'lodash/has' ^ ERROR: Unexpected token:

    name ?? expected: punc ?? 55
  57. UglifyJS 56

  58. Terser github.com/terser-js/terser 57

  59. Что можно еще улучшить? 58

  60. Treeshaking 59

  61. 60

  62. module.js export function square(x) { return x * x; }

    export function cube(x) { return x * x * x; } 61
  63. index.js import {cube} from './module' console.log(cube(x)) 62

  64. Мечта function cube(x) { return x * x * x;

    } console.log(cube(x)) 63
  65. Реальность !function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]= {i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=r,t.d=function(e,r,n) {t.o(e,r)||Object.defineProperty(e,r, {configurable:!1,enumerable:!0,get:n})},t.r=function(e)

    {Object.defineProperty(e,"__esModule",{value:!0})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=0)}([function(e,r,t){"use strict";t.r(r);var n;console.log((n=3)*n*n)}]); 64
  66. Dry противоречит Treeshaking 65

  67. module.js export function square(x) { return x * x; }

    export function cube(x) { return x * x * x; } 66
  68. module.js export function square(x) { return x * x; }

    export function cube(x) { return square(x) * x } 67
  69. Иногда Treeshaking не работал в Webpack 68

  70. Статья на Хабре bit.do/treesh 69

  71. ... /* unused harmony export square */ /* harmony export

    (immutable) */ __webpa function square(x) { return x * x; } function cube(x) { t * * 70
  72. ... /* unused harmony export square */ /* harmony export

    (immutable) */ __webpa function square(x) { return x * x; } function cube(x) { t * * 71
  73. module.js export class MyClass { print(){ console.log('find me'); } }

    72
  74. /* unused harmony export MyClass */ var MyClass = function

    () { function MyClass() { babelHelpers.classCallCheck(this, My } MyClass.prototype.turn = function prin console.log('find me'); }; 73
  75. /* unused harmony export MyClass */ var MyClass = function

    () { function MyClass() { babelHelpers.classCallCheck(this, My } MyClass.prototype.turn = function prin console.log('find me'); }; 74
  76. UglifyJS не понимает, что выкидывать 75

  77. ... /* unused harmony export MyClass */ var MyClass =

    /*#__PURE__*/ function ... 76
  78. Как починить Babel 7 Webpack 77

  79. Как нужно 78

  80. Код на Dart void main() { print(cube(3)); } int cube(int

    x) => x * x * x; int square(int x) => x * x; 79
  81. Вывод ... main: function() { H.printString("27"); } ... 80

  82. class Car { Rudder rudder; List<Wheel> wheelList; } 81

  83. class Rudder { void turn(){ print('turn'); } } class Wheel

    { void pump(){ print('pump'); } } 82
  84. void main(){ final car = new Car(); car.wheelList = [

    new Wheel() new Wheel(), new Wheel(), new Wheel(), ]; car.rudder = new Rudder(); car.rudder.turn(); } 83
  85. ... main: function() { H.printString("turn"); } ... 84

  86. — Почему так не может Terser?! 85

  87. — Потому что он использует AST 86

  88. Dart использует SSA форму для оптимизаций 87

  89. SSA (англ. Static single assignment form) — промежуточное представление, используемое

    компиляторами, в котором каждой переменной значение присваивается лишь единожды 88
  90. Книга по SSA bit.do/ssabook 89

  91. — Я не хочу писать на Dart! — Есть же

    Closure Compiler! 90
  92. const car = { wheelList: [ new Wheel(), new Wheel(),

    new Wheel(), new Wheel(), ], rudder: new Rudder(), }; 91
  93. console.log("turn"); 92

  94. ОПТИМИЗАЦИИ ОПТИМИЗАЦИИ normalize optimizeArgumentsArray aggressiveInlineAliases collapseProperties earlyInlineVariables earlyPeepholeOptimizations removeUnusedCode disambiguateProperties

    codeRemovingLoop devirtualizePrototypeMethods flowSensitiveInlineVariables mainOptimizationLoop flowSensitiveInlineVariables removeUnusedCode collapseAnonymousFunctions extractPrototypeMemberDeclarations ambiguateProperties renameProperties convertToDottedProperties coalesceVariableNames peepholeOptimizations exploitAssign collapseVariableDeclarations denormalize renameVars renameLabels latePeepholeOptimizations 93
  95. 94

  96. 95

  97. Сложности 96

  98. 527k ⟹ 140k java -jar compiler.jar \ --compilation_level ADVANCED \

    --language_in=ES6 \ --js lodash.js > out.js 97
  99. Нужны типы 98

  100. /** * This annotation spans multiple lines * @type {{

    * id:number, * val:string, * }} */ 99
  101. TSICKLE TSICKLE TypeScript ⟹ Closure Translator 100

  102. Если все скомпилировалось в это, то console.log("turn"); 101

  103. Как будет работать здесь? <script> var rudder = new Rudder();

    </script> 102
  104. const car = {}; car.idNumber = '1234'; function displayId(car) {

    alert(car['idNumber']); } alert(car.idNumber); displayId(car); 103
  105. var a={}; a.a="1234"; alert(a.a); alert(a.idNumber); 104

  106. prepack.io 105

  107. (function () { function fibonacci(x) { return x <= 1

    ? x : fibonacci(x - 1 } global.x = fibonacci(15); })(); 106
  108. x = 610; 107

  109. Lodash? 527k ⇒ 868k 108

  110. Выводы Потоковое сжатие Можно оставить как есть GCC не для

    библиотек Искать другие варианты 109
  111. Алексей Золотых twitter: Спасибо за внимание! @zolotyh 110