github: babel概括总结javascript
在前端的发展过程当中,javascript的兼容性,一直是前端头痛的问题,在之前的一些有些项目中,为解决浏览器兼容而花费的时间甚至还要多余实际的业务逻辑开发时间,babel就是其中处理兼容的转译工具(或者叫平台)。css
javascript在不断发展,新的提案标准每一年都会有,在获得普遍普及以前,Babel 把用最新标准编写的 JavaScript 代码向下编译成能够在今天随处可用的版本html
babel的编译过程分为3步,解析(parse),转换(transform),生成(generate),对应的三个插件分别是Babylon
、babel-traverse
、babel-generator
。
babylon将源码转换为抽象语法树(AST);babel-traverse经过AST生成一个便于操做、转换的path对象,供咱们的babel插件处理;babel-generator读取AST并将其转换为代码和源码映射。这些过程不是本文的关注点,咱们关注的是结果,哪些插件与咱们的生产息息相关,咱们如何去使用babel的插件。前端
vue脚手架生成的项目在.babelrc
文件中的配置:vue
{ "presets": [ ["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } }], "stage-2" ], "plugins": ["transform-vue-jsx", "transform-runtime"] }
babel插件推崇的是功能的单一性,就是每一个插件的功能尽量的单一,好比我要使用es6的箭头函数,那就能够装一个转换插件npm i -D @babel/plugin-transform-arrow-functions
,将其写进.babelrc
文件里就好了:详情java
{ "presets": [], "plugins": ["@babel/plugin-transform-arrow-functions"] }
这样,咱们写的:node
(a) => [...a]
会被该插件转换为:react
function (a) { return [...a] }
这个插件也只解决箭头函数的转换,就算函数内部用了其它新语法也无论,这个好处是很明显的,就跟咱写项目推崇组件的细腻度一个道理webpack
然而呢,js发展有点快,想一下那个es2015(es6)一下加了多少东西,咱们要使用还得一个一个的npm i -D xxx
,这个有点小麻烦,因此就能够采用presets
配置项。npm i -D babel-preset-es2015
,而后配置.babelrc
。详情git
为了承接上文,这里暂时先用babel6的写法,babel7里也能够用babel-preset-es2015,可是文档里去掉了,es201五、es201六、es2017(2018年的东西直接写在env里了,7月份2019年的新标准就要来罗@_@)等都被放在env里面了,之后这几个preset会不会砍掉就不知道咯
{ "presets": ["es2015"], "plugins": [] }
这样咱们就可使用包括箭头函数在内的es6的新语法
而不用去担忧兼容问题。这下这两个的关系也就清晰了,presets
里面配置的是一些plugins
集合
在babel 7.3.0
里面,presets
-- 对应插件
有这些:
@babel/preset-env
@babel/preset-stage-0
@babel/preset-stage-1
@babel/preset-stage-2
@babel/preset-stage-3
@babel/preset-flow
@babel/preset-react
babel-preset-minify
@babel/preset-typescript
在presets配置里面,咱们看到了:
["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } }]
这个env是@babel/preset-env
这个集合插件配置项,这里的配置项:
modules
:"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false, defaults to "auto".
target
:就是告诉babel你的js要兼容哪些环境,它会帮你将你写的js转译成目标环境兼容的js语法,这个具体配置能够看browserslist 那就是说,js不管用什么新玩意,@babel/preset-env
都能跟我兼容到我想要的环境?带着问题,咱们再看看官网的介绍,What is Babel:
再看看经过env转换的几个demo:
a => a // 转为 function (a) {return a} function func (b = false) {return false} // 转为 function func (b) { b = b || false return b }
比较明显,上面的都属于转换语法
,因此应该说“js不管用什么新语法
,@babel/preset-env
都能帮你兼容到目标环境”。
@babel/preset-env
具体能够帮咱们转化哪些呢?看这儿JavaScript新特性和Babel插件的映射关系,这个是@babel/preset-env集合插件所包含的插件列表,每一个插件对应转换一个新特性,至于没有的,好比promise、Array.from等,请往下看。
在上面的配置中,咱们看到env下面有个stage-2。stage-x,这里面包含的都是当年最新规范的草案,每一年更新。细分为以下几步
官网里有一句话It is important to note that @babel/preset-env does not support stage-x plugins.
,就是说@babel/preset-env中不包含在草案阶段的新属性的转换插件
其实咱们经过 plugin-features,以及 proposals/finished-proposals(其中2019就是今年的stage-4),能够发现@babel/preset-env是包含了stage-4阶段的plugins的。
好比写react的同窗比较熟悉的decorators目前就处于stage-2阶段,咱们要用这些处于草案阶段的新属性,能够安装npm i -D @babel/preset-stage-2
,而后在presets里写上stage-2,babel就会经过那些处于草案阶段的新属性的插件将咱们代码中的用到的新属性转译成为es5
此外,低一级的 stage 会包含全部高级 stage 的内容,例如 stage-2 会包含 stage-2, stage-3 的全部内容。
Babel 几乎能够编译全部时新的 JavaScript 语法,但对于 APIs 来讲却并不是如此。好比说:Promise
、WeakMap
、Array.from
、Object.assign
、Array.prototype.includes
、generator
等。为了解决这个问题,咱们使用一种叫作 Polyfill(代码填充,也可译做兼容性补丁) 的技术。能让你提早使用还不可用的 APIs。
引入它很简单,咱们npm i -S @babel/polyfill
,
在vue中的入口文件main.js文件的最上面:
import "@babel/polyfill";
或者在webpack入口里引入:
module.exports = { entry: ["@babel/polyfill", "./main.js"], };
二者任选其一
上面这两种方式是将整个polyfill都引入了,不少代码其实对咱们是没有用的,好比,咱们的env配置的是不须要兼容ie9如下的浏览器,这种引入方式把全部兼容ie的代码都引入了,包含ie8如下,因此,通常咱们会在.babelrc
文件里的env里配置下useBuiltIns
参数,这样babel在引入的时候会根据咱们env环境去加载相应的polyfill:详细
有以下三种方式
// .babelrc { ["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] }, // 是否自动引入polyfill,开启此选项必须保证已经安装了babel-polyfill // 在这里设置自动引入后,babel会根据你配置的兼容的环境,去按需加载polyfill的代码,这样能保证代码量最少 // 参数:Boolean,默认为false. "useBuiltIns": false }] } // webpack.base.conf.js module.exports = { entry: ["@babel/polyfill", "./main.js"], };
咱们看到上面的配置中有个transform-runtime
,这个是配置@babel/plugin-transform-runtime,它是作什么的呢?官网说:一个插件,经过重复使用babel注入的助手(helper)代码,来减小代码体积
,咱们看看它是如何工做的。
npm i -D @babel/plugin-transform-runtime // .babelrc { "plugins": [ "@babel/plugin-transform-runtime", // 默认配置 { "absoluteRuntime": false, "corejs": false, "helpers": true, "regenerator": true, "useESModules": false } ] }
好比这个es6的class类:
class Person { }
在没有使用transform-runtime时,每一个使用class
函数处,Babel 会生成class
的helper函数放置在文件顶部,就是说在多个文件里使用了class
, babel就会在每一个文件里面都生成相同的helper:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function"); } } var Person = function Person() { _classCallCheck(this, Person); };
这样没必要要的重复会使咱们的代码体积很是雍肿,transform-runtime就是来解决这个重复生成helper的问题的,它会将这个es6的class
语法的helper
函数放在babel-runtime/helpers
里,而后在使用处经过require引入,这样就不必在每一个使用处重复定义helper
了,达到了减小代码体积的效果。
"use strict"; var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck"); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var Person = function Person() { (0, _classCallCheck3.default)(this, Person); };
@babel/runtime和@babel/polyfill这两个模块功能几乎相同,就是转码新增 api
根据它们两的特色,@babel/polyfil通常用于前端项目,@babel/runtime通常用来写插件
Babel 的 CLI 是一种在命令行下使用 Babel 编译文件的简单方法。有时候咱们只是写一个插件,须要用babel转一下咱们代码中的高阶语法,由于项目可能不太大,用不到构建工具,就能够用babel-cil。转换依据咱们的.babelrc
文件或者package.json
中babel选项
编译一个文件
babel my-file.js
若是咱们想要把一个目录整个编译成一个新的目录,可使用 --out-dir 或者 -d。.
$ babel src --out-dir lib # 或 $ babel src -d lib
babel-loader是什么呢?前面说了,咱们能够经过babel-cil在命令行里告诉babel转译哪些js,也能够经过babel-register,在代码里经过require来转,可是,如今前端开发是一个工程化过程,依赖关系比较复杂,在一个稍微大点儿的项目中还真无法手动告诉babel要处理哪些文件,好比一个.vue
文件,里面还包含html、css,还有一些不是js的鬼语法,这时候就要借助其它插件先提早处理下,因此,webpack根据依赖关系,加载文件的时候遇到js文件后,会将文件内容的js字符串根据loader配置的前后顺序,挨个儿传递给它们处理,babel-loader就是其中之一
babel就是将目标环境(浏览器)经过打补丁升级成支持最新javascript语法的环境的工具。
// .babelrc { "presets": [ ["env", { // 这里默认是false,不用再写一遍 - // "modules": false, // 通常不单独写出来,babel/preset-env会自个读取package里面的browserslist,与css兼容环境保持一致 // https://github.com/browserslist/browserslist - // "targets": { - // "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] - // }, }], "stage-2" ], "plugins": ["transform-vue-jsx", "transform-runtime"] } // webpack.base.conf.js module.exports = { entry: ["@babel/polyfill", "./main.js"], };
名称 | 做用 | 备注 |
---|---|---|
babel/cli | 容许命令行使用 babel 命令转译文件 | 通常在写插件时使用 |
babel/polyfill | 为全部 API 增长兼容方法 | 须要在全部代码以前 require,且体积比较大 |
babel/plugin-transform-runtime | 把帮助类方法从每次使用前定义改成统一 require,精简代码 | --- |
babel/runtime | helper库 | 须要安装为依赖,而不是开发依赖,node环境使用,web环境不须要 |
babel/loader | babel插件在webpack项目中的一个入口 | --- |
babel/core | babel的polyfill库 | --- |
babel/preset-env | babel预制环境的集合插件,经过配置目标环境,转换标准上的新特性 | 只转新特性,不转api |
babel/preset-stage-2 | 转换草案stage-2以及stage-3阶段的的新属性 | --- |
参考: