引入
在此之前或许你已经用过babel,也许听说过什么 babel-loader babel-core、babel-cli、babel-plugin-...、babel-preset-env 反正各种乱七八糟的做项目随便用一一下就可以了,对他只有个一知半解,甚至不知道他到底是干什么的,反正项目要用,照着用就行了,至少博主之前的状态是这样,如果只对他有个一知半解甚至都不了解,那么项目出bug了你都不知道怎么去调试,只能复制-->粘贴-->百度。 基于此,写下自己对Babel的理解。
Babel是什么?
我们在他的官方网站找到这样一句话
Babel is a JavaScript compiler
Babel 是一个 JavaScript 编译器
Babel是一个编译器,针对JavaScript,为什么会有Babel这样一个工具的存在?
本文默认你对es6、es7等有所涉足,我们在写es6 语法的时候是不是很方便,什么promise、
async await`,可是es6 语法写的虽然很酸爽,但是浏览器他不兼容啊,你想想你写的代码在浏览器上跑不起来,在好的语法在好的特性又有什么用?
这个时候Babel这样一个工具出来了,他可以将我们写的es6 语法转换为浏览器兼容的语法,比如将箭头函数转换为普通函数,有了这样一个工具我们就即可以写酸爽的语法,又可以让使浏览器兼容。 相信到这你已经知道了Babel的概念,并且可以脑补出Babel可以干什么。
下面介绍Babel的使用和一些细节
Babel 的使用
- 单体文件
- 命令行
- 配合Webpack使用
本文将介绍Babel配合webpack使用的情况
配置文件
babel的配置文件有几种,.babelrc
,babel.config.json
配置方法都一样,本文以.babelrc
配置文件为主
在初次接触Babel我们只要用到一下两项配置
代码语言:javascript复制//.babelrc
{
"presets": [...],
"plugins": [...]
}
下面介绍presets与plugins
Plugins
Plugins顾名思义插件。 babel 本身不具有任何转化功能,我们要的代码要转换某些功能,比如将es6转换为es5,我们就需要下载相应的插件,并且将这些插件配置到.babelrc文件的plguins
里面。
比如将箭头函数转换为浏览器能识别的普通函数 我们就需要用到 @babel/plugin-transform-arrow-functions
插件,并将其添加到配置文件 1.首先下载插件 npm i @babel/plugin-transform-arrow-functions -D
2.添加至配置文件
//.babelrc
{
"plugins":[
"@babel/plugin-transform-arrow-functions"
]
}
这样babel就能够将箭头函数转换为普通函数了
代码语言:javascript复制//转换前
var a = () => {};
//转换后
var a = function () {};
Q:什么?你怎么知道要用这个插件? A:看文档啊!【https://www.babeljs.cn/docs/plugins】
Presets
Presets顾名思义预设。 我们要转换一些语法就得使用各种插件,并且添加到配置文件,如果每次项目需要的babel插件都差不多,而我们每次都要进行重复的下载,配置工作,这样效率是不是很低,很繁琐。 这个时候我们就可以利用presets
这个功能,将一些常用的babel插件,配置放入预设中,下载直接将这个预设放入配置文件即可
比如项目中经常要使用到 @babel/plugin-transform-arrow-functions
和@babel/plugin-transform-for-of
插件,那么我们可以将这两个插件设为预设 具体操作参照文档【https://www.babeljs.cn/docs/presets#创建-preset】
将其设为预设后 下次在.babelrc文件配置即可,如假设预设为 myPreset,那么在配置文件
代码语言:javascript复制//.babelrc
{
"presets":["myPreset" ]
}
当然除了我们自定义预设,我们还可以使用别人定义好的一些预设,如你经常看到的 @babel/preset-env
、@babel/preset-react
使用方法
1.下载preset npm i @babel/preset-env
2.配置文件
代码语言:javascript复制//.babelrc
{
"presets":["preset-env" ]
}
Q:我怎么知道有哪些预设? A:看文档 ,搜npm Q我怎么知道预设里面有哪些插件? A: 要知道预设里面有哪些插件,最好的方式就是搜npm看他的依赖项
可以看到preset-env有 66个插件
不过 preset 分为以下几种: 第一种:官方内容 env, react, flow 等 第二种:stage-x,这里面包含的都是当年最新规范的草案,每年更新。 这里面还细分为 Stage 0 - 稻草人: 只是一个想法,经过 TC39 成员提出即可。
Stage 1 - 提案: 初步尝试。
Stage 2 - 初稿: 完成初步规范。
Stage 3 - 候选: 完成规范和浏览器初步实现。
Stage 4 - 完成: 将被添加到下一年度发布
这也就是你在早期的项目或文档中看到 stage-0,stage-1的字眼,原来他们都是预设,不过现在你不用纠结这个问题Babel7已经放弃stage预设了。
Polyfill
Plolyfill 垫片。 babel默认只转换新的 JavaScript 语法,比如箭头函数、扩展运算(spread)。 不转换新的 API,例如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。如果想使用这些新的对象和方法,则需要为当前环境提供一个垫片(polyfill)。 Polyfill主要有三种
@babel/polyfill
Babel 包含一个polyfill 库即@babel/polyfill
。这个库里包含 regenerator 和 core-js。 什么是regenerator? npm地址 :https://www.npmjs.com/package/regenerator-runtime github地址:https://github.com/facebook/regenerator/blob/master/packages/regenerator-runtime/runtime.js
官方对他的描述直译过来是 重新生成编译的生成器和异步函数的独立运行时 在网上找到这样一句话 生成器函数、async、await函数经babel编译后,regenerator-runtime模块用于提供功能实现。 博主对于生成器(regenerator)也是一知半解,这里我们就简单的理解成,项目中要使用async、await函数就必须使用这个库吧!(有知道的欢迎下方留言)。
什么是core-js? npm 地址 https://www.npmjs.com/package/core-js 下面是官方对他的解释
JavaScript的模块化标准库。包括到2021年的ECMAScript的Polyfill:
promises
、symbols
、collections
、iterators
、typed
、arrays
、许多其他特性、ECMAScript提案、跨平台WHATWG / W3C特性和提案,比如URL。您可以只加载所需的特性,或者在不污染全局命名空间的情况下使用它。
粗暴的理解就是,你要使用一些js高级特性如promise就得使用这个库。
经过上面潦草的描述,我们在总结一下 由于babel只能将es6 语法转换为低级语法,而当我们使用一些高级特性时比如 async
、await
类似的Api,babel就显得无能为力了,因为babel无法实现这些高级Api的功能,这个时候就需要一个垫片(polyfill),而babel又包含了一个polyfill叫@babel/polyfill
这个polyfill本身也无法实现像async
等高级API的功能,但是市面上有现成的封装好的类库实现了,于是@babel/polyfill
将他们包含进来。这样当我们引入@babel/polyfill
时,就可以丝滑的写高级语法了!
使用方式
1.npm i @babel/polyfill -S
记住是-S** 因为polyfill要在源代码之前运行!
2.在入口文件导入
代码语言:javascript复制import "@babel/polyfill";
当然在webpack中你也可以这样干
在@babel/polyfill的描述有这样一段
The polyfill is provided as a convenience but you should use it with @babel/preset-env and the useBuiltIns option so that it doesn't include the whole polyfill which isn't always needed. Otherwise, we would recommend you import the individual polyfills manually
大概意思就是建议我们将@babel/polyfill和@babel/preset-env配合使用并根据需求设置 useBuiltlns
选项,这样就不至于将这个polyfill加载进来,显得很大。 不然就使用手动导入各个polyfill的方式。
**在webpack中我们可以将@babel/polyfill和@babel/preset-env配合使用! ** 主要是设置 useBuiltlns
选项 更多preset-env的配置请参照 https://babeljs.io/docs/en/babel-preset-env#options
{
"presets" : [
["@babel/preset-env",{
"targets": {
"browsers": [
"last 2 versions",
"not ie <= 9"
]
},
"useBuiltIns": "usage"
}]
]
}
useBuiltlns:false
:此时不对 polyfill 做操作。如果引入 @babel/polyfill,则无视配置的浏览器兼容,引入所有的 polyfill。 useBuiltlns:usage
:按需加载polyfill,根据配置的浏览器兼容以及代码所用到的polyfill, 不至于将所有polyfill加载进来,使用这种方式我们不用手动导入polyfill但是需要安装。 useBuiltlns:entry
:根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手动添加 import @babel/polyfill
,会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill。
**@babel/polyfill带来的问题 ** babel-polyfill,通过改写全局prototype的方式实现,它会加载整个polyfill,针对编译的代码中新的API进行处理,并且在代码中插入一些帮助函数,比较适合单独运行的项目。
babel-polyfill解决了Babel不转换新API的问题,但是直接在代码中插入帮助函数,会导致污染了全局环境,并且不同的代码文件中包含重复的代码,导致编译后的代码体积变大。虽然这对于应用程序或命令行工具来说可能是好事,但如果你的代码打算发布为供其他人使用的库,或你无法完全控制代码运行的环境,则会成为问题。
后来@babel/polyfill凉了,通过官方我们知道 从 Babel 7.4.0起 @babel/polyfill已经被弃用了。 之前使用的方式现在改成了
代码语言:javascript复制//import "@babel/polyfill"; //之前的写法
import "core-js/stable";
import "regenerator-runtime/runtime";
babel-runtime
代码语言:javascript复制npm i babel-runtime -S
为了解决 @babel/polyfill带来的问题,Babel提供了单独的包babel-runtime用于提供编译模块的工具函数,启用插件babel-plugin-transform-runtime
后,Babel就会使用babel-runtime下的工具函数。
babel-runtime插件能够将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用。每当要转译一个api时都要手动加上require(‘babel-runtime’)。简单说 babel-runtime 更像是一种按需加载的实现,比如你哪里需要使用 Promise,只要在这个文件头部 require Promise from ‘babel-runtime/core-js/promise’就行了
不过如果你许多文件都要使用 Promise,难道每个文件都要 import 一遍不成?
babel-plugin-transform-runtime
为了方便使用 babel-runtime,解决手动 require 的苦恼。它会分析我们的 ast 中,是否有引用 babel-rumtime 中的垫片(通过映射关系),如果有,就会在当前模块顶部插入我们需要的垫片。
transform-runtime 是利用 plugin 自动识别并替换代码中的新特性,你不需要再引入,只需要装好 babel-runtime 和 配好 plugin 就可以了。
好处是按需替换,检测到你需要哪个,就引入哪个 polyfill,如果只用了一部分,打包完的文件体积对比 babel-polyfill 会小很多。而且 transform-runtime 不会污染原生的对象,方法,也不会对其他 polyfill 产生影响。
所以 transform-runtime 的方式更适合开发工具包,库,一方面是体积够小,另一方面是用户(开发者)不会因为引用了我们的工具,包而污染了全局的原生方法,产生副作用,还是应该留给用户自己去选择。
babel-runtime 和 babel-plugin-transform-runtime
在大多数情况下,你应该安装 babel-plugin-transform-runtime 作为开发依赖(使用 —save-dev),并且将 babel-runtime 作为生产依赖(使用 —save)。这个看vue-cli生成的 package.json就能发现。
因为babel编译es6到es5的过程中,babel-plugin-transform-runtime这个插件会自动polyfill es5不支持的特性,这些polyfill包就是在babel-runtime这个包里(core-js 、regenerator等)
npm install —save-dev babel-plugin-transform-runtime
npm install —save babel-runtime
使用技巧
以下内容摘自 https://blog.csdn.net/vv_bug/article/details/107092536 @babel/preset-env包含了一些基本es语法转换的插件(箭头函数、类转换等等),同时还支持polyfill,有usage跟entry模式,但是preset-env添加polyfill会像之前使用@babel/polyfill一样,会污染全局变量。
@babel/plugin-transform-runtime主要是利用@babel/runtime提取了一些公共的babel帮助函数,同时也支持polyfill的添加,添加的polyfill都是以一个局部变量的形式引入,不会污染全局变量。 如果你做的是一个二方库,然后需要被别人依赖,那么建议使用@babel/plugin-transform-runtime来引入polyfill,因为你要尽可能的专注于做自己的事,而不是说去影响别人,语法转换可以使用preset-env预设,比如以下配置
代码语言:javascript复制module.exports = {
presets: [
[
"@babel/preset-env",
]
],
plugins: [
[
"@babel/plugin-transform-runtime",
{
corejs: {version: 3, proposals: true},
helpers: true,
useESModules: true,
regenerator: true,
absoluteRuntime: "./node_modules"
}
]
]
};
如果你做的是一个普通的业务项目的话,可以用preset-env来转换语法和polyfill,然后再利用@babel/plugin-transform-runtime来引入helpers跟generator做到代码重复利用,比如以下配置:
代码语言:javascript复制module.exports = {
presets: [
[
"@babel/preset-env",
{
corejs: 3,
useBuiltIns: 'usage',
}
]
],
plugins: [
[
"@babel/plugin-transform-runtime",
{
corejs: false,
helpers: true,
useESModules: false,
regenerator: true,
absoluteRuntime: "./node_modules"
}
]
]
};
@babel/plugin-transform-runtime常见配置参照:https://babeljs.io/docs/en/babel-plugin-transform-runtime#options
下面在介绍几个与Babel相关的
@babel/core:babel的核心库
babel-loader:使用webpack时作为有个loader在代码混淆之前进行代码转换
@babel/preset-env:babel预设的一种
@babel/cli:允许使用babel命令转译文件