网页
综合
更新时间 2026-11-14 16:18:42
Tree Shaking | Webpack
自动蜘蛛
webpack.kr
正文约 3899 字
Tree shaking은 사용되지 않는 코드를 제거하기 위해 JavaScript 컨텍스트에서 일반적으로 사용되는 용어입니다. ES2015 모듈 구문은 정적 구조에 의존합니다. 예를 들면, import와 export가 있습니다. 이름과 개념은 ES2015 모듈 번들러의 rollup에 의해 대중화되었습니다.
webpack 2 릴리스에서는 ES2015 모듈(별칭 harmony 모듈)과 사용하지 않는 모듈의 export를 감지하는 기능을 제공합니다. 새로운 webpack 4의 릴리스는 package.json의 "sideEffects" 프로퍼티를 통해 컴파일러에 힌트를 제공하는 방식으로 기능을 확장합니다. 프로젝트의 어떤 파일이 "순수"한지 나타내며, 만약 사용하지 않는다면 제거해도 괜찮은지 표시합니다.
이 가이드의 나머지 부분은 Getting Started에서 시작합니다. 가이드를 아직 읽지 않았다면 지금 읽어보세요.
다음 두 함수를 내보내는 새 유틸리티 파일인 src/math.js를 프로젝트에 추가해 보겠습니다.
mode 옵션을 development로 설정하여 번들이 압축되지 않도록 합니다.
이를 통해 새로운 메소드 중 하나를 사용하도록 entry 스크립트를 업데이트하고, 스크립트를 간단하게 하기 위해 lodash를 삭제하겠습니다.
우리는 src/math.js 모듈에서 square 메소드를 가져오지 않았습니다. 이 함수는 "사용하지 않는 코드"로 알려져 있고, 사용하지 않아 삭제되어야 하는 export를 의미합니다. 이제 npm 스크립트인 npm run build를 실행하여 출력된 번들을 살펴보겠습니다.
dist/bundle.js (around lines 5 - 25)
위의 unused harmony export square 주석을 참고하세요. 아래 코드를 보면 square를 가져오지 않지만, 여전히 번들에 포함되어 있습니다. 다음 섹션에서 수정해 보겠습니다.
100% ESM 모듈에서는 사이드 이펙트를 쉽게 식별할 수 있습니다. 그러나 우리는 아직 거기까지는 도달하지 않았으므로, 도달하기 까지는 코드의 "순수성"에 대한 힌트를 webpack 컴파일러에 제공해야 합니다.
이를 수행하는 방법은 package.json의 "sideEffects" 속성입니다.
위에 언급한 코드는 사이드 이펙트를 포함하지 않으므로, 간단하게 false로 프로퍼티를 표시하여 사용하지 않는 export는 제거해도 괜찮다는 것을 webpack에 알릴 수 있습니다.
"사이드 이펙트"는 하나 이상의 export를 보여주는 것 이외에도 import할 때 특별한 동작을 수행하는 코드입니다. 예를 들면 폴리필이 있습니다. 폴리필은 전체 스코프에 영향을 미치며 일반적으로 export를 제공하지 않습니다.
코드에 사이드 이펙트가 있다면 대신 배열을 사용할 수 있습니다.
배열은 관련된 파일의 간단한 전역 패턴을 허용합니다. 내부적으로 glob-to-regexp을 사용합니다 (사용 가능: *, **, {a,b}, [a-z]). /을 포함하지 않는 *.css와 같은 패턴은 **/*.css처럼 취급합니다.
가져온 모든 파일은 tree shaking의 대상이 됩니다. side effect만을 위해 가져온 CSS 파일이 프로덕션 모드에서 의도치 않게 제거되지 않도록 side effect 목록에 추가해야 합니다.
마지막으로 "sideEffects"는 module.rules 옵션으로도 설정할 수 있습니다.
sideEffects와 usedExports(트리 쉐이킹으로 알려져 있음)의 최적화는 두 가지 다른 점이 있습니다.
sideEffects는 전체 모듈 및 파일, 전체 하위 트리를 건너뛸 수 있기 때문에 훨씬 더 효율적입니다.
usedExports는 terser를 사용하여 문장에서 사이드 이펙트를 감지합니다. 이것은 JavaScript에서 어려운 작업이며 간단한 sideEffects 플래그만큼 효과적이지 않습니다. 또한 사이드 이펙트를 확인해야 하는 명세가 있기 때문에 하위트리 및 의존성을 무시할 수 없습니다. export 기능은 잘 동작하지만, React의 Higher Order Components(HOC)는 이와 관련된 문제가 있습니다.
동적 import()를 사용하는 경우 webpackExports 매직 주석을 사용하여 노출해야 하는 내보내기를 지정할 수 있으며, 이렇게 하면 webpack이 나머지 내보내기를 자동으로 처리합니다. 자세한 내용은 매직 주석을 참고하세요.
미리 번들된 버전은 아래와 같습니다.
Button이 사용되지 않으면 export { Button$1 };을 효과적으로 제거하고 나머지 코드를 모두 남길 수 있습니다. "이 코드가 사이드 이펙트가 없거나 안전하게 삭제할 수 있을까요?"라는 질문을 할 수 있습니다. withAppProvider()(Button) 라인 때문에 말하기 어렵습니다. withAppProvider가 호출되고 리턴 값도 호출됩니다. merge 또는 hoistStatics를 호출할 때 사이드 이펙트가 있나요? WrappedComponent.contextTypes (Getter?)를 읽거나 WithProvider.contextTypes (Setter?)를 할당할 때 사이드 이펙트가 있나요?
Terser는 알아내려고 노력하지만 여러 상황에서 장담할 수는 없습니다. 이것은 terser가 알아낼 수 없기 때문에, terser가 역할을 잘 수행하지 못한다는 것이 아닙니다. JavaScript 같은 동적 언어에서 확실하게 판단하는 것은 매우 어렵습니다.
그러나 /*#__PURE__*/ 어노테이션을 이용하여 terser를 도와줄 수 있습니다. 그 구문은 사이드 이펙트가 없는 것으로 표시합니다. 그래서 간단한 변경만으로 코드를 tree-shake 할 수 있습니다.
이렇게 하면 이 코드를 제거 할 수 있습니다. 그러나 포함되어야 하거나 평가가 필요한 import는 사이드 이펙트가 있을 수 있기 때문에 여전히 이에 대한 문제가 남아 있습니다.
이 문제를 해결하기 위해 package.json의 "sideEffects" 프로퍼티를 사용합니다.
이것은 /*#__PURE__*/와 비슷하지만, 구문 레벨이 아닌 모듈 레벨에서 사용합니다. "sideEffects" 프로퍼티에 대해 "sideEffect가 없다고 플래그된 모듈에서 직접적인 export가 없는 경우 번들러는 사이드 이펙트에 대한 평가를 건너 뛸 수 있다."라고 설명하고 있습니다.
Shopify's Polaris 예시에서 원래 모듈은 다음과 같습니다.
import { Button } from "@shopify/polaris";는 다음과 같이 동작합니다.
매칭되는 리소스별로 자세히 보겠습니다.
위의 경우에 4개 모듈만 번들에 포함됩니다.
이 최적화 후, 다른 최적화도 적용할 수 있습니다. 예를 들면, buttonFrom과 Button.js에서 export하는 buttonsFrom은 사용되지 않습니다. usedExports 최적화는 이를 알아채고 terser는 모듈에서 일부 명령문을 삭제할 수 있습니다.
모듈의 연결에도 적용됩니다. 따라서 이 4개의 모듈과 엔트리 모듈(그리고 아마도 좀 더 많은 의존성)을 연결할 수 있습니다. 결국 index.js에는 생성되는 코드가 없습니다.
sideEffects 플래그의 영향을 더 잘 이해하기 위해 CSS 에셋이 포함된 npm 패키지의 전체 예제와 트리 셰이킹 중에 이러한 에셋이 어떻게 영향을 받을 수 있는지 살펴보겠습니다. "awesome-ui"라는 가상의 UI 컴포넌트 라이브러리를 만들어 보겠습니다.
dist/components/index.js
dist/components/Button/index.js
dist/components/Button/Button.css
dist/components/Card/index.js and dist/components/Modal/index.js would have similar structure.
이제 Button 구성 요소만 사용하려는 컨수머 애플리케이션을 상상해 보세요.
本页为82搜索生成的网页快照,内容来自站内索引;演示数据的原文链接仅作示例。