babel知识点
Opened this issue · 0 comments
简介
Babel 模块都是作为独立的 npm 包发布的,并且(从版本 7 开始)都是以 @babel 作为冠名的。这种模块化的设计能够让每种工具都针对特定使用情况进行设计。 其中,最重要的两个是 @babel/core 和 @babel/cli。
npm install --save-dev @babel/core @babel/cli
CLI 命令行工具
使用示例
npx babel src --out-dir dist
npx babel === ./node_modules/.bin/babel 命令
主要三个options: --out-dir、--plugins、--presets
插件和预设(preset)
代码转换功能以插件的形式出现,插件是小型的 JavaScript 程序,用于指导 Babel 如何对代码进行转换。(即ast三大步骤中的transform,一般的插件实现就是编写ast transform的vistor)
如:将es2015+语法的箭头函数转换成es5语法,通常使用插件:
npm install --save-dev @babel/plugin-transform-arrow-functions
使用方式:
npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions
具体实现,就是获取ast语法,然后创建一个functionExpression去替换arrowFunctionExpression。参考抽象语法树AST
那么,是不是所有需要转换的语法,都需要配置一个个插件来进行转换呢?
答案是不需要的。
可以使用一个 "preset" (即一组预先设定的插件)。
@babel/preset-env,这个preset 所包含的插件将支持所有最新的 JavaScript (ES2015、ES2016 等)特性,即这个插件会转换大部分默认的语法,而不用一个个的配置插件。
配置文件
babel除了使用cli的方式来进行转换语法外,更多的场景是使用配置文件的形式。
babel配置文件支持两种方式:babel.config.js、.babelrc
区别(根据使用场景)
babel.config.js (官方推荐)
- 希望以编程的方式创建配置文件
- 希望编译 node_modules 目录下的模块
module.exports = function (api) {
api.cache(true);
const presets = [
[
"@babel/env",
{
targets: {
"targets":{
"chrome":"58",
"ie":"10"
},
useBuiltIns: "usage",
},
],
];
const plugins = [ ... ];
return {
presets,
plugins
};
}
现在,名为 env 的 preset 只会为目标浏览器中没有的功能加载转换插件。
.babelrc
- 只是需要一个简单的并且只用于单个软件包的配置
{
"presets": [...],
"plugins": [...]
}
另外,还可以 .babelrc.js: 与 .babelrc 的配置相同,但你可以使用 JavaScript 编写。
const presets = [ ... ];
const plugins = [ ... ];
module.exports = { presets, plugins };
Polyfill
@babel/polyfill 模块包括 core-js 和一个自定义的 regenerator runtime 模块用于模拟完整的 ES2015+ 环境
这意味着你可以使用诸如 Promise 和 WeakMap 之类的新的内置组件、 Array.from 或 Object.assign 之类的静态方法、 Array.prototype.includes 之类的实例方法以及生成器函数(generator functions)(前提是你使用了 regenerator 插件)。为了添加这些功能,polyfill 将添加到全局范围(global scope)和类似 String 这样的内置原型(native prototypes)中。
对于软件库/工具的作者来说,这可能太多了。如果你不需要类似 Array.prototype.includes 的实例方法,可以使用 transform runtime 插件而不是对全局范围(global scope)造成污染的 @babel/polyfill。这能够确保你的库或是组件不包含不必要的 polyfills。通常来说,打包 polyfills 应当是最终使用你的库的应用的责任
更进一步,如哦你确切地指导你所需要的 polyfills 功能,你可以直接从 core-js 获取它们。
@babel/preset-env插件提供了配置属性useBuiltIns,此参数设置为 "usage" 时,会同时包含你所需要的 polyfill(即设置这个时,可以不用引入@babel/polyfill)。
Babel 将检查你的所有代码,以便查找目标环境中缺失的功能,然后只把必须的 polyfill 包含进来。示例代码如下:
Promise.resolve().finally();
将被转换为(由于 Edge 17 没有 Promise.prototype.finally):
require("core-js/modules/es.promise.finally");
Promise.resolve().finally();
vue项目推荐使用 @vue/babel-preset-app,它已经预包含所需要的 polyfill。
presets: ['@vue/app']
-proposal
推荐的插件(指的是没有纳入官方release版本的那些属性插件):
- @babel/plugin-proposal-function-bind
- @babel/plugin-proposal-class-properties
插件顺序
- 插件在 Presets 前运行。
- 插件顺序从前往后排列。
- Preset 顺序是颠倒的(从后往前)。
最新的@babel/polyfill只是core-js的一个软链,core-js是一个javascript模块标准库,是最新的所有javascript语法的一个polyfill.
babylon引入方式变成了@babel/parser.
以前所有babel-的变成了@babel/.
babel默认是进来啥code,出去就啥code,即要使其转换代码,必须配置相关插件.
@babel/preset-env takes any target environments you've specified and checks them against its mappings to compile a list of plugins and passes it to Babel.(默认读取.browserslistrc 文件来获取支持的浏览器版本target)
babel-plugin-transform-runtime 和 babel-runtime
1.在转换 ES2015 语法为 ECMAScript 5 的语法时,babel 会需要一些辅助函数,例如 _extend。
babel 默认会将这些辅助函数内联到每一个 js 文件里,这样文件多的时候,项目就会很大。
所以 babel 提供了 transform-runtime 来将这些辅助函数“搬”到一个单独的模块 babel-runtime 中,这样做能减小项目文件的大小。
2.开发应用建议使用 babel-polyfill 会在全局新增对应方法
开发框架建议babel-plugin-transform-runtime局部变量 不会污染全局 局部使用es6的函数或方法
优点:
(1)提高代码重用性,缩小编译后的代码体积。
(2)防止污染全局作用域。(启用corejs配置)
几个包关系
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了。
@babel/plugin-proposal-optional-chaining
使用该插件可以用来做数据防御处理。
以前的处理:
if(res && res.data && res.data.length > 0 {
...
}
现在可以使用:
if(res?.data?.length>0){
...
}
是不是方便、简洁很多,告别了繁琐各种 & 和 各种层层检查?
跟多使用:
- 结合数组: a?.b?.[0]
- 结合函数: a?.b?[0]
- 结合执行: a?.()
- 结合delete: delete a?.b
browserslist
你会发现有 package.json 文件里的 browserslist 字段 (或一个单独的 .browserslistrc 文件),指定了项目的目标浏览器的范围。这个值会被 @babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀。
参考
es6features
core-js
babel
Babel插件:@babel/plugin-transform-runtime