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。jsimport { 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.jsconst b = 'xx';const bbbbbbb = 'xx';// CONCATENATED MODULE: ./src/main.jsconsole.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。jsimport { 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.jsconst 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对比一下```javascriptoutput: {...,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.jsimport 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 TypeScriptaxios$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.jsinput: {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.jsimport {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.jsmain.jsmain-219c2eaf.js// main.jsimport './main-219c2eaf.js';// main-219c2eaf.jsconst deepCopy = function (obj) {// do ..};console.log(deepCopy(a));import('./a-19173be8.js').then(e => {console.log(e);});// a-19173be8.jsimport { 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/代码分离/懒加载)
笔者建议,最好自己上手打包 调试,得到的打包产物 并仔细分析。一时看不懂的话,也可以收藏本文,过段时间在看,先了解前置知识
最后,感谢爱学习的你,谢谢点赞!
