Babel基础知识整理
Babel是一个JavaScript编译器
Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。下面列出的是 Babel 能为你做的事情:
- 语法转换
- 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过 @babel/polyfill 模块)
- 源码转换 (codemods)
Presets(预设)
一、@babel/preset-env (官方preset)
作用:预设目标环境,添加我们需要的预设环境
babel-preset-env将基于你的实际浏览器及运行环境,自动的确定babel插件及polyfill,编译ES2015及此版本以上的语言,在没有配置项的情况下,babel-preset-env表现的同babel-preset-latest一样(或者可以说同babel-preset-es2015, babel-preset-es2016, and babel-preset-es2017结合到一起,表现的一致)。
1 { 2 "presets": ["@babel/preset-env"] 3 } 4 5 { 6 "presets": ["@babel/preset-env”,option] 7 }
常用配置字段:
1、targets: String | Array | { [String]: string } 。默认为{}
这一属性说明了当前的项目的适用环境。可以编写字符串的内容,作为boswerlist的遍历条件。例如:"targets": "> 0.25%, not dead" 或者也可以使一个对象。来设置对于每一个浏览器最低版本的控制。例如:{ "targets": { "chrome": "58", "ie": "11" } },其中的浏览器关键字从如下之中选取:chrome, opera, edge, firefox, safari, ie, ios, android, node, electron
2、 boswerlist:
是在不同的前端工具之间共用目标浏览器和 node 版本的配置工具,browserslist可以在babel之中配置,来告诉babel具体转化代码的规则。有三种配置方式:
- 在.babelrc或者babel.config.js中去配置
1 { 2 "presets": [ 3 [ 4 "@babel/preset-env", 5 { 6 "targets": { 7 "node": "4", 8 "chrome": "58", 9 "ie": "11" 10 } 11 } 12 ] 13 ] 14 }
- 在package.json 里面增加如下配置
1 { 2 "browserslist": [ 3 "last 1 version", "> 1%", 4 "maintained node versions", "not dead" 5 ] 6 }
- 在工程的根目录下存在.browerslistrc配置文件
# 注释是这样写的,以#号开头 last 1 version > 1% maintained node versions not dead
配置文件来源:browerslist 将使用如下配置文件限定的的浏览器和 node 版本范围:
- 工具 options,例如 Autoprefixer 工具配置中的 browsers 属性。
- BROWERSLIST 环境变量。
- 当前目录或者上级目录的browserslist配置文件。
- 当前目录或者上级目录的browserslistrc配置文件。
- 当前目录或者上级目录的package.json配置文件里面的browserslist配置项(推荐)。
- 如果上述的配置文件缺失或者其他因素导致未能生成有效的配置,browserslist 将使用默认配置> 0.5%, last 2 versions, Firefox ESR, not dead。
实践经验:
- 仅仅当你在特定浏览器上开发类似于信息亭之类的 web app 的时候,才可以用类似last 2 Chrome versions的查询条件来锁定特别具体的浏览器品牌和版本。市面上有各种各样的浏览器,同时浏览器的版本碎片化也很严重,如果你在开发一款通用的 webapp,那就应该考虑浏览器多样性导致的兼容问题。
- 如果你不想用 browserslsit 的默认设置,推荐使用last 1 version, not dead 和 > 0.2%(或者> 1% in US,> 1% in my stats).仅仅使用last n versions 将添加太多的废弃浏览器到工程里面来,同时也并没有有效的覆盖那些占有率仍然很高的老版本浏览器。
- 不移除某些浏览器,是因为你不了解它们的分布。Opera mini 在非洲有一亿用户,全球范围内,它也比 微软的 Edge 浏览器更加流行。QQ 浏览器的使用量比桌面端的火狐和 Safari 浏览器加起来还多。
查询条件列表:可以用如下查询条件来限定浏览器和 node 的版本范围(大小写不敏感):
- > 5%: 基于全球使用率统计而选择的浏览器版本范围。>=,<,<=同样适用。
- > 5% in US : 同上,只是使用地区变为美国。支持两个字母的国家码来指定地区。
- > 5% in alt-AS : 同上,只是使用地区变为亚洲所有国家。这里列举了所有的地区码。
- > 5% in my stats : 使用定制的浏览器统计数据。
- cover 99.5% : 使用率总和为99.5%的浏览器版本,前提是浏览器提供了使用覆盖率。
- cover 99.5% in US : 同上,只是限制了地域,支持两个字母的国家码。
- cover 99.5% in my stats :使用定制的浏览器统计数据。
- maintained node versions :所有还被 node 基金会维护的 node 版本。
- node 10 and node 10.4 : 最新的 node 10.x.x 或者10.4.x 版本。
- current node :当前被 browserslist 使用的 node 版本。
- extends browserslist-config-mycompany :来自browserslist-config-mycompany包的查询设置
- ie 6-8 : 选择一个浏览器的版本范围。
- Firefox > 20 : 版本高于20的所有火狐浏览器版本。>=,<,<=同样适用。
- ios 7 :ios 7自带的浏览器。
- Firefox ESR :最新的火狐 ESR(长期支持版) 版本的浏览器。
- unreleased versions or unreleased Chrome versions : alpha 和 beta 版本。
- last 2 major versions or last 2 ios major versions :最近的两个发行版,包括所有的次版本号和补丁版本号变更的浏览器版本。
- since 2015 or last 2 years :自某个时间以来更新的版本(也可以写的更具体since 2015-03或者since 2015-03-10)
- dead :通过last 2 versions筛选的浏览器版本中,全球使用率低于0.5%并且官方声明不在维护或者事实上已经两年没有再更新的版本。目前符合条件的有 IE10,IE_Mob 10,BlackBerry 10,BlackBerry 7,OperaMobile 12.1。
- last 2 versions :每个浏览器最近的两个版本。
- last 2 Chrome versions :chrome 浏览器最近的两个版本。
- defaults :默认配置> 0.5%, last 2 versions, Firefox ESR, not dead。
- not ie <= 8 : 浏览器范围的取反。
- 可以添加not在任和查询条件前面,表示取反
注意事项:
Browserslist 会处理浏览器的每个版本,所以应该避免配置这样的查询条件Firefox > 0.多个查询条件组和
在一起之后,其之间的的覆盖是以OR 的方式,而是不是AND,也就是说只要浏览器版本符合筛选条件里面的
一种即可。
所有的查询条件均基于Can I Use的支持列表。例如:last 3 ios versions 可能会返回8.4, 9.2, 9.3(混合了
主版本和次版本),然而last 3 Chrome versions可能返回50, 49, 48(只有主版本),总之一切以 CanIUse网
站收集的浏览器版本数据为准。
不同环境的差异化配置:
"browserslist": { "production": [ "> 1%", "ie 10" ], "development": [ "last 1 chrome version", "last 1 firefox version" ] }
3、useBuiltIns : "usage"| "entry"| false,默认为false。
此选项配置@babel/preset-env如何处理polyfill。usage和entry选项配置上都是增加按需引入的功能。
- “useBuiltIns”: “usage"
在文件需要的位置单独按需引入,可以保证在每个bundler中只引入一份。当前模式类似于@babel/plugin-transform-runtime,polyfill局部使用,制造一个沙盒环境,不造成全局污染
- “useBuiltIns”: “entry”
在项目入口引入一次(多次引入会报错),插件@babel/preset-env会将把@babel/polyfill根据实际需求打散,只留下必须的,例如:
In
import "@babel/polyfill”;
Out (实际引入取决于环境不同)
import "core-js/modules/es6.promise";
import "core-js/modules/es7.string.pad-start";
import "core-js/modules/es7.string.pad-end";
import "core-js/modules/es7.array.includes";
4、modules
选项用于模块转化规则设置,可选配置包括:"amd" | "umd" | "systemjs" | "commonjs" | false, 默认使用 "commonjs"。即,将代码中的ES6的import转为require。
如果你当前的webpack构建环境是2.x/3.x,推荐将modules设置为false,即交由 Webpack 来处理模块化,通过其 TreeShaking 特性将有效减少打包出来的 JS 文件大小。这部分参考这里的回答:ECMAScript 6 的模块相比 CommonJS 的require (...)有什么优点?
5、include :Array<string|RegExp>,默认为[]
转译时必须包含的插件
6、exclude: Array<string|RegExp>,默认为[]
转译时排除在外的插件
二、@babel/preset-react(官方preset)
作用:react 语法转译
1、默认包含的插件
此 preset 始终包含以下插件:
- @babel/plugin-syntax-jsx
- @babel/plugin-transform-react-jsx
- @babel/plugin-transform-react-display-name
以及以下开发环境下的选择辅助开发插件:
PS:一般使用该预设都是默认值,不需要单独配置插件
配置参数:
- pragma
string 类型,默认值为 React.createElement。编译 JSX 表达式时替用于换所使用的函数(function)。
- pragmaFrag
string 类型,默认值为 React.Fragment。编译 JSX 片段时用于替换所用的组件。
- useBuiltIns
boolean 类型,默认值为 false。当插件需要某项功能时,此参数用于确定是使用内置功能还是通过 polyfill 来模拟。
- development
boolean 类型,默认值为 false。用于确定是否开启用于辅助开发的插件,例如 @babel/plugin-transform-react-jsx-self 和 @babel/plugin-transform-react-jsx-source。当与 env 参数 配置或 js 配置文件 一同使用时会非常有用。
- throwIfNamespace
boolean 类型,默认值为 true。如果使用了 XML 命名空间标签,此参数用于设置是否抛出错误。例如:<f:image />。虽然 JSX 规范允许这样做,但是默认情况下是被禁止的,因为 React 的 JSX 目前并不支持这 种方式。
三、@babel/preset-stage-x
作用:可以将处于某一阶段的js语法编译为正式版本的js代码
1、stage-X: 指处于某一阶段的js语言提案。
提案共分为5个阶段:
- stage-0:稻草人-只是一个大胆的想法
- stage-1:提案-初步尝试
- stage-2:初稿-完成初步规范
- stage-3:候选-完成规范和浏览器初步实现
- stage-4:完成-将被添加到下一年发布
如果不知道需要哪个stage-x的话,直接引入stage-0,stage-0包含所有stage-1所有插件,stage-1包含所有stage-2所有插件,stage-2包含所有stage-3所有插件(每个stage包含的插件就不列了,感兴趣可以自行百度,可以参考看下http://www.bubuko.com/infodetail-1895965.html)
PS: babel-preset-stage-4已经整合入Presets不单独发布了
plugins(插件)
Babel 是一个编译器(输入源码 => 输出编译后的代码)。就像其他编译器一样,编译过程分为三个阶段:解析、转换和打印输出。
现在,Babel 虽然开箱即用,但是什么动作都不做。它基本上类似于 const babel = code => code; ,将代码解析之后再输出同样的代码。如果想要 Babel 做一些实际的工作,就需要为其添加插件。
除了一个一个的添加插件,你还可以以 preset 的形式启用一组插件。
一、转换插件
作用:这些插件用于转换你的代码。(转换插件将启用相应的语法插件,因此你不必同时指定这两种插件。)
1、ES3
2、ES5
3、ES2015(ES6)
- arrow-functions
- block-scoped-functions
- block-scoping
- classes
- computed-properties
- destructuring
- duplicate-keys
- for-of
- function-name
- instanceof
- literals
- new-target
- object-super
- parameters
- shorthand-properties
- spread
- sticky-regex
- template-literals
- typeof-symbol
- unicode-regex
4、ES2016
5、ES2017
6、ES2018
- async-generator-functions
- dotall-regex
- named-capturing-groups-regex
- object-rest-spread
- optional-catch-binding
- unicode-property-regex
7、Modules
8、Experimental
- class-properties
- decorators
- do-expressions
- export-default-from
- export-namespace-from
- function-bind
- function-sent
- logical-assignment-operators
- nullish-coalescing-operator
- numeric-separator
- optional-chaining
- partial-application
- pipeline-operator
- private-methods
- throw-expressions
9、Minification
- inline-consecutive-adds
- inline-environment-variables
- member-expression-literals
- merge-sibling-variables
- minify-booleans
- minify-builtins
- minify-constant-folding
- minify-dead-code-elimination
- minify-flip-comparisons
- minify-guarded-expressions
- minify-infinity
- minify-mangle-names
- minify-numeric-literals
- minify-replace
- minify-simplify
- minify-type-constructors
- node-env-inline
- property-literals
- regexp-constructors
- remove-console
- remove-debugger
- remove-undefined
- simplify-comparison-operators
- undefined-to-void
10、React
- react-constant-elements
- react-display-name
- react-inline-elements
- react-jsx
- react-jsx-compat
- react-jsx-self
- react-jsx-source
11、其他
- external-helpers
- flow-strip-types
- jscript
- object-assign
- object-set-prototype-of-to-assign
- proto-to-assign
- regenerator
- runtime
- strict-mode
- typescript
二、语法插件
这些插件只允许 Babel 解析(parse) 特定类型的语法(而不是转换)。(注意:转换插件会自动启用语法插件。因此,如果你已经使用了相应的转换插件,则不需要指定语法插件。)
{ "parserOpts": { "plugins": [ "jsx", "flow" ] } }
三、插件/Preset路径
如果插件再 npm 上,你可以输入插件的名称,babel 会自动检查它是否已经被安装到 node_modules 目录下
{ "plugins": ["babel-plugin-myPlugin"] }
你还可以指定插件的相对/绝对路径。
{ "plugins": ["./node_modules/asdf/plugin"] }
四、插件的短名称
如果插件名称的前缀为 babel-plugin-,你还可以使用它的短名称:
1 { "plugins": [ 2 "myPlugin”, 3 "babel-plugin-myPlugin" // 两个插件实际是同一个 4 ] 5 }
这也适用于带有冠名(scope)的插件:
1 { "plugins": [ 2 "@org/babel-plugin-name”, 3 "@org/name" // 两个插件实际是同一个 4 ] 5 }
五、插件顺序
- Plugin 会运行在 Preset 之前。
- Plugin 会从第一个开始顺序执行。ordering is first to last.
- Preset 的顺序则刚好相反(从最后一个逆序执行)。
常用插件说明
1、babel-core //必备的核心库
2、babel-loader //webpack loader配置必备
3、babel-preset-env //有了它,你不再需要添加2015、2016、2017,全都支持
4、babel-preset-stage-0 //有了它,你不再需要添加stage-1,stage-2,stage-3,默认向后支持
5、babel-plugin-transform-runtime 、babel-runtime //支持helpers,polyfill,regenerator配置
babel-plugin-transform-runtime该插件主要做了三件事
- 自动转换generators/async
- 使用core-js来按需给内置类型打上polyfill。(这一点和useBuiltIns:'usage'一样)
- 通过helpers 选项自动移除嵌入的babel helper,并且用module引用来代替。否则每个文件中都会加入这些inline babel helper,造成代码冗余。默认为ture。
@babel/runtime 和 @babel/polyfill 虽然都是为内置类型打上垫片,但是@babel/runtime是在模块内起作用,不会污染全局的Promise,Map...。所以实例的方法不会被polyfill。
但是如果运行环境很low,比如比如说Android一些老机子,而你有需要大量使用Promise、Object.assign、Array.find之类的全局对象或者其所属方法,那么使用babel-polyfill,绝对是一劳永逸。
接着,再来说说babel-runtime,相对而言,它的处理方式比较温柔,套用步步高的广告词就是哪里需要加哪里,比如说你需要Promise,你只需要import Promise from 'babel-runtime/core-js/promise'即可,这样不仅避免污染全局对象,而且可以减少不必要的代码。
不过,如果N个文件都需要Promise,难道得一个个文件的加import Promise from 'babel-runtime/core-js/promise'么,显然不是,Babel已经为这样情况考虑过了,只需要使用babel-plugin-transform-runtime就可以轻松的帮你省去手动import的痛苦,而且,它还做了公用方法的抽离,哪怕你有100个模块使用了Promise,但是promise的polyfill仅仅存在1份,所有要的地方都是引用一地方,具体的配置参考如下:
1 // .babelrc 2 { 3 "presets": [ 4 "env", 5 "stage-0" 6 ], 7 "plugins": [ 8 "transform-runtime" 9 ], 10 "comments": false 11 }
@babel/plugin-transform-runtime 还可以和preset-env中的useBuiltIns一起使用,作用的顺序按照plugins --> presets 的顺序,也就是先使用@babel/plugin-transform-runtime 打上垫片,然后再使用@babel/polyfill 再打上一次垫片。
如果使用react:
1、babel-plugin-transform-decorators-legacy //支持修饰符语法 @connect
2、babel-preset-react //支持解析react语法,如果使用vue把react替换成vue就好
如果需要热更新:
1、react-hot-loader //虽然它长得不像babel,但是它也需要在babelrc做配置
最后总结
主要几个配置
- presets : 预设,插件的集合,倒序执行
- plugins : 插件,先执行插件,在执行预设,顺序执行
- ignore : 忽略的文件
- minify : 压缩代码
- common : 是否需要注释
- env : 设置不同的环境,应用不同的配置,配置取值:BABEL_ENV,如若没有取 NODE_ENV 的值,默认为 development.
常见预设
- env : 用于替换 es2015 / es2016 / es2017 的预设。根据环境引入插件
- react :react的插件集合
- react-optimize : react 代码优化,如去除 propsType 减少生产上面代码
- stage-x :草案代码插件集合
- flow : flow 插件集合
- minify : 代码优化的集合
- typescript : typescript 插件集合
常用插件
- transform-async-to-generator : 异步函数 async/await 插件
- transform-decorators-leagacy : 装饰器插件
- syntax-dynamic-import :import() 插件
- transform-runtime : 辅助器插件,用于ployfill
- transform-object-rest-spread : 用于合并 var test = {a:1,b2};var t = {...test,n:1}
- transform-funciton-bind : 用于编译 obj::fun => fun.bind(obj)
资料来源: