Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
バンドル最適化マニアクス at tfconf
Search
Koutarou Chikuba
May 14, 2022
Programming
4.7k
8
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
バンドル最適化マニアクス at tfconf
treeshake, DCE, terser mangling
Koutarou Chikuba
May 14, 2022
More Decks by Koutarou Chikuba
See All by Koutarou Chikuba
技術記事、 専門家としてのプログラマ、 言語化
mizchi
16
8.7k
CI/CD 改善の勘所
mizchi
0
290
極限環境で最終ビルドを絞るためのフロントエンド設計
mizchi
16
6.2k
Server Side JavaScript のためのバンドル最適化
mizchi
5
8.1k
V8 as a container on CDN Edge worker
mizchi
6
2.6k
Edge Side Frontend という新領域
mizchi
35
15k
「たかがJavaScript」のその先 #TECHPLAY
mizchi
47
21k
Deno Node 両刀
mizchi
7
2.7k
「フロントエンド領域」を再定義する
mizchi
50
38k
Other Decks in Programming
See All in Programming
Optunaを作ったときにやりたかったこと、その後 (2026/10/16, Optuna Meetup #3)
iwiwi
2
1.1k
ハーネス設計入門 〜 基礎知識の整理から実務へのステップアップ 〜
kinopeee
28
34k
APNsからLive Activityを開始する話
yumnumm
0
170
When benchmarks go bad - what I learned from measuring performance wrong
hollycummins
0
220
なぜCTOを降りてFDEを選んだのか?〜なぜプロダクト企業がFDEで顧客の現場に踏み込むのか〜
gonta
1
220
ジョジョの世界をXRで再現する
mofurune
0
180
パスワード再設定メールのURLについて知っておこう
uyuki234
0
170
AI時代のTerminal環境を UNIX哲学で設計・運用する
yasunori0418
0
110
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
AWS CDKのカスタムリソースでContinuum(旧Security Agent)を実装した話 | Implementing Continuum (formerly Security Agent) via AWS CDK Custom Resource
akihisaikeda
2
160
Kotlinize Is Not Just Converting to Kotlin
chigichan24
1
180
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Kaigi on Rails 2026, Tokyo, Japan
marcoroth
2
300
Featured
See All Featured
Facilitating Awesome Meetings
lara
57
7.2k
Tell your own story through comics
letsgokoyo
1
1.1k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.5k
The Cult of Friendly URLs
andyhume
79
7k
A designer walks into a library…
pauljervisheath
211
25k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
280
Deep Space Network (abreviated)
tonyrice
0
380
Mobile First: as difficult as doing things right
swwweet
225
10k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
1k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
450
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 ͕ग़Δͱࢥ͏͚Ͳɺݱঢ়ͳ͍Ͱ͢)
͓ΘΓ