Slide 1

Slide 1 text

όϯυϧ࠷దԽϚχΞΫε @mizchi | Plaid, Inc at Techfeed Conference 2022

Slide 2

Slide 2 text

JS Ͱ͔͍ͬͱਏ͘ͳ͍ʁ

Slide 3

Slide 3 text

JS Ͱ͔͍ͬͱ͖… » ։ൃ࣌ » खݩͷϏϧυαΠΫϧ͕஗͍ » npm install Ͱ CI ͕஗͍ » Ϣʔβʔମݧ » μ΢ϯϩʔυͰىಈ·Ͱ͕஗ͯ͘ਏ͍ » CPU ࠅ࢖ͰόοςϦ͕ਏ͍

Slide 4

Slide 4 text

ϑϩϯτΤϯυվળ㲈Ϗϧυվળ

Slide 5

Slide 5 text

୭͕ԿΛվળ͢Δͷ͔ʁ » ΞϓϦέʔγϣϯ։ൃऀ: ϚΫϩνϡʔχϯά » Chunk Split / Treeshake » ϥΠϒϥϦ։ൃऀ: ϚΠΫϩνϡʔχϯά » Treeshakable ͳ API ͷఏڙ » mangling

Slide 6

Slide 6 text

ࠓ೔࿩͢಺༰ » جૅฤ: օ͕஌Δ΂͖ Treeshake+DCE » ্ڃฤ: ϥΠϒϥϦ։ൃऀͷͨΊͷϚΠΫϩνϡʔχϯά

Slide 7

Slide 7 text

جૅฤ: Treeshake + DCE

Slide 8

Slide 8 text

ESM Treeshake όϯυϥ(rollup/webpack)͕ ESM ͷະ࢖༻ import Λ࡟আ͢Δػ ೳ import {a, b} from "x"; console.log(a); // όϯυϧ࣌ʹ b ͕ফ͑Δ ※ ͨͩ͠ side effect (ޙड़) ͕ͳ͍લఏ

Slide 9

Slide 9 text

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 » جຊతʹτοϓϨϕϧͰ࣮ߦ͞ΕΔίʔυΛॻ͔ͳ͍

Slide 10

Slide 10 text

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;

Slide 11

Slide 11 text

Treeshake+DCEͷ࣮ફ

Slide 12

Slide 12 text

ݕূ༻ 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(/* ུ */), ], }

Slide 13

Slide 13 text

ࠓճͷιʔείʔυ import { prod, dev } from "./sub"; // prod=0, dev=1 export const ex = process.env.NODE_ENV === "production" ? prod : dev;

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

4. Bundle with Treeshake » prod=0 ͷΈల։ const prod = 0; export const ex = prod;

Slide 18

Slide 18 text

5. terser compress » ϩʔΧϧม਺໊Λѹॖ const o=0;export{ex as o}

Slide 19

Slide 19 text

Treeshake+DCE ͷ࢖͍ํ » ؀ڥ͝ͱʹఆ਺ల։Ͱ if(false){...} ͳ Dead Code Λ࡞Δ » ϥΠϒϥϦ࡞ऀ: treeshakable ͳ API ઃܭΛ͢Δ » αΠζࢹ఺ͩͱϝιουνΣʔϯ͸ආ͚Δ » ϥΠϒϥϦར༻ऀ: ඞཁͳίʔυ͚ͩ import » ಛʹ import * as ... Λආ͚Δ » Ұ෦ͷϥΠϒϥϦ͸ NODE_ENV=production ͰϏϧυ࣌࠷దԽ

Slide 20

Slide 20 text

ϚΠΫϩνϡʔχϯά্ڃฤ

Slide 21

Slide 21 text

terser ͱ஥ྑ͘ͳΔ » ΊͬͪΌݡ͍͜ͱΛ͢Δ͚Ͳҙ֎ͱ͙͢ఘΊΔͷͰ໨ࢹֶͯ͠΅͏ » https://try.terser.org/ ͕༑ୡ » ެ։ API Ҏ֎͸શ෦ 1~2 จࣈʹ͢Δؾ࣋ͪͰ

Slide 22

Slide 22 text

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 ͞ΕΔ໊લ͸୹͘ͳΒͳ͍

Slide 23

Slide 23 text

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};

Slide 24

Slide 24 text

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};

Slide 25

Slide 25 text

͞Βʹൃలฤ: ෳ਺ճ 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)

Slide 26

Slide 26 text

શ෦ఆ਺ԽͳΜͯ଱͑ΒΕͳ͍ਓ΁ mangle.properties.regex ͕࠷ޙͷखஈ // rollup plugins terser({ mangle: { properties: { regex: "^_" } } }), ਖ਼نදݱΛຬͨͨ͠ϓϩύςΟΛ mangle ର৅ʹ͢Δ ຊ౰ʹ ^_ ͕ϓϥΠϕʔτ͔Ͳ͏͔͸ਓ͕ؒ֬ೝ͠·͠ΐ͏

Slide 27

Slide 27 text

ϚΠΫϩνϡʔχϯά: TS ฤ (LTͰ͸࣌ؒແ͍ͷͰεΩοϓ)

Slide 28

Slide 28 text

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];

Slide 29

Slide 29 text

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] Ͱ͖ͳ͍

Slide 30

Slide 30 text

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 ͷܕ৘ใͷ౎߹ͳΜͯ஌Βͳ͍

Slide 31

Slide 31 text

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 ݸҎ্ͩͱਓ͕ؒ͠ΜͲ͘ͳͬͯ͘Δ)

Slide 32

Slide 32 text

TS: ύϑΥʔϚϯεͷͨΊͷ tsconfig.json { "compilerOptions": { "target": "es2019", // 2017 Ҏ߱͸ async await Λม׵͠ͳ͍ "importHelpers": true, // tslib Λ࢖͏ "preserveConstEnums": false, // enum ͷΠϯϥΠϯల։Λڧ੍ "noUnusedLocals": true, // ະ࢖༻ม਺ͷܯࠂ "noUnusedParameters": true, // ະ࢖༻Ҿ਺ͷܯࠂ "importsNotUsedAsValues": "error", // import type ͷڧ੍ } }

Slide 33

Slide 33 text

࣮ફ݁Ռͷ঺հ

Slide 34

Slide 34 text

࣮ફ݁Ռ: @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;

Slide 35

Slide 35 text

Ͳ͏΍ͬͯখ͔ͨ͘͞͠ » ࣗ࡞ύʔαίϯϏωʔλͰ named tuple ͷߏจఆٛΛు͘ » ߏจఆٛΛ cbor ͰόΠφϦʹѹॖ » ϥϯλΠϜʹ͸όΠφϦΛΠϯϥΠϯԽ » ϕϯνऔΔͱ֎෦ binary Λ fetch ͢ΔΑΓஅવ଎͔ͬͨ » (ASIະରԠͰ prettier Λલఏ)

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

Shakerphobia ͷ঺հ » ੿࡞ https://shakerphobia.netlify.app/ » bundlephobia Ͱ͸Θ͔Βͳ͍ treeshake ޙͷαΠζΛܭଌ » skypack + webworker + rollup Ͱϒϥ΢β಺ͰϏϧυ

Slide 38

Slide 38 text

·ͱΊ » ͳΜʹͤΑ Treeshake ͷཧղ͕େࣄ » terser ͸ϝϯόΞΫηεʹऑ͍ » ಛʹΦϒδΣΫτ಺ఆ਺Λආ͚Α͏ » ࠷ऴखஈͱͯ͠ mangle.properties.regex » ϥΠϒϥϦ࡞ऀ͸ґଘπϦʔ্ͷෛՙ͔ͩΒؤுΕ (কདྷతʹ͸ TS ܕ৘ใ࢖ͬͯ Side Effect ൑ఆ͢Δ minifier ͕ग़Δͱࢥ͏͚Ͳɺݱঢ়ͳ͍Ͱ͢)

Slide 39

Slide 39 text

͓ΘΓ