什么是babel?
babel是一种转译文本,目的是将es6以上的语法转译为es5能够识别的语法,以供浏览器或者node进行识别
使用方法
Babel一共有三种使用方法,分别是:
1.单体文件
2.命令行
3.构建工具的插件(webpack的babel-loader等)
运行方式和插件
运行方式:
babel一共分为了三个阶段:解析,转换,生成
需要注意的是,babel本身不具备任何转译方式,他的转化功能全部存在plugin当中,也就是说,当我们不使用任何插件时,使用babel转译出来的文本和不使用的效果是一样的。
插件:
babel的插件一共分为两种,分别是语法插件和转译插件
op:当我们使用了语法插件后就不要用转译插件了
配置文件:
那么我们如何配置babel的插件呢?
1.将插件名字配置到配置文件中(在根目录下创建一个.babelrc或者package.json的babel中)
2.使用npm-install babel-plugin-xxx进行安装
preset:
babel提供的一个插件的集合,开发者在使用时不用重复进行定义安装,提高了开发效率。
执行顺序:
-
plugin会运行在preset之前
-
plugin运行方式是从前至后
-
preset运行方式是从后到前
插件和plugin的配置项
极简情况下(也就是不进行任何配置的情况下),插件和preset只需列出名字即可,但大部分情况下是要做一些配置时,需要将自己转化为一个数组,第一个是名字,后面则是配置项
"presets": [
// 带了配置项,自己变成数组
[
// 第一个元素依然是名字
"env",
// 第二个元素是对象,列出配置项
{
"module": false
}
],
// 不带配置项,直接列出名字
"stage-2"
]
env的配置
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
其他的一些babel配置
Babel-cli
顾名思义,安装了babel-cli我们就可以在命令行去执行babel操作
Babel-node
在babel7以下的版本中,babel-node是存在于babel-cli中的,所以使用它不需要额外安装,他的目的是在node环境中直接运行es6以上的语法规范,而不需要进行转译。
他的作用相当于babel-polyfill
+ babel-register
Babel-register
babel-register改写require命令,为他加上了一个钩子,每次在执行.js,.jsx等文件名后缀时都会进行转译
使用时必须先加载require('babel-register')
。
babel-register只会对require命令加载的文件进行转译,而不会对本文件进行转译。
只适合在开发环境下使用
Babel-polyfill
我们要知道,babel只会转译js的语法,而不会去转译其更新的api,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign
)都不会转码。
那么这时我们就需要借助babel的配置,babel-polyfill,在webpack.config.js中将babel-polyfill作为第一个entry引入。
但他有两个巨大的缺点,一是打包出来的文件较大,因为他会加载所有的方法,但是我们可能只需要转译其中的一个方法,这样就没必要了,二是他会污染全局变量,所以我们现在已经不常用了
但如果代码中包含高版本 js 中类型的实例方法 (例如 [1,2,3].includes(1)
),这还是要使用 polyfill。
Babel-runtime 和babel-plugin-transform-runtime
Babel-plugin-transform-runtime
以async为例,假如我们不使用这个配置的话处理后的文件应该是这样:
// babel 添加一个方法,把 async 转化为 generator
function _asyncToGenerator(fn) { return function () {....}} // 很长很长一段
// 具体使用处
var _ref = _asyncToGenerator(function* (arg1, arg2) {
yield (0, something)(arg1, arg2);
});
我们看到如果不引入该插件他会定义一个async类型的函数,接着调用,但每个被转译的文件都会定义一次asynctogenerator,那么这样会造成时间和空间的浪费。
op:为什么每个被转译的文件都会定义?
因为这是babel的默认行为,文件作用域
在每个文件中引入
_asyncToGenerator
是为了避免跨文件的依赖关系。这种方式保证了单独运行一个文件时,代码仍然可以正常工作。
那么我们使用babel-plugin-transform-runtime呢?
// 从直接定义改为引用,这样就不会重复定义了。
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
// 具体使用处是一样的
var _ref = _asyncToGenerator3(function* (arg1, arg2) {
yield (0, something)(arg1, arg2);
});
我们看到,定义变为了引用,这样就不会重复定义,节约了时间空间。
我们看到,在使用babel-plugin-transform-runtime的同时,我们也使用了babel-runtime,那么runtime的作用是什么呢?
他的内部集成了以下几种类型:
-
core-js:转换一些内置类(promise,symbol等)和静态方法(array from),绝大部分转换都是在这里做的,自动引入
-
regenerator
: 作为core-js
的拾遗补漏,主要是generator/yield
和async/await
两组的支持。当代码中有使用generators/async
时自动引入。 -
helpers:上面的asyncToGenerator就是其中一个,在代码中内置的helpers使用时会移除第一段定义,而变成第二段的引用
op:
常见的一些helpers:
_asyncToGenerator
:将async/await
转换为基于 Generator 的代码。
_inherits
:实现类继承。
_extends
:实现对象的展开(类似于Object.assign
)。
_typeof
:检查变量类型的兼容实现。
Babel-loader
在一些大型项目中,我们会用webpack进行项目的打包构建,在压缩后的代码我们使用babel处理理论上来说效率很低,需要的时间很长,那么我们有没有一种办法,在文件压缩前进行babel处理呢,这样就有了babel插入构建文件的需求,webpack中有loader,便有babel-loader这个东西
常见配置:
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader'
}
]
}
当然,复杂一点,我们还想引入babel的一些配置,可以这么写:
// loader: 'babel-loader' 改成如下:
use: {
loader: 'babel-loader',
options: {
// 配置项在这里
}
}
这样的配置优先级是最高的。
babel7版本的一些改变
本质上来说核心机制没有变化,不过改动了一些写法和用法
Npm package名称的变化:
这是 babel 7 的一个重大变化,把所有 babel-*
重命名为 @babel/*
,例如:
-
babel-cli
变成了@babel/cli
。 -
babel-preset-env
变成了@babel/preset-env
。进一步,还可以省略preset
而简写为@babel/env
。 -
babel-plugin-transform-arrow-functions
变成了@babel/plugin-transform-arrow-functions
。和preset
一样,plugin
也可以省略,于是简写为@babel/transform-arrow-functions
。
当然,.babelrc的配置也要发生改变,例如:
{
"presets": [
- "env"
+ "@babel/preset-env"//改成@babel的写法
]
}
不再支持低版本的node
node>=6
Babel/node从babel/cli中独立了
之前我们提到过,babel/node之前是父子关系,现在node强起来了,需要独立安装了
标签:node,插件,plugin,babel,loader,转译 From: https://blog.csdn.net/2401_86877597/article/details/144153175