rollup打包产物解析及原理(对比webpack)
上篇: webpack打包产物解析及原理(含cjs/esm/代码分离/懒加载)
rollup定位
rollup比webpack晚出2年,对比webpack肯定是有差异化的
我们可以查看官网https://rollupjs.org/guide/en/#overview
得到以下几个特点
- 建议开发者使用esm写模块。
- 使用esm模块的好处就很多了:(可以参考我另一篇:https://juejin.cn/post/6959360326299025445 )
- 高版本浏览器原生支持(浏览器只有2种方法支持引入js模块,1是script标签,2就是esm模块)
- 可以做tree shaking(早期webpack版本是不支持tree shaking的)
- 可以解决循环引用的问题
- 使用esm模块的好处就很多了:(可以参考我另一篇:https://juejin.cn/post/6959360326299025445 )
- esm最终将在任何地方都可以实现(浏览器+node都可以用,是未来的标准),但 Rollup 让您今天就可以实现。
- 这句话很重要,也是rollup的特点,也是诞生的原因
- 里面有历史原因(详细可以参考我的上篇关于webpack的)
- 简单的说就是:ESM - ECMAScript 模块是未来的官方标准和主流。但是浏览器的版本需要比较高,比如chorme都需要63版本以上
- 所以rollup主要的出发点是:未来还没到,但rollup可以让你先写未来的代码(指esm)
rollup 使用流程
浏览器环境使用的应用程序的话:
- 无需考虑浏览器兼容问题的话
- 开发者写esm代码 -> rollup通过入口,递归识别esm模块 -> 最终打包成一个或多个bundle.js -> 浏览器直接可以支持引入
<script type="module">
- 开发者写esm代码 -> rollup通过入口,递归识别esm模块 -> 最终打包成一个或多个bundle.js -> 浏览器直接可以支持引入
- 需考虑浏览器兼容问题的话
- 可能会比较复杂,需要用额外的polyfill库,或结合webpack使用
打包成npm包的话:
- 开发者写esm代码 -> rollup通过入口,递归识别esm模块 -> (可以支持配置输出多种格式的模块,如esm、cjs、umd、amd)最终打包成一个或多个bundle.js
- (开发者要写cjs也可以,需要插件@rollup/plugin-commonjs)
初步看来
- (开发者要写cjs也可以,需要插件@rollup/plugin-commonjs)
- 很明显,rollup 比较适合打包js库(react、vue等的源代码库都是rollup打包的)或 高版本无需往下兼容的浏览器应用程序(现在2022年了,时间越往后,迁移到rollup会越多,猜测)
- 这样打包出来的库,可以充分使用上esm的tree shaking,使源库体积最小
举个小🌰简单的对比一下 webpack打包和rollup打包
此demo是纯esm的写法
// 入口main。js
import { b } from './test/a'
console.log(b + 1)
console.log(1111)
// './test/a'
export const b = 'xx'
export const bbbbbbb = 'xx'
rollup打包效果(非常干净,无注入代码)
const b = 'xx';
console.log(b + 1);
console.log(1111);
webpack打包效果(有很多注入代码)
- 实际上,我们自己写的代码在最下面。上面注入的大段代码 都是webpack自己的兼容代码,目的是自己实现require,modules.exports,export,让浏览器可以兼容cjs和esm语法
- (可以理解为,webpack自己实现polyfill支持模块语法,rollup是利用高版本浏览器原生支持esm(所以rollup无需代码注入))
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "./";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// CONCATENATED MODULE: ./src/test/a.js
const b = 'xx';
const bbbbbbb = 'xx';
// CONCATENATED MODULE: ./src/main.js
console.log(b + 1);
console.log(1111);
/***/ })
/******/ ]);
两者处理源代码模块的对比
纯esm | 纯cjs | 两者混用 | |
---|---|---|---|
webpack | 支持(有代码注入) | 支持(有代码注入) | 支持(有代码注入) |
rollup | 支持(无注入) | 原生不支持(需增加插件@rollup/plugin-commonjs) | 原生不支持(需增加插件@rollup/plugin-commonjs) |
rollup的初衷也是希望开发者去写esm,而不是cjs。因为esm是javascript的新标准,是未来,有很多优点,高版本浏览器也支持
两者处理对外暴露模块,非常不一样!!(解释rollup为什么适合打包库)
上面的demo 加上export 导出
// 入口main。js
import { b } from './test/a'
console.log(b + 1)
console.log(1111)
export { // 新增导出
b
}
// './test/a'
export const b = 'xx'
export const bbbbbbb = 'xx'
rollup打包 导出(非常干净,无注入代码)
- rollup本身不去做polyfill
- rollup的配置文件无需特殊配置,而且还可以支持多种模块导出(esm,cjs,umd,amd) 打包的到 esm 和 cjs
```javascript // esm const b = ‘xx’; console.log(b + 1); console.log(1111); export { b };// rollup.config.js
const OUTPUT_DIR = 'dist'
const INPUT_FILE = 'src/main.js'
export default[
// esm
{
input: INPUT_FILE,
output: {
file: OUTPUT_DIR + '/esm/index.js',
format: 'esm' // 导出esm模块
}
},
// commonjs
{
input: INPUT_FILE,
output: {
file: OUTPUT_DIR + '/cjs/index.js',
format: 'cjs' // 导出cjs模块
}
},
// umd
{
input: INPUT_FILE,
output: {
file: OUTPUT_DIR + '/umd/index.js',
format: 'umd' // 导出umd模块
}
},
]
// cjs const b = ‘xx’; console.log(b + 1); console.log(1111); exports.b = b;
// umd (兼容3种写法:cjs,amd,global(global可以初步理解为直接通过window传值)) (function (global, factory) { typeof exports === ‘object’ && typeof module !== ‘undefined’ ? factory(exports) : typeof define === ‘function’ && define.amd ? define([‘exports’], factory) : (global = typeof globalThis !== ‘undefined’ ? globalThis : global || self, factory(global.aa = {})); })(this, (function (exports) { ‘use strict’; const b = ‘xx’; console.log(b + 1); console.log(1111); exports.b = b; Object.defineProperty(exports, ‘__esModule’, { value: true }); }));
webpack 导出 (区别巨大,注入代码较多,导出esm支持的不太好)
- webpack需配置 (此处是 webpack 4.x) <br />webpack暂时只能支持导出 cjs 或 更往前兼容的包(umd)<br />**不支持esm(实验性)**<br /><br />我们此处导出 cjs的包, 和rollup对比一下
```javascript
output: {
...,
library: 'myLib', // 暴露出去的变量的名字
libraryTarget: 'commonjs',
}
- 注入代码特别多,比较冗余 ```javascript exports[“myLib”] = /**/ (function(modules) { // webpackBootstrap /**/ // The module cache /**/ var installedModules = {}; /**/ /**/ // The require function /**/ function webpack_require(moduleId) { /**/ /**/ // Check if module is in cache /**/ if(installedModules[moduleId]) { /**/ return installedModules[moduleId].exports; /**/ } /**/ // Create a new module (and put it into the cache) /**/ var module = installedModules[moduleId] = { /**/ i: moduleId, /**/ l: false, /**/ exports: {} /**/ }; /**/ /**/ // Execute the module function /**/ modules[moduleId].call(module.exports, module, module.exports, webpack_require); /**/ /**/ // Flag the module as loaded /**/ module.l = true; /**/ /**/ // Return the exports of the module /**/ return module.exports; /**/ } /**/ /**/ /**/ // expose the modules object (webpack_modules) /**/ webpack_require.m = modules; /**/ /**/ // expose the module cache /**/ webpack_require.c = installedModules; /**/ /**/ // define getter function for harmony exports /**/ webpack_require.d = function(exports, name, getter) { /**/ if(!webpack_require.o(exports, name)) { /**/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /**/ } /**/ }; /**/ /**/ // define esModule on exports /**/ webpackrequire.r = function(exports) { /**/ if(typeof Symbol !== ‘undefined’ && Symbol.toStringTag) { /**/ Object.defineProperty(exports, Symbol.toStringTag, { value: ‘Module’ }); /**/ } /**/ Object.defineProperty(exports, ‘esModule’, { value: true }); /**/ }; /**/ /**/ // create a fake namespace object /**/ // mode & 1: value is a module id, require it /**/ // mode & 2: merge all properties of value into the ns /**/ // mode & 4: return value when already ns object /**/ // mode & 8|1: behave like require /**/ webpack_require.t = function(value, mode) { /**/ if(mode & 1) value = webpack_require(value); /**/ if(mode & 8) return value; /**/ if((mode & 4) && typeof value === ‘object’ && value && value.esModule) return value; /**/ var ns = Object.create(null); /**/ webpackrequire.r(ns); /**/ Object.defineProperty(ns, ‘default’, { enumerable: true, value: value }); /**/ if(mode & 2 && typeof value != ‘string’) for(var key in value) webpackrequire.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /**/ return ns; /**/ }; /**/ /**/ // getDefaultExport function for compatibility with non-harmony modules /**/ webpackrequire.n = function(module) { /**/ var getter = module && module.esModule ? /**/ function getDefault() { return module[‘default’]; } : /**/ function getModuleExports() { return module; }; /**/ webpack_require.d(getter, ‘a’, getter); /**/ return getter; /**/ }; /**/ /**/ // Object.prototype.hasOwnProperty.call /**/ webpack_require.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /**/ /**/ // webpack_public_path /**/ webpack_require.p = “./“; /**/ /**/ /**/ // Load entry module and return exports /**/ return webpack_require(webpack_require.s = 0); /**/ }) /**/ /**/ ([ / 0 / /*/ (function(module, __webpack_exports, __webpack_require) {
“use strict”; // ESM COMPAT FLAG webpack_require.r(webpack_exports);
// EXPORTS 这一行是处理esm的导出,因为我们用的是 export { b: xx }, 如果我们用cjs的导出 比如 module.exports = { b: xx }, 此处就会没有,会更简单,直接是 module.exports = { b: xx } webpack_require.d(webpack_exports, “b”, function() { return / reexport / b; });
// CONCATENATED MODULE: ./src/test/a.js const b = ‘xx’; const bbbbbbb = ‘xx’;
// CONCATENATED MODULE: ./src/main.js console.log(b + 1); console.log(1111);
// }) /*/ ]);
注意看 倒数第10多行,有个
// EXPORTS webpack_require.d(webpack_exports, “b”, function() { return / reexport / b; });
这一行是处理esm的导出,因为我们用的是 export { b: xx }, 如果我们用cjs的导出 比如 module.exports = { b: xx }, 此处就会没有此行,会更简单,直接是 module.exports = { b: xx } ( webpack会自己模拟实现 module.exports )
<a name="fd14a4e6"></a>
## 为什么webpack需要注入这么多代码?
因为webpack比rollup早出2年,诞生在esm标准出来前,commonjs出来后
- 当时的浏览器只能通过script标签加载模块
- **script标签加载代码是没有作用域的,只能在代码内 用iife的方式 实现作用域效果**,
- **这就是webpack打包出来的代码 大结构都是iife的原因**
- 并且**每个模块都要装到function里面**,才能保证互相之间作用域不干扰。
- **这就是为什么 webpack打包的代码为什么乍看会感觉乱,找不到自己写的代码的真正原因**
- 关于webpack的代码注入问题,是因为**浏览器不支持cjs**,所以**webpack要去自己实现require和module.exports方法**(才有很多注入)(webpack自己实现polyfill)
- 这么多年了,甚至到现在2022年,**浏览器为什么不支持cjs**?
- **cjs是同步的,运行时的,node环境用cjs,node本身运行在服务器,无需等待网络握手,所以同步处理是很快的**
- **浏览器是 客户端,访问的是服务端资源,中间需要等待网络握手,可能会很慢,所以不能 同步的 卡在那里等服务器返回的,体验太差**
- **后续出来esm后,webpack为了兼容以前发在npm上的老包**(并且当时心还不够决绝,导致这种“丑结构的包”越来越多,以后就更不可能改这种“丑结构了”),所以保留这个iife的结构和代码注入,**导致现在看webpack打包的产物,乍看结构比较乱且有很多的代码注入,自己写的代码都找不到**
rollup诞生于esm标准出来后,就是针对esm设计的,也没有历史包袱,所以可以做到真正的“打包”(精简,无额外注入)
- (根据npm版本上传显示最早上传时间: **webpack是2013年左右,rollup是2015.5**)
<a name="3c0fc8b2"></a>
## rollup如何打包第三方依赖 和 懒加载模块 和 公共模块?
和webpack打包一样,有两种:单chunk包 和 多chunk包
1. 单chunk包<br />无额外配置,一般会把所有js打成一个包。打包外部依赖(第三方)也是一样的。比如: <br />**此处rollup打包有个注意点**:
```javascript
// 入口 main.js
import Axios from 'axios'
Axios.get()
console.log(1111)
------ 打包后的结果 ------
// 最终会把axios的源代码 和 main.js 主代码,打包到一个文件内,无额外代码注入
// 以下是截取了一头一尾,中间省略
import require$$1$1 from 'http';
import require$$2 from 'https';
import require$$0$1 from 'url';
import require$$3 from 'assert';
import require$$4 from 'stream';
import require$$0 from 'tty';
import require$$1 from 'util';
import require$$7 from 'zlib';
var axios$1 = {exports: {}};
var bind$2 = function bind(fn, thisArg) {
return function wrap() {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
};
};
...
...
...
axios$1.exports = axios;
// Allow use of default import syntax in TypeScript
axios$1.exports.default = axios;
var _axios_0_18_1_axios = axios$1.exports;
_axios_0_18_1_axios.get();
console.log(1111);
- 很多第三方依赖很早就有了,所以用的是commonjs模块导出,rollup打包的话,需要安装插件@rollup/plugin-node-resolve。因为是cjs的包,所以也不存在tree shaking
- 插件原理是,把cjs的包,转成esm包,在打包
- 现在比较流行的monorepo,就是完全用esm写库+rollup打包,可以很轻易的做到tree shaking,让核心库变的更小,解析速度更快,还可以对外提供工具,扩大影响力
- 多个chunk包(代码分离)
配置多个入口,此法比较简单,可自行测试
// rollup.config.js
input: {
index: 'src/main.js',
other: 'src/other.js',
},
代码分离 (动态import,懒加载, import(xxx).then(module => {}) )
- 此处有一个官方的例子,再清楚不过了
对于代码分割,还有一种方法可以通过 output.manualChunks 选项显式告诉 Rollup 哪些模块要分割成单独的块。
总结: ```javascript // 入口 main.js / DYNAMIC IMPORTS 动态import Rollup supports automatic chunking and lazy-loading Rollup支持自动分块和懒加载 via dynamic imports utilizing the import mechanism 通过dynamic imports动态导入 of the host system. / if (displayMath) { import(‘./maths.js’).then(function (maths) {
}); }console.log(maths.square(5));
console.log(maths.cube(5));
// ‘./maths.js’ import square from ‘./square.js’; export {default as square} from ‘./square.js’; export function cube (x ) { return square(x) * x; }
// ‘./square.js’ export default function square ( x ) { return x * x; }
———————— 打包结果 ———————— // main.js ‘use strict’; / DYNAMIC IMPORTS 动态import Rollup supports automatic chunking and lazy-loading Rollup支持自动分块和懒加载 via dynamic imports utilizing the import mechanism 通过dynamic imports动态导入 of the host system. / if (displayMath) { // 打包成cjs模块的话,import替换成 Promise + require // Promise.resolve(require(‘../chunk-0ee5c472.js’)).then(function (maths) { import(‘../chunk-c4d97f01.js’).then(function (maths) { console.log(maths.square(5)); console.log(maths.cube(5)); }); }
// ‘../chunk-0ee5c472.js’ ‘use strict’; function square ( x ) { return x x; } function cube (x ) { return square(x) x; } exports.cube = cube; exports.square = square;
- **动态import,rollup对比webpack 打包后的模块格式的支持度**
| 打包后的模块格式: | esm | cjs | amd | umd |
| --- | --- | --- | --- | --- |
| webpack | 不支持,实验中 | 支持 | 支持 | 支持 |
| rollup | 支持 | 支持 | 支持 | 不支持 |
- 实现原理,对比webpack:
- webpack是**自己实现的“动态import“**(借助promise + script标签 + window对象 + 模拟import方法)
- rollup是 (打包成esm模块)利用浏览器(chorme63 以上)天然支持[动态import](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/import#%E5%8A%A8%E6%80%81import)
- 或 (打包成cjs模块)promise + cjs的require
- **此处有个很重要细节点**
- rollup打的包,如果要用 动态import(现在vue和react的单页项目 特别流行用动态import加载路由,算硬需求了),**注意 如果要在浏览器上跑,首先要是esm的包(浏览器不支持cjs),然后浏览器版本要注意(chorme63 以上)**
- 因为rollup不做额外代码注入,完全利用高版本浏览器原生支持import(所以代码特别干净,webpack会做大量的兼容 自己实现require和import)<br />
3. rollup如何处理公共模块?(比如, a、b、c 3个模块 同时依赖 d)<br />**有2种情况:**
1. 源代码内 **不存在 动态import**,那么会打成**一个chunk**(a、b、c、d 4个模块都在一包内,d只正常有一份)
1. 源代码内 **存在 懒加载模块,并且懒加载的模块也访问了公共依赖**,比如 <br />总结:**对于公共依赖,rollup不会出现重复打包的情况!并且完全无注入代码!无需额外配置。** 对比webpack的话,webpack需要配置 optimization.splitChunks (webpack4.x 以上)
```javascript
// 入口 main.js
import {deepCopy} from '@xxx/methods/deepCopy.js' // 这是放在公司的npm域内的一个包,可以理解为export一个简单的deepCopy函数
console.log(deepCopy(a))
import('./test/a').then(e => {
console.log(e)
})
// './test/a' 懒加载模块 也依赖 同一公共模块
import {deepCopy} from '@xxx/methods/deepCopy.js'
const a = {a: 1}
export const b = deepCopy(a)
---------- 是否会把 公共依赖 打包2份呢? --------------
答案是no,rollup还是牛p,公共依赖只会出来一份,然后对外 export (此处举例是导出esm格式, 亲测导出cjs格式一样的可以,此处就不赘述,有兴趣可以自己test一下)
生成的目录结构,有3个文件
a-19173be8.js
main.js
main-219c2eaf.js
// main.js
import './main-219c2eaf.js';
// main-219c2eaf.js
const deepCopy = function (obj) {
// do ..
};
console.log(deepCopy(a));
import('./a-19173be8.js').then(e => {
console.log(e);
});
// a-19173be8.js
import { d as deepCopy } from './main-219c2eaf.js';
const a = {a: 1};
const b = deepCopy(a);
export { b };
总结 rollup vs webpack
rollup 诞生在esm标准出来后
- 出发点就是希望开发者去写esm模块,这样适合做代码静态分析,可以做tree shaking减少代码体积,也是浏览器除了script标签外,真正让JavaScript拥有模块化能力。是js语言的未来
- rollup完全依赖高版本浏览器原生去支持esm模块,所以无额外代码注入,打包后的代码结构也是清晰的(不用像webpack那样iife)
- 目前浏览器支持模块化只有3种方法:
- ①script标签(缺点没有作用域的概念)
- ②script标签 + iife + window + 函数作用域(可以解决作用域问题。webpack的打包的产物就这样)
- ③esm (什么都好,唯一缺点 需要高版本浏览器)
- 目前浏览器支持模块化只有3种方法:
webpack 诞生在esm标准出来前,commonjs出来后
- 当时的浏览器只能通过script标签加载模块
- script标签加载代码是没有作用域的,只能在代码内 用iife的方式 实现作用域效果,
- 这就是webpack打包出来的代码 大结构都是iife的原因
- 并且每个模块都要装到function里面,才能保证互相之间作用域不干扰。
- 这就是为什么 webpack打包的代码为什么乍看会感觉乱,找不到自己写的代码的真正原因
- script标签加载代码是没有作用域的,只能在代码内 用iife的方式 实现作用域效果,
- 关于webpack的代码注入问题,是因为浏览器不支持cjs,所以webpack要去自己实现require和module.exports方法(才有很多注入)
- 这么多年了,甚至到现在2022年,浏览器为什么不支持cjs?
- cjs是同步的,运行时的,node环境用cjs,node本身运行在服务器,无需等待网络握手,所以同步处理是很快的
- 浏览器是 客户端,访问的是服务端资源,中间需要等待网络握手,可能会很慢,所以不能 同步的 卡在那里等服务器返回的,体验太差
- 这么多年了,甚至到现在2022年,浏览器为什么不支持cjs?
- 后续出来esm后,webpack为了兼容以前发在npm上的老包(并且当时心还不够决绝,导致这种“丑结构的包”越来越多,以后就更不可能改这种“丑结构了”),所以保留这个iife的结构和代码注入,导致现在看webpack打包的产物,乍看结构比较乱且有很多的代码注入,自己写的代码都找不到
最终使用推荐
- 打包开源库:不用思考,rollup会是你更好的选择
- rollup本身也支持很多插件,生态也成熟,各种场景几乎都能照顾到
- 打包应用程序:个人推荐,看您的 应用程序 需不需要兼容老浏览器
兼容表如下(其实就是 动态import的兼容表 ) 以chorme为例,需要chorme63以上
如果不考虑兼容老浏览器,建议用 vite 开发应用程序(vite官网(react/vue/ts都支持) )- dev开发方面:vite提供dev服务器,以及比webpack快的多的热更新,dev开发的体验更好了
- prd生产方面:vite 打生产包,实际上用的就是rollup,笔者用vite已经上过真实项目,开发体验很棒,打的生产包比用webpack小了很多,有不错的性能提升
- vite的优点和特点,可以看我另一篇:vite原理浅析-dev篇(对比webpack)
- 理论上 chorme63以上 可以开箱即用,chorme63以下也不是完全不能用,需要自己加polyfill或vite插件(vite推荐的兼容做法 )
篇幅有点长,最好先对webpack有充分的了解,在看此篇,会更好理解 和 全面对比。了解webpack可以先看笔者的上篇 webpack打包产物解析及原理(含cjs/esm/代码分离/懒加载)
笔者建议,最好自己上手打包 调试,得到的打包产物 并仔细分析。一时看不懂的话,也可以收藏本文,过段时间在看,先了解前置知识
最后,感谢爱学习的你,谢谢点赞!