Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

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

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

treeshake, DCE, terser mangling

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 ͕ग़Δͱࢥ͏͚Ͳɺݱঢ়ͳ͍Ͱ͢)