1. 说说你对 Webpack 的理解?它解决了什么问题?
Webpack 是一个现代的 JS 应用程序的静态模块打包器。它主要做的事情就是:分析你的项目结构,找到 JavaScript 模块以及其他的一些浏览器不能直接运行的扩展语言(Sass TS 等)。并将其打包为合适的格式以供浏览器使用。
- Webpack 的主要功能:
- 代码转换
- 文件优化
- 代码分割
- 模块合并
- 自动刷新
- 代码校验
- 自动发布等等
早期模块化?
把单独的功能抽离到单独的
js文件,通过script引入。出现的问题:模块都在全局中,大量模块污染环境,并且模块与模块之间没有依赖关系,维护困难,没有私有空间等问题
解决方案:出现了
命名空间方式,规定每个模块只暴露一个全局对象,模块的内容都挂载在这个对象中; ---》还是没有解决第一种方式的依赖等问题。再后来,使用立即执行函数模块提供私有空间,通过参数的形式作为依赖声明; ---》这种方式还是存在一些问题。比如:通过 script 引入模块,这些模块的加载并不受代码的控制理想的解决方式:在页面中引入一个
JS入口文件,其余用到的模块可以通过代码控制,按需加载进来除了 模块加载 的问题以外,还需要规定
模块化的规范。如今流行的:CommonJS、ES Module从前后端渲染的 JSP、PHP。到前端原生 JavaScript。再到 jQuery 开发。再到目前三大框架 Vue, React, Angular 开发。也从 JavaScript 到后面的 es5,6,7,8...。再到 TypeScript。有些编写的 CSS 预处理器 less、sass 等。如今的前端变得十分复杂,所以我们开发过程中会遇到以下问题:
- 项目需要通过模块化的方式来开发
- 使用一些高级的特性来加快我们的开发效率,如:ES6+、TypeScript 开发脚本逻辑,通过 Less、Sass 等方式来编写 css 样式代码
- 监听文件的变化并且反映到浏览器上,提高开发效率
- JS 代码需要模块化,HTML 和 CSS 这些资源文件有些也需要模块化
- 开发完后我们需要将代码压缩、合并以及一些优化等问题
综合:Webpack 恰巧可以解决以上问题!
扩展:webpack -> 垫片 Shimming
比如,我们使用 jQuery 中的$符号时,浏览器不认识($ is not defined)。我们可以使用 webpack 内置插件 ProvidePlugin({\$: 'jquery'})。模块中使用了$就自动引入jquery,并将jquery赋值给$.这种方式就叫垫片 Shimming
2. Webpack 的构建流程?
- 初始化参数:解析
webpack配置参数,合并shell传入和webpack.config.js文件配置的参数, 形成最后的配置结果 - 开始编译:上一步得到的参数初始化
Compiler对象,注册所有配置的插件,插件会监听webpack构建生命周期的事件节点,做出相应的反应。然后执行run方法开始执行编译 - 确认入口:根据配置的
entry入口,开始解析文件构建AST(抽象语法树),找出依赖,递归下去 - 编译模块:递归中根据文件类型和
loader配置,调用所有配置的loader对文件进行转换,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理 - 完成模块编译并输出资源:递归完事后,得到每个文件结果,包含每个模块以及他们之间的依赖关系,根据
entry或分包配置生成代码块chunk - 输出完成:输出所有的
chunk到文件系统

3. 常见的 Loader、Plugin 有哪些?能手写吗?
Plugin 的理解
Plugin 就是一个扩展器,它比 Loader 更加灵活,因为它可以接触到 Webpack 编译器。在 Webpack 运行的生命周期中会广播出许多的事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。这样 Plugin 就可以通过一些 hook 函数来拦截 Webpack 的执行,做一些 Webpack 打包之外的事情。像:打包优化、资源管理、注入环境变量等等。
插件实例上都会有个 apply 方法,并将 compiler 作为其参数。(类似于:Vue 插件都有个 install 方法)
在开发 Plugin 时最常用的两个对象 Compiler 和 Compilation,它们都继承自Tapable,是 Plugin 和 Webpack 之间的桥梁。类似于 react-redux 是连接 React 和 Redux 的桥梁。(Tapable 有同步钩子和异步钩子(异步串行钩子 和 异步并行钩子))
注册钩子的方式:同步(tap 注册 -》 call 执行);异步(tap -》call、tapAsync -》callAsync、tapPromise -》promise)
通过 schema-utils 验证 options 的合法性
常见的
Plugin:- html-webpack-plugin:可以根据模板自动生成 html 代码,并自动引用 css 和 js 文件
- extract-text-webpack-plugin:将 js 文件中引用的样式单独抽离成 css 文件(webpack4 推荐使用 mini-css-extract-plugin)
- 两者有啥区别?
- 后者:更容易使用、异步加载、而且只针对 CSS,并且不重复编译,性能更好
- 该插件一般在(生产环境)使用。代替 loaders 中的 style-loader,暂时不支持 HMR
- 两者有啥区别?
- clean-webpack-plugin: 清理每次打包的文件
- speed-measure-webpack-plugin: 可以看每个 Loader 和 Plugin 执行耗时(webpack5 使用 speed-measure-webpack5-plugin)
- webpack-bundle-analyzer: 可视化 Webpack 输出文件的体积
- copy-webpack-plugin:拷贝插件
- friendly-errors-webpack-plugin: 识别某些类别的 webpack 错误,并清理,聚合和优先级,以提供更好的开发人员体验
- webpack 中提供了 stats 选项显示打包信息(只展示错误信息、都展示等等)
- optimize-css-assets-webpack-plugin:压缩 css
- purgecss-webpack-plugin: 去除无用的 css
- uglifyJs-webpack-plugin:压缩 js (压缩 es6 的代码不是很友好;并且是单线程压缩代码,打包时间慢,所以开发环境将其关闭,生产环境打开(parallelUglifyPlugin 开启多个子进程打包,每个子进程还是 UglifyJS 打包,但并行执行)-》webpack4 推荐用 terser-webpack-plugin(开启 parallel 参数【一般是电脑的 CPU 核数减 1】,使用进程压缩);此插件 webpack5 中内置了)
- compression-webpack-plugin:(生产环境可采用)gzip 压缩 JS 和 CSS【需要后台配置 nginx】
- HotModuleReplacementPlugin:热更新(自带的)
- happypack:开启多进程打包,提高打包速度(不维护了)
- ProvidePlugin:自动加载模块,代替 require 和 import(自带的)
- DefinePlugin:定义全局变量(浏览器获取的值,需使用 JSON.stringify 包裹)
- IgnorePlugin:忽略或排除(moment 不用全部加载,只加载中文)
- DllPlugin:动态链接库,配合 DllReferencePlugin 一起使用(自带的)
Loader 的理解
就是一个 代码转码器,对各种资源进行转换。它的特点:单一原则,每个 loader 只做对应的事情。它的执行顺序:从右到左,从下到上。有几种分类:pre、normal(默认)、inline、post。Loader 就是一个函数,接受原始资源作为参数,输出进行转换后的内容。
loader 的执行分为两个阶段:Pitch 阶段和 Normal 阶段。loader 会先执行 pitch,然后获取资源再执行 normal loader。如果 pitch 有返回值时,就不会走之后的 loader,并将返回值返回给之前的 loader。这就是为什么 pitch 有 熔断 的作用!
loader-utils 中 getOptions(this) 方法用来获取 loader 中 options 的配置
schema-utils 中 validate 方法用来验证 loader 中 options 的配置是否合法 {type: 'object', properties: {}, additionalProperties: true}
loader 分为同步(return 或 this.callback(null, source, map, meta)两种方式)和 异步(this.async())
常见的 Loader:
- file-loader:
- url-loader:
- babel-loader:
- css-loader:
- style-loader:
- eslint-loader:
- cache-loader:
- less-loader、sass-loader、styles-loader:
- image-webpack-loader:压缩图片
- postcss-loader、autoprefixer-loader
Compiler上有哪些钩子? environment、run、make、emit、afterEmit、done、thisCompilation(初始化 compilation 时调用) 等等Compilation上有哪些钩子?
4. 如何提高 Webpack 的构建速度?
优化webpack构建的方式有很多,主要可以从优化搜索时间、缩小文件搜索范围、减少不必要的编译等方面入手:
- 多线程/多实例构建:
HappyPack(不维护了)、thread-loader - 优化
loader的配置include和exclude- 配置
babel-loader时,可以配置cacheDirectory开启缓存
- 合理的使用
resolve.extensionsextensions: [".js",".json"]
- 优化
resolve.modules- 用于配置
webpack去哪些目录下寻找第三方模块。默认值为['node_modules'],配置了可以减少查找路径
- 用于配置
- 优化
resolve.alias"@":path.resolve(\_\_dirname,'./src')减少查找过程
- 使用
DllPlugin插件- 打包成一个
Dll库,webpack.DllPlugin()-> 生成mainfest.json文件 - 引入
Dll库,webpack.DllReferencePlugin()
- 打包成一个
- 使用
cache-loader- 针对一些开销较大的
loader前添加cache-loader,将其结果缓存到磁盘里,显著提升二次构建速度(保存和读取这些缓存文件会有一些时间开销,只针对一些开销大的loader) use:['cache-loader', ...loaders]
- 针对一些开销较大的
terser开启多线程- 使用多进程并行运行来提高构建速度
optimization:{ minimizer: [new TerserPlugin({ parallel: true })] }
- 合理使用
sourceMap
5. Webpack4 和 Webpack5 有哪些区别?
压缩代码
内部自带
terser-webpack-plugin插件(生产环境自动开启压缩)【webpack4需要独立安装】// webpack5开发环境启动压缩 const TerserPlugin = require('terser-webpack-plugini') module.exports = { optimization: { usedExports: true, // 只导出被使用的模块 minimize: true, // 启动压缩 minimizer: [new TerserPlugin()], }, }扩展:开发模块开启
Tree Shaking的方式?【生产环境自动开启】optimization.sideEffects: true; // 开启 (package.json里也要配置:'sideEffects: false | true | []')Tree-shaking机制的原理?treeShaking也叫摇树优化,是指在打包中去除那些引入了,但是在代码中没有被用到的死代码。从而来优化打包体积。生产环境默认开启- 它可以在
代码不运行的状态下,分析出不需要的代码 - 利用
es6模块的规范ES6 Module引入进行静态分析,故而编译的时候正确判断到底加载了那些模块 Tree-shaking的实现:- 先标记出模块导出值中哪些没有被用过
- 使用
Terser删掉这些没被用到的导出语句 - 标记过程大致可划分为三个步骤:
Make 阶段,收集模块导出变量并记录到模块依赖关系图ModuleGraph变量中Seal 阶段,遍历ModuleGraph标记模块导出变量有没有被使用生成产物时,若变量没有被其它模块使用则删除对应的导出语句
扩展:
CSS可以Tree-Shaking吗?在
webpack中Tree-Shaking是通过uglifyJSPlugin来Tree-Shaking JS的。CSS需要使用Purify-CSS[不在维护],使用purgecss-webpack-plugin,mini-css-extract-plugin提取 css 插件配合使用。
缓存配置
webpack4通过hard-source-webpack-plugin缓存webpack5内置了cache缓存机制
module.exports = { // 使用持久化缓存 cache: { type: 'filesystem', cacheDirectory: path.join(__dirname, 'node_modules/.cache/webpack'), }, }扩展:什么是长缓存?
在
webpack中可以在output选项中指定hash, 通过HashedModuleIdsPlugin或者NamedModulesPlugin来生成唯一的moduleId,保证没有改动的文件hash值不变注意:
cache在开发模式默认设置成type: memory; 【生产模块把cache给禁用掉了】启动服务的差别
webpack4启动服务 ->webpack-dev-serverwebpack5启动服务使用内置的webpack serve启动
输出代码
webpack4只能输出es5的代码webpack5新增属性output.ecmaVersion,可以生成es5和es6的代码
代码分割
webpack4将超过30kb文件单独提为一个chunk(minSize: 30000)webpack5可以区分是js还是css,精准划分(minSize: {javascript: 30000, css: 50000})
模块联邦
Module Federationdevtool 差别
webpack4有 13 种webpack5有 26 种扩展:
webpack4一般开发环境配置cheap-eval-module-source-map,在生产用none;webpack5 使用eval-cheap-module-source-map
6. Grunt、Gulp、Webpack、Rollup、Vite 的比较?
模块化管理工具 和 自动化构建工具 是不同的。两者主要的侧重点不一样。自动化构建工具 则侧重于 前端开发的整个过程 的控制管理(像流水线)。而模块化管理工具更侧重于 模块打包。可以把开发中的所有资源(图片、js、css 文件等)都当成 模块
Webpack: 是当前最流行的
模块化管理工具和打包工具。其通过 loader 的转换,可以将任何形式的资源都当成模块。它还可以将各个模块通过其 依赖关系 打包成符合生产环境部署的前端资源。它还可以将应用程序分解成可管理的代码块,可以按需加载打包原理:解析各个模块的依赖关系,使用 loader 转换文件,使用 plugin 注入钩子,打包合并模块,最终生成 bundle 文件,使用 express 开启本地服务器,浏览器请求的是 bundle 文件
- 优点:
- 基本之前 gulp 可以操作的,webpack 都可以做
- 同时支持
热更新、tree-shaking、Scope Hoisting、动态加载、代码拆分、文件指纹、代码压缩、静态资源处理等 - 支持多种打包方式
- 缺点:
- 各个模块之间的依赖关系过于复杂 会导致打包速度很慢
- 使用热更新时,改动一个模块,其他有依赖关系的模块也会重新打包
- 不支持打包出 esm 格式的代码(打包后的代码再次被引用时 tree shaking 困难),打包后亢余代码较多
- 优点:
Vite:和 webpack 差不多,vite 是当下新型的
模块化管理工具和打包工具。它本地启动速度比 webpack 快了很多。但是 vite 还完成没有替换 webpack 的能力,不管是从社区还是从能力来说,vite 本身还是太过脆弱,它的产生和火热完成依赖于 vue 本身的热度打包原理:就是启动一个
koa服务器拦截由浏览器请求的 ESM 的请求,通过请求的路径找到目录下对应的文件做一定的处理,最终以 ESM 的格式返回给客户端
Vite1.x使用koa,Vite2.x使用connectVite 作为一个基于浏览器原生的 ESM 的构建工具,它省略了开发环境的打包过程,利用浏览器去解析 import,在服务端按需编译返回。同时,开发环境模块热更新也非常快,不会随着模块的增多而变慢。
Vite 的主要特性:
- Instant Server Start —— 即时服务启动
- Lightning Fast HMR —— 闪电般快速的热更新
- Rich Features —— 丰富的功能
- Optimized Build —— 经过优化的构建
- Universal Plugin Interface —— 通用的 Plugin 接口
- Fully Typed APIs —— 类型齐全的 API
缺点:
- 项目的开发浏览器要支持
es module, 要求浏览器比较新【@vitejs/plugin-legacya】插件打包出一个兼容性比较好的版本 - 生产环境使用 rollup 打包会造成开大环境与生产环境不一致
- 生态没有
webpack丰富 - 很多第三方 SDK 没有产出 ems 格式的代码,这就需要我们自己去做一些兼容
对于一些 没有产出 esm 的模块,如何去兼容呢?
业界是有一些如
lebab的方法可以将commjs代码快速转化为esm的,但是对于一些格式不规范的代码,可能还是需要单独处理。- 项目的开发浏览器要支持
构建工具的发展:Browserify -> Gulp -> Parcel -> Webpack -> Rollup -> (后来的非打包)Snowpack 和 Vite
注意:在
Vite中约定若path的请求路径满足/^\/@modules\//格式时,被认为是一个node_modules模块。Vite 的热加载原理,其实就是在客户端与服务端建立了一个 websocket 连接,当代码被修改时,服务端发送消息通知客户端去请求修改模块的代码,完成热更新。
- 服务端:服务端做的就是监听代码文件的改变,在合适的时机向客户端发送 websocket 信息通知客户端去请求新的模块代码。
- 客户端:Vite 中客户端的 websocket 相关代码在处理 html 中时被写入代码中。可以看到在处理 html 时,vite/client 的相关代码已经被插入。
Vite 的实现核心:
- 实现静态服务功能 ->
koa-static - 解析 import 语法 重写路径 ->
es-module-lexerparse 方法 - 解析 以/@modules 文件开头的内容 找到对应的结果,设置响应类型 type='js'返回给客户端
- 处理 html 不存在 process
- 解析.vue 文件
- 实现静态服务功能 ->
Rollup:是下一代 ES6 模块打包工具,可以将我们按照 ESM(ES2015 Module)规范编写的源码构建输出如下格式:
- IIFE:自执行函数,可通过 script 标签加载
- AMD:通过 requirejs 加载
- CommonJS:Node 默认的模块规范,可通过 webpack 加载
- UMD:兼容 IIFE、AMD、CJS 三种模块规范
- ESM:ES2015 Module 规范,可用 webpack,rollup 加载
优点:
- 支持动态导入
- 支持 tree-shaking。仅加载模块里用得到的函数以减少文件大小
- Scope Hoisting。rollup 可以将所有的小文件生成到一个大文件中,所有代码都在同一个函数作用域里;不会像 webpack 那样用很多函数来包装模块
- 没有其他冗余代码, 执行很快。除了必要的
cjs,umd头外,bundle 代码基本和源码差不多,也没有奇怪的__webpack_require__,Object.defineProperty之类的东西
缺点:
- 不支持热更新功能
- 对于 commonjs 模块,需要额外的插件将其转化为 es2015 供 rollup 处理
- 无法公共代码拆分
适用场景:开发第三方库、生成单一的 umd 文件的场景
比较 Webpack:
- Rollup 目前还不支持代码拆分(Code Splitting)和模块的热更新(HMR)
- 一般,对于应用开发使用 Webpack,对于类库开发使用 Rollup
- 需要代码拆分(Code Splitting),或者很多静态资源需要处理,再或者构建的项目需要引入很多 CommonJS 模块的依赖时,使用 webpack。代码库是基于 ES6 模块,而且希望代码能够被其他人直接使用,使用 Rollup
- React 已经将构建工具从 Webpack 换成了 Rollup
Gulp:是基于 流 的 前端自动化构建工具,采用代码优于配置的策略,更容易学习和使用,它让简单的任务简单,复杂的任务复杂
- 优点:
- gulp 文档简单,学习成本低,使用也比较简单
- 对大量源文件可以进行流式处理,借助插件,可以对文件类型进行多种操作处理
- 缺点:
- 不支持 tree-shaking、热更新、代码拆分等
- gulp 对 js 模块化方案无能为力,只是对静态资源做流式处理,处理之后并未做有效的优化整合
适用场景:静态资源密集操作型场景,主要用于 css、图片等静态资源的处理操作
比较 grunt:
- 易用,Gulp 相比于 Grunt 更简洁,而且遵循代码优于配置策略,维护 Gulp 更像是写代码
- 高效,Gulp 核心设计是基于 Unix 的流的概念,通过管道连接,不需要写中间文件
- 易学,Gulp 的核心 API 只有 5 个,之后可以通过管道流组合自己想要的任务
- 流:使用 Grunt 的 I/O 过程中会产生一些中间态的临时文件,一些任务生成临时文件,其它任务可能会基于临时文件再做处理并生成最终的构建后文件。而使用 Gulp 的优势就是利用流的方式进行文件的处理,通过管道将多个任务和操作连接起来,因此只有一次 I/O 的过程,流程更清晰,更纯粹
- 代码优于配置:维护 Gulp 更像是写代码,而且 Gulp 遵循 CommonJS 规范,因此跟写 Node 程序没有区别
- 优点:
Grunt:是一套 前端自动化工具,帮助处理反复重复的任务。一般用于:编译、压缩、合并文件,简单语法检查等
- 特点:
- Grunt 有一个完成的社区,插件丰富
- 它简单易学,你可以随便安装插件并配置它们
- 特点:
Webpack 的定位是模块打包器,而 Gulp/Grunt 属于构建工具
7. 了解热更新原理吗?它是如何做到的?说说其原理?
开启了 express 应用,添加了对 webpack 编译的监听,添加了和浏览器的 websocket 长连接,当文件变化触发 webpack 进行编译并完成后,会通过 socket 告诉浏览器准备刷新。而为了减少刷新的代价,就是不用刷新页面,而是刷新某个模块,webpack-dev-server 可以支持热更新,通过生成文件的 hash 来对比需要更新的模块,浏览器再进行热替换
扩展:hash、chunkhash、contenthash 三者的区别?
hash 一般是结合 CDN 缓存来使用的
- hash:是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的 hash 值都会更改,并且全部文件都公用相同的 hash 值(每一次构建都会生成新的 hash 值(不管文件是否有改动)-》导致没有办法实现缓存效果)
- chunkhash:和 hash 不一样,它根据不同的入口文件(entry)进行依赖文件解析,构建对于的 chunk,生成对应的哈希值。-》(同一个 chunk 的一个依赖改变了,其他的依赖哈希值也会变)
- contenthash:针对的是对应的内容是否改变
如何避免相同的随机值? webpack 在
计算hash后分割chunk。产生相同随机值可能是因为这些文件属于同一个chunk,可以将某个文件提到独立的chunk(如放入entry)扩展:CDN 原理?
- 用户输入域名,首先经过 dns 解析,请求 cname 指向的那台 cdn 专用的 dns 服务器
- dns 服务器返回全局负载均衡的服务器 IP 给用户
- 用户请求全局负载均衡服务器,服务器根据 IP 返回所在区域的负载均衡服务器 IP 给用户
- 用户请求区域负载均衡服务器,服务器根据 IP 选择距离最近的,负载比较合适的一台缓存服务器 IP 给用户。(当没有对应内容时,会去上一级缓存服务器去找,直到找到资源所在的服务器,并且缓存在缓存服务器中,供下次访问)
域名 -> dns 解析 -> 全局负载均衡的服务器 -> 所在区域的负载均衡服务器 -> 缓存服务器
- CDN 回溯:就是
CDN发现自己没有这个资源(一般是缓存的数据过期了),转头向根服务器(或者它的上层服务器)去要这个资源的过程
8. sourceMap 有哪些?对应的作用是什么?
9. Babel 的原理?
Babel 是 JS 语法转换器(将一些高级语法转换成浏览器可以识别的低级语法)
Babel的功能很纯粹,它只是一个编译器。大多数编译器的工作过程可以分为三部分:解析(Parse) :将源代码转换成更加抽象的表示方法(例如抽象语法树)。包括词法分析和语法分析。词法分析主要把字符流源代码(Char Stream)转换成令牌流(Token Stream)。语法分析主要是将令牌流转换成抽象语法树(Abstract Syntax Tree,AST)。转换(Transform) :通过Babel的插件能力,对(抽象语法树)做一些特殊处理,将高版本语法的 AST 转换成支持低版本语法的 AST。让它符合编译器的期望,当然在此过程中也可以对 AST 的 Node 节点进行优化操作,比如添加、更新以及移除节点等。生成(Generate) :将 AST 转换成字符串形式的低版本代码,同时也能创建 Source Map 映射。
Babel 的原理:
- 使用
babylon将源代码进行解析-> 得到AST - 使用
babel-traverse对AST树进行遍历转义 -> 得到新的AST树 - 使用
babel-generator通过AST树生成ES5代码
- 使用
Babel 的包构成:
babel-core:babel的核心库,提供一下 babel 转义 API,如babel.transform等,用于对代码进行转译。(webpack的babel-loader是调用这些 API 来完成转译的)babylon:js的词法解析器babel-traverse:用于对AST的遍历babel-generator: 根据AST生成代码
工具包和功能包:
babel-cli:babel的命令行工具,通过命令行对 js 代码进行转译babel-register:通过绑定node.js的require来自动转译require引用的js代码文件babel-types:用于检验、构建和改变 AST 树的节点babel-polyfill:JS 标准新增的原生对象和 API 的 shim,实现上仅仅是core-js和regenerator-runtime两个包的封装babel-runtime:功能类似babel-polyfill,一般用于library或plugin中,因为它不会污染全局作用域
扩展:babel-runtime 和 babel-polyfill 的区别?
babel 默认只转译新的 JS 语法,而不转译新的 API(如:Iterator、Set、Generator、Proxy、Symbol 等全局对象),以及一些定义在全局对象上的方法(如:Object.assign)都不会转译。如果想使用这些新的对象和方法,则需要为当前环境提供一个 polyfill
babel-polyfill,它会加载整个polyfill库,解决了babel不转译新 API的问题。并且在代码中插入一些帮助函数缺点:直接在代码中插入帮助函数,会导致
污染了全局环境;并且全部引入,打包后会有很多重复的代码,导致编译后的代码体积变大babel-runtime:babel为了解决以上问题,提供了单独的包,用以提供编译模块的工具函数。启用babel-plugin-transform-runtime(它会帮我自动动态require @babel/runtime中的内容)后,babel就会使用babel-runtime下的工具函数;这样可以避免自行引入 polyfill 时导致的污染全局命名空间的问题babel-runtime适合在组件,类库项目中使用,而babel-polyfill适合在业务项目中使用
扩展:babel-runtime 为什么适合 JavaScript 库和工具包的实现?
- 避免
babel编译的工具函数在每个模块里重复出现,减小库和工具包的体积 - 在没有使用
babel-runtime之前,库和工具包一般不会直接引入polyfill。否则像Promise这样的全局对象会污染全局命名空间。在使用babel-runtime后,库和工具只要在package.json中增加依赖babel-runtime,交给babel-runtime去引入polyfill就行了
注意:具体项目还是需要使用 babel-polyfill,只使用 babel-runtime 的话,实例方法不能正常工作(例如 "foobar".includes("foo"))
10. module、chunk、bundle 分别是什么意思,有何区别?
- 对于一份同逻辑的代码,当我们手写下一个一个的文件,它们无论是 ESM 还是 commonJS 或是 AMD,他们都是 module
- 当我们写的 module 源文件传到 webpack 进行打包时,webpack 会根据文件引用关系生成 chunk 文件,webpack 会对这个 chunk 文件进行一些操作
- webpack 处理好 chunk 文件后,最后会输出 bundle 文件,这个 bundle 文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行
总结:我们直接写出来的是 module,webpack 处理时是 chunk,最后生成浏览器可以直接运行的 bundle