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

バンドル最適化マニアクス at tfconf

バンドル最適化マニアクス at tfconf

treeshake, DCE, terser mangling

Avatar for Koutarou Chikuba

Koutarou Chikuba

May 14, 2022
Tweet

More Decks by Koutarou Chikuba

Other Decks in Programming

Transcript

  1. JS Ͱ͔͍ͬͱ͖… » ։ൃ࣌ » खݩͷϏϧυαΠΫϧ͕஗͍ » npm install Ͱ

    CI ͕஗͍ » Ϣʔβʔମݧ » μ΢ϯϩʔυͰىಈ·Ͱ͕஗ͯ͘ਏ͍ » CPU ࠅ࢖ͰόοςϦ͕ਏ͍
  2. ୭͕ԿΛվળ͢Δͷ͔ʁ » ΞϓϦέʔγϣϯ։ൃऀ: ϚΫϩνϡʔχϯά » Chunk Split / Treeshake »

    ϥΠϒϥϦ։ൃऀ: ϚΠΫϩνϡʔχϯά » Treeshakable ͳ API ͷఏڙ » mangling
  3. ESM Treeshake όϯυϥ(rollup/webpack)͕ ESM ͷະ࢖༻ import Λ࡟আ͢Δػ ೳ import {a,

    b} from "x"; console.log(a); // όϯυϧ࣌ʹ b ͕ফ͑Δ ※ ͨͩ͠ side effect (ޙड़) ͕ͳ͍લఏ
  4. Treeshake ͷલఏΛ཈͑Δ τοϓϨϕϧͰ෭࡞༻Λى͜͢ͱ Treeshake Ͱ͖ͳ͘ͳΔ // test_shakable.js const offset =

    new Date().getTimezoneOffset(); export const getOffset = () => offset; $ npx agadoo test_shakable.js # Rich-Harris/agadoo Failed to tree-shake test_shakable.js » new Date().getTimezoneOffset() ͕ Side Effect » جຊతʹτοϓϨϕϧͰ࣮ߦ͞ΕΔίʔυΛॻ͔ͳ͍
  5. DCE: Dead Code Elimination ະ࢖༻ίʔυΛ࡟আ͢Δ֤छ minifier ͷػೳ // source if(false){

    unused; } export const x = true ? f() : -1; function f(){ return 1; unused;} // ΠϯϥΠϯల։ // out export const x=1;
  6. ݕূ༻ rollup.config.js import ts from "rollup-plugin-ts"; import { terser }

    from "rollup-plugin-terser"; import replace from "@rollup/plugin-replace"; export default { plugins: [ ts({/* ུ */}), replace({ "process.env.NODE_ENV": JSON.stringify('production') }), terser(/* ུ */), ], }
  7. ࠓճͷιʔείʔυ import { prod, dev } from "./sub"; // prod=0,

    dev=1 export const ex = process.env.NODE_ENV === "production" ? prod : dev;
  8. 1. ఆ਺ల։ » process.env.NODE_ENV=production import { prod, dev } from

    "./sub"; export const ex = "production" === "production" ? prod : dev;
  9. 2. ఆ਺ಉ࢜ͷධՁ » "production"==="production" => true import { prod, dev

    } from "./sub"; export const ex = true ? prod : dev;
  10. 3. DCE » true?prod:dev => prod import { prod }

    from "./sub"; export const ex = prod;
  11. Treeshake+DCE ͷ࢖͍ํ » ؀ڥ͝ͱʹఆ਺ల։Ͱ if(false){...} ͳ Dead Code Λ࡞Δ »

    ϥΠϒϥϦ࡞ऀ: treeshakable ͳ API ઃܭΛ͢Δ » αΠζࢹ఺ͩͱϝιουνΣʔϯ͸ආ͚Δ » ϥΠϒϥϦར༻ऀ: ඞཁͳίʔυ͚ͩ import » ಛʹ import * as ... Λආ͚Δ » Ұ෦ͷϥΠϒϥϦ͸ NODE_ENV=production ͰϏϧυ࣌࠷దԽ
  12. compress: Կ͕୹͘ͳΔ͔ʁ // source const long_long_name_1: string = 'a'; const

    long_long_name_2: string = 'b'; export const exported_name_is_not_shrinkable = long_long_name_1 + long_long_name_2; // out const o="ab";export{o as exported_name_is_not_shrinkable}; » ϩʔΧϧม਺͸֎ʹग़ͳ͍ͷͰ compress ର৅ (module લఏ) » export ͞ΕΔ໊લ͸୹͘ͳΒͳ͍
  13. compress: ϝϯόΞΫηε͸َ໳ //source const x = { _private_value: 1, f()

    { return this._private_value;}, unused_prop2: 2, unused_prop3: 3, }; export const f = x.f; // out const e={_private_value:1,f(){return this._private_value}, unused_prop2:2,unused_prop3:3}.f;export{e as f};
  14. compress: ΦϒδΣΫτΛ΍ΊΔͱ //source const private_value = 1; const unused_prop2 =

    2; const unused_prop3 = 3; export const f = () => private_value; // out const o=()=>1;export{o as f};
  15. ͞Βʹൃలฤ: ෳ਺ճ minify // source const x = { A:

    { B: { v: 2, C: { D: { v:4, E: { F: { v: 6 } } }}} }}; console.log(x.A.B.v,x.A.B.C.D.v,x.A.B.C.D.E.F.v); /*1*/ const v={B:{v:2,C:{D:{v:4,E:{F:{v:6}}}}}};console.log(v.B.v,v.B.C.D.v,v.B.C.D.E.F.v); /*2*/ const v={v:2,C:{D:{v:4,E:{F:{v:6}}}}};console.log(v.v,v.C.D.v,v.C.D.E.F.v); /*3*/ const o=2,v={D:{v:4,E:{F:{v:6}}}};console.log(o,v.D.v,v.D.E.F.v); /*4*/ const o={v:4,E:{F:{v:6}}};console.log(2,o.v,o.E.F.v); /*5*/ const o=4,c={F:{v:6}};console.log(2,o,c.F.v); /*6*/ console.log(2,4,6); » terser ͸ΞΫηεઌͷఆ਺൑ఆΛઙ͔͘͠΍ͬͯͳ͍ʂ » compress: { passes: 6 } (default: 1)
  16. શ෦ఆ਺ԽͳΜͯ଱͑ΒΕͳ͍ਓ΁ mangle.properties.regex ͕࠷ޙͷखஈ // rollup plugins terser({ mangle: { properties:

    { regex: "^_" } } }), ਖ਼نදݱΛຬͨͨ͠ϓϩύςΟΛ mangle ର৅ʹ͢Δ ຊ౰ʹ ^_ ͕ϓϥΠϕʔτ͔Ͳ͏͔͸ਓ͕ؒ֬ೝ͠·͠ΐ͏
  17. TS: enum Λආ͚Δ // source enum XXX { AAA, BBB

    } XXX[XXX.AAA]; // out var XXX; (function (XXX) { XXX[XXX["AAA"] = 0] = "AAA"; XXX[XXX["BBB"] = 1] = "BBB"; })(XXX || (XXX = {})); XXX[XXX.AAA];
  18. TS: const enum Λ࢖͏ // source const enum XXX {

    AAA, BBB } console.log(XXX.AAA, XXX.BBB); // out console.log(0,1); » "preserveConstEnum": false ͰݩΩʔΛফͤΔ » ݩΩʔ͕࢒Βͳ͍ͷͰ XXX[XXX.AAA] Ͱ͖ͳ͍
  19. TS: private ͸ҙຯͳ͍ class C { constructor(private __private_x: number) {}

    private _private_method() { return this.__private_x;} public f() { return this._private_method();} } console.log(new C(1).f()); // out console.log(new class{constructor(t){this.__private_x=t}_private_method() {return this.__private_x}f(){return this._private_method()}}(1).f()); » terser ͸ TS ͷܕ৘ใͷ౎߹ͳΜͯ஌Βͳ͍
  20. TS: ߏ଄ମʹ named tuple Λ࢖͏ type Range = [start: number,

    end: number]; const range: Range = [1, 3]; const inRange = (x: number, [start, end]: Range) => { return start <= x && x <= end; } » ݻఆ௕ͷ഑ྻͷϝϯόʹ໊લΛ͚ͭΔ͜ͱ͕Ͱ͖Δ » ϓϩύςΟ໊͕ index ͳͷͰม਺໊ͷίετ͕গͳ͍ » (3 ݸҎ্ͩͱਓ͕ؒ͠ΜͲ͘ͳͬͯ͘Δ)
  21. TS: ύϑΥʔϚϯεͷͨΊͷ tsconfig.json { "compilerOptions": { "target": "es2019", // 2017

    Ҏ߱͸ async await Λม׵͠ͳ͍ "importHelpers": true, // tslib Λ࢖͏ "preserveConstEnums": false, // enum ͷΠϯϥΠϯల։Λڧ੍ "noUnusedLocals": true, // ະ࢖༻ม਺ͷܯࠂ "noUnusedParameters": true, // ະ࢖༻Ҿ਺ͷܯࠂ "importsNotUsedAsValues": "error", // import type ͷڧ੍ } }
  22. ࣮ફ݁Ռ: @mizchi/mints » αΠζಛԽͷ TypeScript ίϯύΠϥ: 8.1 kb(gzip) // npm

    install --save @mizchi/mints import { transformSync } from "@mizchi/mints"; const out = transfromSync("const x: number = 1;"); console.log(out.code); // const x=1;
  23. Ͳ͏΍ͬͯখ͔ͨ͘͞͠ » ࣗ࡞ύʔαίϯϏωʔλͰ named tuple ͷߏจఆٛΛు͘ » ߏจఆٛΛ cbor ͰόΠφϦʹѹॖ

    » ϥϯλΠϜʹ͸όΠφϦΛΠϯϥΠϯԽ » ϕϯνऔΔͱ֎෦ binary Λ fetch ͢ΔΑΓஅવ଎͔ͬͨ » (ASIະରԠͰ prettier Λલఏ)
  24. ·ͱΊ » ͳΜʹͤΑ Treeshake ͷཧղ͕େࣄ » terser ͸ϝϯόΞΫηεʹऑ͍ » ಛʹΦϒδΣΫτ಺ఆ਺Λආ͚Α͏

    » ࠷ऴखஈͱͯ͠ mangle.properties.regex » ϥΠϒϥϦ࡞ऀ͸ґଘπϦʔ্ͷෛՙ͔ͩΒؤுΕ (কདྷతʹ͸ TS ܕ৘ใ࢖ͬͯ Side Effect ൑ఆ͢Δ minifier ͕ग़Δͱࢥ͏͚Ͳɺݱঢ়ͳ͍Ͱ͢)