서론
Rollup was designed with libraries rather than apps in mind, and it is a perfect fit for React's use case.
Behind the Scenes: Improving the Repository Infrastructure - React Blog 에서 이를 보고 놀랐습니다. 이렇게 좋은 것이 왜 라이브러리 전용일까?什么原因致使它不适合用来构建 App?
零.webpack

webpack 은 복잡한 SPA 의 모듈 빌드에 전념하고 있으며, 매우 매력적인 것은 다양한 loader 입니다:
Essentially, webpack loaders transform all types of files into modules that can be included in your application's dependency graph.
일관된 방식으로 다양한 리소스 의존성을 처리하며, loader 를 통해 리소스 유형 차이를 숨깁니다 (js 는 module, css 는 module, img 도 module……). 장점은 다음과 같습니다:
No more carefully placing your files in the right folders and hacked-together scripts for adding hashes to file URLs?—?webpack can take care of it for you.
다른 매우 강력한 기능 은 다음과 같습니다:
-
Code Splitting: 프로덕션 환경에서 온디맨드 로드/병렬 로드
-
Tree Shaking: 빌드 시 불필요한 코드 (export) 제거
-
HMR: 개발 중 모듈 핫 스왑
-
Commons Chunk: 빌드 시 공통 의존성 추출
-
Dependency Graph: 빌드 완료 후 모듈 의존성 그래프 출력, bundle 에 가독성 부여
一.취지
rollup 은 처음부터 ES6 module 을 지향했습니다:
Next-generation ES6 module bundler.
당시 AMD, CMD, UMD 형식 논쟁이 아직 뜨거웠고, ES6 module 에는 브라우저 구현이 없었습니다. rollup 은 이렇게 나타났습니다
Rollup was created for a different reason: to build flat distributables of JavaScript libraries as efficiently as possible, taking advantage of the ingenious design of ES2015 modules.
(Webpack and Rollup: the same but different 에서 인용, rollup 작성자 본인 서술)
ES6 module 메커니즘을 최대한 활용하여 구조가 평평하고 성능이 뛰어난 라이브러리 bundle 을 구축하는 것, 즉 라이브러리向け로 설계된 것입니다
二.핵심 장점
It solves one problem well: how to combine multiple modules into a flat file with minimal junk code in between.
rollup 이 사람을 경탄 하게 하는 것은 bundle 의 깔끔함입니다. 특히 iife 형식은 내용이 매우 깨끗하고余分한 코드가 거의 없으며, 정말로 각 모듈을 의존 순서대로先后로 연결했을 뿐입니다
이는 rollup 의 모듈 처리 사고방식과 관련이 있습니다:
To achieve this, instead of turning modules into functions like many other bundlers, it puts all the code in the same scope, and renames variables so that they don't conflict. This produces code that is easier for the JavaScript engine to parse, for a human to read, and for a minifier to optimize.
모든 모듈을 bundle 파일 내 최외각 스코프에 평평하게 배치하며, 모듈 간 스코프 분리가 없습니다. 재명명에 의존하여 동일 스코프 하에서 명명 충돌 문제를 해결합니다. 몇 가지 명백한 이점:
-
런타임 성능 (코드 구조가 평평하여 파싱하기 쉬움)
-
bundle 소스 코드 가독성 (자연스러운 순서 구조, 모듈 정의/점프 없음)
-
압축 최적화성 (모듈 정의 등 압축되지 않는 보일러플레이트 코드 없음)
이렇게 하는 단점도 명확합니다:
-
모듈 시스템이 너무 정적이라 HMR 등의 기능 구현이 어려움
-
ES6 module 만 지향하여 cjs, umd 의존성을 신뢰성 있게 처리할 수 없음 (rollup-plugin-commonjs 를 사용할 때마다 문제에 직면합니다)
라이브러리 지향이라면 첫 번째 불지지도 괜찮지만, 두 번째 점은 정말头疼스럽고, 이차 의존성은 제어 불가능하며, 항상 불가피하게 cjs 모듈을 ES6 module 로 자동 변환하지 못하는 문제에 직면합니다. 예를 들어:
'foo' is not exported by bar.js (imported by baz.js)
일부 시나리오는 Troubleshooting 을 통해 namedExports 방식으로 그다지 즐겁지 않게 해결할 수 있습니다. 다른 때는 external 또는 globals 로 우회하며, 심지어 plugin 적용 순서를 조정하는 해법도 있습니다…… 그러나这类 문제를彻底적으로 해결할 방법은 없습니다:
Webpack gets around the need for namedExports by keeping everything as CommonJS modules and implementing Node's require system in the browser, which is why the resulting bundles are larger and take longer to start up. Both options are valid, but that's the tradeoff to be aware of — more config (Rollup, when using modules like React), or larger bundles (webpack).
(Is "named exports" feature or bug? 에서 인용)
cjs 는 결국 역사가 되겠지만, 현재 및 당분간은, npm 에 여전히 상당수의 cjs 모듈이 존재하며, SPA 든 library 든, 여전히 빈번하게 cjs 모듈 의존성 처리 문제에 직면합니다
三.선택 원칙
Use webpack for apps, and Rollup for libraries
App 을 구축한다면 webpack 이 적합, 라이브러리라면 물론 rollup 이 더 좋음
webpack 이 App 을 구축하는 장점은 다음 측면에 나타납니다:
-
강력한 플러그인 생태계, 주류 프론트엔드 프레임워크에 대응하는 loader 있음
-
App 지향 기능 지원, 예를 들어 이전에 언급한 HMR, Code Splitting, Commons Chunk 등은 App 개발에 필수적인 기능
-
Web 개발의 각 링크 간소화, 이미지 자동 base64, 리소스 캐시 (chunkId), 루트별 코드 분할,懒加载 등, 구현이 어렵지 않음
-
신뢰할 수 있는 의존 모듈 처리, rollup 과 같은 cjs 문제에 직면하지 않음,
__webpack_require__에 이러한 고민 없음
rollup 에는 이러한 장점이 없으며, 코드 분할 등을 수행하면 해결이 쉽지 않은 문제에 직면합니다. 충분한 시간과 파악이 없다면,轻易에 rollup 을 App 구축 도구로 시도해서는 안 됩니다
rollup 의 장점은高效率인 bundle 에 있으며, 이는 라이브러리가 추구하는 바로,多少周折해도 (React 16 이 수행한ように), 성능을 위해 가치가 있습니다
주의, 이 원칙은 적합한 도구로 적합한 일을 한다 고 말할 뿐이며, 다수의 일반 시나리오에 적용됩니다. rollup 으로 App 을 구축하고, webpack 으로 라이브러리를 구축하는 것도 흔합니다:
That's not a hard and fast rule?—?lots of sites and apps are built with Rollup, and lots of libraries are built with webpack. But it's a good rule of thumb.
전형적으로, 비즈니스 자체에 너무 많은 서드파티 모듈 의존성이 없고,かつ風格約定이 ES6 module 을 따르는 경우, rollup 으로 App 을 구축하는 것도 적합합니다 (Code Splitting 등도 완전히 불가능한 것은 아님)
P.S.另外, rollup 은 glup 이나 webpack 과 같이 stream 기반의 확장을 수행하기 어렵습니다. 예를 들어 vue 파일에서 세 부분을 분리하여分别 처리するなど (vue 플러그인 好像还不支持 ts)
四.외부 의존
React 와 같은 라이브러리의 경우, 가능한 한 서드파티 의존성으로 독립시켜야 하며, bundle 에 build 해서는 안 됩니다. 몇 가지 이유:
-
성능이 나빠짐, 예를 들어 React 16 은大変 노력하여 rollup + GCC(Google Closure Compiler) 로 전환하여 109kb 에 도달했으나, 직접 빌드하면 즉시 해방 전으로 돌아감
-
캐시에 불리함, 라이브러리는 자주 업데이트하지 않으므로 정적 리소스로 캐시 장점을 발휘할 수 있으나, 수동 build 의 내용은 도구 설정 영향을 받음
rollup 하에서는 external + globals 구성을 통해 외부 의존성을 마크할 수 있습니다:
external: ['react', 'react-dom'],
output: {
globals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
}
이렇게 생성된 bundle 은:
// iife
(function (React,ReactDOM) {
//...
}(React,ReactDOM));
// cjs
var React = _interopDefault(require('react'));
var ReactDOM = _interopDefault(require('react-dom'));
따라서 일반적으로 비즈니스 코드를 iife 로打包하고, script 를 통해 CDN 서드파티 라이브러리를 인용합니다:
<script crossorigin src="https://unpkg.com/react @16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom @16/umd/react-dom.production.min.js"></script>
<!-- 또는聚合 버전 -->
<script crossorigin src="//cdn.jsdelivr.net/combine/npm/react @16.0.0/umd/react.production.min.js,npm/react-dom @16.0.0/umd/react-dom.production.min.js"></script>
P.S.rollup 의 external 와 globals 는 조금 이상하며, key 도 value 도,また이 두 가지가配合 사용해야 한다는 것도. 더 많은 정보는 [question] Difference between externals and globals 참조
참고 자료
-
Webpack and Rollup: the same but different: 매우 흥미로운 기사, 발견이 너무 늦었음
아직 댓글이 없습니다