Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
バンドル最適化マニアクス at tfconf
Search
Koutarou Chikuba
May 14, 2022
Programming
7
4.3k
バンドル最適化マニアクス at tfconf
treeshake, DCE, terser mangling
Koutarou Chikuba
May 14, 2022
Tweet
Share
More Decks by Koutarou Chikuba
See All by Koutarou Chikuba
CI/CD 改善の勘所
mizchi
0
35
極限環境で最終ビルドを絞るためのフロントエンド設計
mizchi
15
5.1k
Server Side JavaScript のためのバンドル最適化
mizchi
5
6.8k
V8 as a container on CDN Edge worker
mizchi
6
2.1k
Edge Side Frontend という新領域
mizchi
35
14k
「たかがJavaScript」のその先 #TECHPLAY
mizchi
47
20k
Deno Node 両刀
mizchi
6
2.4k
「フロントエンド領域」を再定義する
mizchi
50
36k
光を超えるためのフロントエンドアーキテクチャ
mizchi
91
23k
Other Decks in Programming
See All in Programming
Go 1.24でジェネリックになった型エイリアスの紹介
syumai
1
120
JavaScriptツール群「UnJS」を5分で一気に駆け巡る!
k1tikurisu
9
1.8k
ARA Ansible for the teams
kksat
0
160
パスキーのすべて ── 導入・UX設計・実装の紹介 / 20250213 パスキー開発者の集い
kuralab
3
830
PHPカンファレンス名古屋2025 タスク分解の試行錯誤〜レビュー負荷を下げるために〜
soichi
1
550
Serverless Rust: Your Low-Risk Entry Point to Rust in Production (and the benefits are huge)
lmammino
1
140
Lottieアニメーションをカスタマイズしてみた
tahia910
0
130
クリーンアーキテクチャから見る依存の向きの大切さ
shimabox
4
870
Java Webフレームワークの現状 / java web framework at burikaigi
kishida
9
2.2k
Django NinjaによるAPI開発の効率化とリプレースの実践
kashewnuts
1
150
PHP ステートレス VS ステートフル 状態管理と並行性 / php-stateless-stateful
ytake
0
110
新宿駅構内を三人称視点で探索してみる
satoshi7190
2
110
Featured
See All Featured
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
7k
BBQ
matthewcrist
87
9.5k
Facilitating Awesome Meetings
lara
52
6.2k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Optimising Largest Contentful Paint
csswizardry
34
3.1k
How STYLIGHT went responsive
nonsquared
98
5.4k
Docker and Python
trallard
44
3.3k
The Power of CSS Pseudo Elements
geoffreycrofte
75
5.5k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
27
1.6k
Code Review Best Practice
trishagee
67
18k
Fashionably flexible responsive web design (full day workshop)
malarkey
406
66k
Transcript
όϯυϧ࠷దԽϚχΞΫε @mizchi | Plaid, Inc at Techfeed Conference 2022
JS Ͱ͔͍ͬͱਏ͘ͳ͍ʁ
JS Ͱ͔͍ͬͱ͖… » ։ൃ࣌ » खݩͷϏϧυαΠΫϧ͕͍ » npm install Ͱ
CI ͕͍ » Ϣʔβʔମݧ » μϯϩʔυͰىಈ·Ͱ͕ͯ͘ਏ͍ » CPU ࠅͰόοςϦ͕ਏ͍
ϑϩϯτΤϯυվળ㲈Ϗϧυվળ
୭͕ԿΛվળ͢Δͷ͔ʁ » ΞϓϦέʔγϣϯ։ൃऀ: ϚΫϩνϡʔχϯά » Chunk Split / Treeshake »
ϥΠϒϥϦ։ൃऀ: ϚΠΫϩνϡʔχϯά » Treeshakable ͳ API ͷఏڙ » mangling
ࠓ͢༰ » جૅฤ: օ͕Δ͖ Treeshake+DCE » ্ڃฤ: ϥΠϒϥϦ։ൃऀͷͨΊͷϚΠΫϩνϡʔχϯά
جૅฤ: Treeshake + DCE
ESM Treeshake όϯυϥ(rollup/webpack)͕ ESM ͷະ༻ import Λআ͢Δػ ೳ import {a,
b} from "x"; console.log(a); // όϯυϧ࣌ʹ b ͕ফ͑Δ ※ ͨͩ͠ side effect (ޙड़) ͕ͳ͍લఏ
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 » جຊతʹτοϓϨϕϧͰ࣮ߦ͞ΕΔίʔυΛॻ͔ͳ͍
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;
Treeshake+DCEͷ࣮ફ
ݕূ༻ 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(/* ུ */), ], }
ࠓճͷιʔείʔυ import { prod, dev } from "./sub"; // prod=0,
dev=1 export const ex = process.env.NODE_ENV === "production" ? prod : dev;
1. ఆల։ » process.env.NODE_ENV=production import { prod, dev } from
"./sub"; export const ex = "production" === "production" ? prod : dev;
2. ఆಉ࢜ͷධՁ » "production"==="production" => true import { prod, dev
} from "./sub"; export const ex = true ? prod : dev;
3. DCE » true?prod:dev => prod import { prod }
from "./sub"; export const ex = prod;
4. Bundle with Treeshake » prod=0 ͷΈల։ const prod =
0; export const ex = prod;
5. terser compress » ϩʔΧϧม໊Λѹॖ const o=0;export{ex as o}
Treeshake+DCE ͷ͍ํ » ڥ͝ͱʹఆల։Ͱ if(false){...} ͳ Dead Code Λ࡞Δ »
ϥΠϒϥϦ࡞ऀ: treeshakable ͳ API ઃܭΛ͢Δ » αΠζࢹͩͱϝιουνΣʔϯආ͚Δ » ϥΠϒϥϦར༻ऀ: ඞཁͳίʔυ͚ͩ import » ಛʹ import * as ... Λආ͚Δ » Ұ෦ͷϥΠϒϥϦ NODE_ENV=production ͰϏϧυ࣌࠷దԽ
ϚΠΫϩνϡʔχϯά্ڃฤ
terser ͱྑ͘ͳΔ » ΊͬͪΌݡ͍͜ͱΛ͢Δ͚Ͳҙ֎ͱ͙͢ఘΊΔͷͰࢹֶͯ͠΅͏ » https://try.terser.org/ ͕༑ୡ » ެ։ API
Ҏ֎શ෦ 1~2 จࣈʹ͢Δؾ࣋ͪͰ
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 ͞ΕΔ໊લ͘ͳΒͳ͍
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};
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};
͞Βʹൃలฤ: ෳճ 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)
શ෦ఆԽͳΜͯ͑ΒΕͳ͍ਓ mangle.properties.regex ͕࠷ޙͷखஈ // rollup plugins terser({ mangle: { properties:
{ regex: "^_" } } }), ਖ਼نදݱΛຬͨͨ͠ϓϩύςΟΛ mangle ରʹ͢Δ ຊʹ ^_ ͕ϓϥΠϕʔτ͔Ͳ͏͔ਓ͕ؒ֬ೝ͠·͠ΐ͏
ϚΠΫϩνϡʔχϯά: TS ฤ (LTͰ࣌ؒແ͍ͷͰεΩοϓ)
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];
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] Ͱ͖ͳ͍
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 ͷܕใͷ߹ͳΜͯΒͳ͍
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 ݸҎ্ͩͱਓ͕ؒ͠ΜͲ͘ͳͬͯ͘Δ)
TS: ύϑΥʔϚϯεͷͨΊͷ tsconfig.json { "compilerOptions": { "target": "es2019", // 2017
Ҏ߱ async await Λม͠ͳ͍ "importHelpers": true, // tslib Λ͏ "preserveConstEnums": false, // enum ͷΠϯϥΠϯల։Λڧ੍ "noUnusedLocals": true, // ະ༻มͷܯࠂ "noUnusedParameters": true, // ະ༻Ҿͷܯࠂ "importsNotUsedAsValues": "error", // import type ͷڧ੍ } }
࣮ફ݁Ռͷհ
࣮ફ݁Ռ: @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;
Ͳ͏ͬͯখ͔ͨ͘͞͠ » ࣗ࡞ύʔαίϯϏωʔλͰ named tuple ͷߏจఆٛΛు͘ » ߏจఆٛΛ cbor ͰόΠφϦʹѹॖ
» ϥϯλΠϜʹόΠφϦΛΠϯϥΠϯԽ » ϕϯνऔΔͱ֎෦ binary Λ fetch ͢ΔΑΓஅવ͔ͬͨ » (ASIະରԠͰ prettier Λલఏ)
None
Shakerphobia ͷհ » ࡞ https://shakerphobia.netlify.app/ » bundlephobia ͰΘ͔Βͳ͍ treeshake ޙͷαΠζΛܭଌ
» skypack + webworker + rollup ͰϒϥβͰϏϧυ
·ͱΊ » ͳΜʹͤΑ Treeshake ͷཧղ͕େࣄ » terser ϝϯόΞΫηεʹऑ͍ » ಛʹΦϒδΣΫτఆΛආ͚Α͏
» ࠷ऴखஈͱͯ͠ mangle.properties.regex » ϥΠϒϥϦ࡞ऀґଘπϦʔ্ͷෛՙ͔ͩΒؤுΕ (কདྷతʹ TS ܕใͬͯ Side Effect ఆ͢Δ minifier ͕ग़Δͱࢥ͏͚Ͳɺݱঢ়ͳ͍Ͱ͢)
͓ΘΓ