<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Bundler on Commentary of Takao</title><link>https://takao.blog/zh-tw/tags/bundler/</link><description>Recent content in Bundler on Commentary of Takao</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Commentary of Takao</copyright><lastBuildDate>Tue, 22 Oct 2024 00:00:00 +0900</lastBuildDate><atom:link href="https://takao.blog/zh-tw/tags/bundler/index.xml" rel="self" type="application/rss+xml"/><item><title>JavaScript Bundle Size 優化: From Analysis to Action</title><link>https://takao.blog/zh-tw/web/bundle-size-optimization/</link><pubDate>Tue, 22 Oct 2024 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/bundle-size-optimization/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/bundle-size-optimization-zh-tw.png" alt="Featured image of post JavaScript Bundle Size 優化: From Analysis to Action" /&gt;&lt;p&gt;JavaScript bundle size directly impacts user experience. Larger bundles mean longer download times, slower parsing and compilation, and worse Core Web Vitals. A 100KB increase in JavaScript reduces conversion rates by 2 to 3 percent. Bundle optimization is an ongoing investment, not a one-time fix, and follows a cycle of analysis, identification, optimization, and monitoring.&lt;/p&gt;
&lt;h2 id="bundle-analysis-tools"&gt;Bundle Analysis Tools
&lt;/h2&gt;&lt;p&gt;Understanding what is in your bundle is the first step. webpack-bundle-analyzer provides an interactive treemap visualization that highlights large dependencies and duplicate modules. Vite users can leverage rollup-plugin-visualizer with sunburst and network graphs, while esbuild offers the &amp;ndash;metafile flag for detailed output analysis. source-map-explorer maps compiled code back to source files.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Generate a bundle analysis report with webpack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npx webpack --profile --json &amp;gt; stats.json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npx webpack-bundle-analyzer stats.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When interpreting analysis output, look for oversized dependencies, duplicate modules across chunks, unexpected bundled polyfills, and outdated library versions that include unnecessary code. A typical optimization walkthrough can reduce a 1.2MB bundle to under 480KB by addressing these findings.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tree-shaking-deep-dive"&gt;Tree Shaking Deep Dive
&lt;/h2&gt;&lt;p&gt;Tree shaking uses static analysis of ES module import and export statements to eliminate unused exports. Effective tree shaking requires three conditions: ESM syntax only, as CommonJS require calls cannot be statically analyzed; side-effect-free packages declared with &lt;code&gt;&amp;quot;sideEffects&amp;quot;: false&lt;/code&gt; in package.json; and proper import styles that avoid namespace imports.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;sideEffects&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;exports&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;./src/index.js&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;./utils&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;./src/utils/index.js&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Common misconceptions include the belief that &lt;code&gt;import * as&lt;/code&gt; prevents tree shaking, that barrel files are safe, and that CSS imports never interfere. In practice, &lt;code&gt;lodash-es&lt;/code&gt; treeshakes far better than &lt;code&gt;lodash&lt;/code&gt;, and &lt;code&gt;date-fns&lt;/code&gt; at 16KB gzipped is dramatically smaller than moment.js at 231KB.&lt;/p&gt;
&lt;h2 id="code-splitting-strategies"&gt;Code Splitting Strategies
&lt;/h2&gt;&lt;p&gt;Code splitting operates at multiple levels. Entry-point splitting separates 攤商, application, and runtime code into distinct bundles. Dynamic splitting uses route-based splitting for SPAs with React.lazy and Suspense, component-level splitting for heavy UI elements such as rich text editors and charts, and library-level splitting triggered on user interaction.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Dashboard&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;React&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;lazy&lt;/span&gt;(() =&amp;gt; &lt;span style="color:#66d9ef"&gt;import&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;./routes/Dashboard&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Analytics&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;React&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;lazy&lt;/span&gt;(() =&amp;gt; &lt;span style="color:#66d9ef"&gt;import&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;./routes/Analytics&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Preload and prefetch hints further optimize loading. Use link rel preload for critical chunks and link rel prefetch for predicted navigation. A real-world case study shows that splitting a dashboard application into eight route chunks reduced the initial bundle by 65 percent while maintaining instant navigation to secondary routes.&lt;/p&gt;
&lt;h2 id="dynamic-import-patterns"&gt;Dynamic Import Patterns
&lt;/h2&gt;&lt;p&gt;Beyond basic route splitting, conditional imports load polyfills only when needed, for example importing core-js features only when the browser lacks native 支援. Environment-specific imports load debugging tools only in development. Feature-flag-based imports enable A/B testing without deploying multiple application versions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;group&amp;#39;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; Array.&lt;span style="color:#a6e22e"&gt;prototype&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;import&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;core-js/actual/array/group&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Webpack magic comments provide fine-grained control over chunk naming, prefetch behavior, and loading mode. The &lt;code&gt;webpackExports&lt;/code&gt; magic comment enables targeted tree shaking within dynamic imports, ensuring that only the specific exports used are included in the resulting chunk.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="dead-code-elimination-and-dependency-profiling"&gt;Dead Code Elimination and Dependency Profiling
&lt;/h2&gt;&lt;p&gt;Tools like ts-prune and unimported identify exports that are imported but never called. Dependency profilers such as cost-of-modules and package-size quantify the bundle impact of each library, encouraging developers to evaluate libraries by their weight, not just their API quality.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Library&lt;/th&gt;
					&lt;th&gt;Gzipped Size&lt;/th&gt;
					&lt;th&gt;Alternative&lt;/th&gt;
					&lt;th&gt;Gzipped Size&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;moment.js&lt;/td&gt;
					&lt;td&gt;231KB&lt;/td&gt;
					&lt;td&gt;dayjs&lt;/td&gt;
					&lt;td&gt;6KB&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;lodash&lt;/td&gt;
					&lt;td&gt;71KB&lt;/td&gt;
					&lt;td&gt;lodash-es&lt;/td&gt;
					&lt;td&gt;24KB&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;redux&lt;/td&gt;
					&lt;td&gt;12KB&lt;/td&gt;
					&lt;td&gt;zustand&lt;/td&gt;
					&lt;td&gt;3KB&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Strategies for reducing dependency count include preferring lighter alternatives, self-hosting small utility functions instead of pulling in a library, and using CDN with subresource integrity for rarely changed large libraries.&lt;/p&gt;
&lt;h2 id="modern-format-output"&gt;Modern Format Output
&lt;/h2&gt;&lt;p&gt;ESM is the modern module format offering better tree shaking, static analysis, and browser-native module loading. Dual ESM and CJS packages use the exports field in package.json for conditional exports. For application bundling, output ESM for modern browsers and 秋季 back to legacy bundles using the type module and nomodule pattern. Vite defaults to ESM-first output, while webpack maintains a CJS compatibility layer for broader 支援.&lt;/p&gt;
&lt;h2 id="monitoring-and-budget-enforcement"&gt;Monitoring and Budget Enforcement
&lt;/h2&gt;&lt;p&gt;Optimization without monitoring is unsustainable. Lighthouse CI detects bundle size regressions, and GitHub Actions with size-limit or bundlesize enforce budgets in pull requests.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;size&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;size-limit&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;size-limit&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;dist/main.js&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;170 KB&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;running&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Real-user monitoring tracks script evaluation time and long tasks through the PerformanceObserver API. Setting per-route budgets and monitoring the 95th percentile of JavaScript execution time provides ongoing visibility into bundle health.&lt;/p&gt;
&lt;h2 id="advanced-techniques"&gt;Advanced Techniques
&lt;/h2&gt;&lt;p&gt;Manual chunk splitting with webpack splitChunks cacheGroups or Vite manualChunks gives fine-grained control over output structure. CSS-in-JS extraction eliminates runtime overhead from styled-components or Emotion. Polyfill strategies using core-js with useBuiltIns usage minimize the compatibility payload. WebAssembly modules require separate size profiling, as standard bundle analyzers do not capture Wasm blob sizes.&lt;/p&gt;
&lt;h2 id="結論"&gt;結論
&lt;/h2&gt;&lt;p&gt;Bundle optimization is an iterative process that must be measured continuously. Set up analysis tools, identify the biggest contributors to bundle size, apply targeted optimizations, and automate monitoring in CI/CD. Teams that treat bundle size as a first-class concern ship faster experiences, better Core Web Vitals scores, and higher conversion rates as a result.&lt;/p&gt;</description></item></channel></rss>