webpack配置babel篇
babel-polyfill & babel-runtime & babel-preset-env
babel-core
babel-core 的作用是把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理。(ast=抽象语法 # 树)
babel-polyfill
babel-runtime
babel-runtime不会污染全局空间和内置对象原型。事实上babel-runtime是一个模块,你可以把它作为依赖来达成ES2015的支持。
比如环境不支持Promise,你可以在项目中加入
require(‘babel-runtime/core-js/promise’)
来获取Promise。
这样我们就弥补了babel-polyfill的缺点,达到了按需加载的效果。但是在实际项目开发过程中,我们往往会写很多新的es6 api,每次都要手动引入相应的包比较麻烦,维护起来也不方便,每个文件重复引入也造成代码的臃肿。
要解决这个问题,就要用到 babel-plugin-transform-runtime
,它会分析我们的 ast 中,是否有引用 babel-rumtime 中的垫片(通过映射关系),如果有,就会在当前模块顶部插入我们需要的垫片。
接下来我们尝试一下,先安装babel-runtime和babel-plugin-transform-runtime
npm install --save babel-runtime
npm install --save-dev babel-plugin-transform-runtime
由于 babel-runtime只是集中了polyfill的library,对应需要的 polyfill 都是要引入项目中,并跟项目代码一起打包的,所以就要加入到生产环境依赖中
下面在.babelrc中加入以下配置
{
"plugins": ["transform-runtime"]
}
执行打包命令,打包出来的bundle.js的大小为63K
,比完整引入polyfill小了好多。但是事物都有两面性,babel-runtime有个缺点,它不模拟实例方法
,即内置对象原型上的方法,所以类似Array.prototype.find,你通过babel-runtime是无法使用的,这只能通过 babel-polyfill 来转码,因为 babel-polyfill 是直接在原型链上增加方法。这就悲催了,难道还是要完整引入babel-polyfill?其实还有一个解决的办法,就是用babel-preset-env
babel-runtime的相关配置
core-js
core-js 是用于 JavaScript 的组合式标准化库,它包含 es5 (e.g: object.freeze), es6的 promise,symbols, collections, iterators, typed arrays, es7+提案等等的 polyfills 实现。也就是说,它几乎包含了所有 JavaScript 最新标准的垫片。不过为什么它不把 generator 也实现了...
// 比如,只不过需要单个引用
require('core-js/array/reduce');
require('core-js/object/values');
regenerator
它是来自于 facebook 的一个库,链接。主要就是实现了 generator/yeild, async/await。
所以 babel-runtime 是单纯的实现了 core-js 和 regenerator 引入和导出,比如这里是 filter 函数的定义,做了一个中转并处理了 esModule 的兼容。
module.exports = { "default": require("core-js/library/fn/array/filter"), __esModule: true };
helpers
它把每个 helper 都单独放到一个文件夹里。这样,配合 transform-runtime 使用的时候,需要用 helper 转化的时候,就从 babel-runtime 中直接引用了。
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
文件结构:
使用
可以单独引入require('babel-runtime/core-js/object/values');
不过这些模块都做了 esModule 的兼容处理,也就是上面引入的模块是{ "default": require("core-js/library/fn/array/filter"), __esModule: true }
这样的,要使用还得加上 .default
。所以我们期待的是,最好能有帮我们自动处理的插件,babel-plugin-transform-runtime
就是用来做这个的。这个我们放到 plugin 去讲。
babel-preset-env
1、babel-preset-env 能根据当前的运行环境,自动确定你需要的 plugins 和 polyfills。通过各个 es标准 feature 在不同浏览器以及 node 版本的支持情况,再去维护一个 feature 跟 plugins 之间的映射关系,最终确定需要的 plugins。
2、单独使用时,只能转化部分语法箭头函数,let,const之类的,不能转化内置对象,实例方法等。如果进一步需要转换内置对象、实例方法,那就得用polyfill。
entry
这是一种入口导入方式, 只要我们在打包配置入口 或者 文件入口写入 import "core-js"
这样一串代码, babel 就会替我们根据当前你所配置的目标浏览器(browserslist)来引入所需要的polyfill 。
useage
这个就比较神奇了, useBuiltIns = useage 时,会参考目标浏览器(browserslist) 和 代码中所使用到的特性来按需加入 polyfill
当然, 使用 useBuiltIns = useage, 还需要填写另一个参数 corejs 的版本号,
core-js 支持两个版本, 2 或 3, 很多新特性已经不会加入到 2 里面了, 比如: flat 等等最新的方法, 2 这个版本里面都是没有的, 所以建议大家用3
.babelrc
{
"presets": [
["@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
这种方式打包体积不大,但是如果我们排除node_modules/目录,遇上没有经过转译的第三方包,就检测不到第三方包内部的 ‘hello‘.includes(‘h‘)这种句法,这时候我们就会遇到bug,
但是不排除的话又耗费性能。
false
剩下最后一个 useBuiltIns = false , 那就简单了, 这也是默认值 , 使用这个值时不引入 polyfill。
options.targets
用来配置需要支持的的环境,不仅支持浏览器,还支持node。
如果没有配置targets选项,就会读取项目中的browserslist配置项。
options.loose
默认值是false,如果preset-env中包含的plugin支持loose的设置,那么可以通过这个字段来做统一的设置。
options.modules
"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false
,默认值是auto
用来转换ES6的模块语法。如果使用false,将不会对文件的模块语法进行转化。
如果要使用webpack中的一些新特性,比如tree shaking 和 sideEffects,就需要设置为false,对ES6的模块文件不做转化,因为这些特性只对ES6的模块有效。
options.useBuiltIns
"usage" | "entry" | false
,默认值是false
这个配置项主要是用来处理@babel/polyfill。
- 设置为false时,会把@babel/polyfill这个包引进来,忽略targets配置项和项目里的browserslist配置
- 设置为entry时,在整个项目里,只需要引入一次@babel/polyfill,它会根据targets和browserslist,然后只加载目标环境不支持的api文件
- 设置为usage时,babel会在目标环境的基础上,只加载项目里用的那些不支持的api的文件,做到按需加载
其他的一些配置项可以看 官方文档
babel-runtime和plugin-transform-runtime区别
babel-runtime这种方式会借助 helper function 来实现特性的兼容,
并且利用 @babel/plugin-transform-runtime 插件还能以沙箱垫片的方式防止污染全局, 并抽离公共的 helper function , 以节省代码的冗余
也就是说 @babel/runtime 是一个核心, 一种实现方式, 而 @babel/plugin-transform-runtime 就是一个管家, 负责更好的重复使用 @babel/runtime
@babel/plugin-transform-runtime 插件也有一个 corejs 参数需要填写
版本2 不支持内置对象 , 但自从Babel 7.4.0 之后,拥有了 @babel/runtime-corejs3 , 我们可以放心使用 corejs: 3 对实例方法做支持
当前的 .babelrc
{
"presets": [
["@babel/preset-env"]
],
"plugins": [
[
"@babel/plugin-transform-runtime", {
"corejs": 3
}
]
]
}
babel-preset-env和plugin-transform-runtime区别
使用 @babel/plugin-transform-runtime 编译后的代码和之前的 @babel/preset-env 编译结果不一样,
它使用了帮助函数, 并且赋予了别名 , 抽出为公共方法, 实现复用。 比如它用了 _Promise 代替了 new Promise , 从而避免了创建全局对象
preset-env 和 plugin-transform-runtime同时使用
useage 和 @babel/runtime 同时使用的情况下比较智能, 并没有引入重复的 polyfill
个人分析原因应该是: babel 的 plugin 比 prset 要先执行, 所以preset-env 得到了 @babel/runtime 使用帮助函数包装后的代码,而 useage 又是检测代码使用哪些新特性来判断的, 所以它拿到手的只是一堆 帮助函数, 自然没有效果了
.babelrc
{
"presets": [
["@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime", {
"corejs": 3
}
]
]
}
entry 和 @babel/runtime
跟 useage 的情况不一样, entry 模式下, 在经过 @babel/runtime 处理后不但有了各种帮助函数还引入了许多polyfill, 这就会导致打包体积无情的增大
个人分析: entry 模式下遭遇到入口的
import "core-js"
及就立即替换为当前目标浏览器下所需的所有 polyfill, 所以也就跟 @babel/runtime 互不冲突了, 导致了重复引入代码的问题, 所以这两种方式千万不要一起使用, 二选一即可
{
"presets": [
["@babel/preset-env",
{
"useBuiltIns": "entry"
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime", {
"corejs": 3
}
]
]
}
@babel/plugin-transform-runtime默认情况下安装@babel/runtime这个库,即corejs为false时使用;
当corejs设置为2时,需要安装使用@babel/runtime-corejs2。
runtime和runtime-corejs2这两个库唯一的区别是,corejs2这个库增加了对core-js这个库的依赖,
而core-js是用来对ES6各个语法polyfill的库,所以在corejs为false的情况下,只能做语法的转换,并不能polyfill任何api。
总结
@babel/preset-env 拥有根据 useBuiltIns 参数的多种polyfill实现,优点是覆盖面比较全(entry), 缺点是会污染全局, 推荐在业务项目中使用
- entry 的覆盖面积全, 但是打包体积自然就大,
- useage 可以按需引入 polyfill, 打包体积就小, 但如果打包忽略node_modules 时如果第三方包未转译则会出现兼容问题
- @babel/runtime 在 babel 7.4 之后大放异彩, 利用 corejs 3 也实现了各种内置对象的支持, 并且依靠 @babel/plugin-transform-runtime 的能力,沙箱垫片和代码复用, 避免帮助函数重复 inject 过多的问题, 该方式的优点是不会污染全局, 适合在类库开发中使用
上面 1, 2 两种方式取其一即可, 同时使用没有意义, 还可能造成重复的 polyfill 文件
对比以上三种方案,我们得出以下结论
方案 | 打包后大小 | 优点 | 缺点 |
---|---|---|---|
babel-polyfill | 259K | 完整模拟ES2015+环境 | 体积过大;污染全局对象和内置的对象原型 |
babel-runtime | 63K | 按需引入,打包体积小 | 不模拟实例方法 |
babel-preset-env(开启useBuiltIns) | 194K | 按需引入,可配置性高 | - |
方案没有绝对的优劣,在开发过程中还是要根据实际情况灵活运用。
几个包的包含关系
babel-polyfill仅仅是引用core-js、regenerator-runtime这两个npm包。
@babel/runtime包含两个文件夹:helpers(定义了一些处理新的语法关键字的帮助函数)、regenerator(仅仅是引用regenerator-runtime这个npm包)。
@babel/runtime-corejs2包含三个文件夹:core-js(引用core-js这个npm包)、helpers(定义了一些处理新的语法关键字的帮助函数)、regenerator(仅仅是引用regenerator-runtime这个npm包)。
可以看出,@babel/runtime-corejs2≈@babel/runtime + babel-polyfill:
@babel/runtime只能处理语法关键字,而@babel/runtime-corejs2还能处理新的全局变量(例如,Promise)、新的原生方法(例如,String.padStart );
使用了@babel/runtime-corejs2,就无需再使用@babel/runtime了。
webpack配置babel篇的更多相关文章
- webpack配置这一篇就够
最近看了一篇好文,根据这个文章重新梳理了一遍webpack打包过程,以前的一些问题也都清楚了,在这里分享一下,同时自己也做了一些小的调整 原文链接:http://www.jianshu.com/p/4 ...
- webpack中babel配置 --- runtime-transform和babel-pollfill
webpack - babel配置 babel是一个javascript编译器,是前端开发中的一个利器.它突破了浏览器实现es标准的限制,使我们在开发中可以使用最新的javascript语法. 通过构 ...
- webpack.config.js====配置babel
参考:https://www.jianshu.com/p/9808f550c6a91. 安装依赖babel-loader: 负责 es6 语法转化babel-preset-env: 包含 es6.7 ...
- 17 webpack中babel的配置
在webpack中,默认只能处理一部分ES6的新语法,一些更高级的ES6语法或者ES7语法, webpack是处理不了的:这时候,就需要借助于第三方的loader,来帮助webpack处理这些高级的语 ...
- webpack配置篇
开发环境(development)和生产环境(production)的构建目标差异很大.在开发环境中,我们需要具有强大的.具有实时重新加载(live reloading)或热模块替换(hot modu ...
- 想入门webpack,这篇就够了
申明:本文转载自简书 文/zhangwang(简书作者)原文链接:http://www.jianshu.com/p/42e11515c10f#著作权归作者所有,转载请联系作者获得授权,并标注" ...
- 使用webpack、babel、react、antdesign配置单页面应用开发环境
这是Webpack+React系列配置过程记录的第一篇.其他内容请参考: 第一篇:使用webpack.babel.react.antdesign配置单页面应用开发环境 第二篇:使用react-rout ...
- webpack配置指南
Webpack已经出来很久了,相关的文章也有很多,然而比较完整的例子却不是很多,让很多新手不知如何下脚,下脚了又遍地坑 说实话,官方文档是蛮乱的,而且有些还是错的错的..很多配置问题只有爬过坑才知道 ...
- webpack4 babel 篇
demo 代码点此,如果对 babel 不熟,可以看一下babel 7 简单指北. webpack 使用 babel 来打包使用 es6 及以上语法的 js 文件是非常方便的,可以通过配置,将 es6 ...
随机推荐
- object-fit 详解
contain 被替换的内容将被缩放,以在填充元素的内容框时保持其宽高比. 整个对象在填充盒子的同时保留其长宽比,因此如果宽高比与框的宽高比不匹配,该对象将被添加"黑边". cov ...
- h2database在springboot中的使用
h2为轻量级数据库,使用特别方便,它可以不使用数据库服务器,直接嵌入到java程序中.可以配置持久化,同样也可以不持久化(数据在内存中)进程结束后,数据就释放,用做测试和演示特别方便.自带后台管理,非 ...
- 多测师讲解接口测试 _面试题003_高级讲师肖sir
接口测试 一.你对HTTP有没有了解过?具体讲一下对http的了解.(答题思路: 定义.常见请求类型.状态码.请求头请求体.响应头和响应体.三次握手和四次挥手.)答:了解,我们做接口的时候基本上都是基 ...
- 多测师讲解python _练习题003_高级讲师肖sir
python 003作业题:# 1.分别打印100以内的所有偶数和奇数并存入不同的列表当中# 2.请写一段Python代码实现删除一个list = [1, 3, 6, 9, 1, 8]# 里面的重复元 ...
- github 如何解决error: failed to push some refs
错误 error: failed to push some refs to 'https://github.com/whitclass/scrapy-spider.git' hint: Updates ...
- 【C++学习笔记】C++经典十二道笔试题!你能做出几道?
1. 运行下面的C++代码,得到的结果是什么? #include "stdafx.h" #include<iostream> using namespace std; ...
- [转]CSS学习笔记
原文:http://www.fx114.net/qa-266-93710.aspx 01.什么是CSS. CSS指层叠样式表(Cascading Style Sheets). ·样式定义如 ...
- html的keywords标签
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" /& ...
- Android 限制控件多次点击
有时候多次点击页面会连续弹出多个页面,这时候写一个方法控制一下就OK. private static long lastClickTime; public synchronized static b ...
- eclipse 开发常见问题集锦
问题1: eclipse导入外部项目,中文显示乱码(如下图) 方案:项目名-->右键属性-->如下图: 问题2: jsp/html页面eclipse双击打开,代码在工作区不显示(如下图:) ...