We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Babel 的编译过程可以分为三个阶段:
1. 解析(Parsing)
使用 babylon 解析器对输入的源代码字符串进行解析并生成初始 AST。整个解析过程分为两个步骤:1)词法分析(lexical analyzer或scanner):将整个代码字符串分割成语法单元数组在线分词工具。2)语法分析(syntax analyzer):它会将词法分析出来的数组转化成树形的表达形式。同时,验证语法,语法如果有错的话,抛出语法错误。
babel 内部使用的解析类库叫做 babylon,并非 babel 自行开发。
简单来说语法分析是对语句和表达式识别,这是个递归过程,在解析中,Babel 会在解析每个语句和表达式的过程中设置一个暂存器,用来暂存当前读取到的语法单元,如果解析失败,就会返回之前的暂存点,再按照另一种方式进行解析,如果解析成功,则将暂存点销毁,不断重复以上操作,直到最后生成对应的语法树。
*可以通过astexplorer.net 网站在线生成抽象语法树AST*
2. 转换(Transformation)
babel中最核心的是 babel-core ,它向外暴露出 babel.transform 接口,遍历 AST 树并应用各 transformers(plugin)。
const babel = require('babel-core'); let result = babel.transform(code, { presets: ['env'], plugins: ['transform-runtime'] })
3. 生成(Code Generation) 利用 babel-generator 将 AST 树生成转码后的代码
将插件的名字增加到配置文件中 (根目录下创建.babelrc或者 package.json的babel中),然后npm install babel-plugin-xxx进行安装。 示例:
npm install babel-plugin-xxx
{ "presets": [ ["env", // 带了配置项,自己变成数组。第一个元素依然是名字 { // 第二个元素是对象,列出配置项 "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } } ], // 不带配置项,直接列出名字 "stage-2" ], "plugins": [ "transform-vue-jsx", "transform-runtime", [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ], "env": { // 基于环境来配置 Babel "test": { "presets": ["env", "stage-2"], "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] } } }
env:Babel 将根据当前环境来开启 env 下的配置。当前环境可以使用 process.env.BABEL_ENV 来获得。 如果 BABEL_ENV 不可用,将会替换成 NODE_ENV,并且如果后者也没有设置,那么缺省值是development。
env
development
因为一套类似es2015的规范,包含了很多个转译插件。为了每次要开发者一个个添加并安装转译插件问题,babel提供了一组插件的集合。理解为单点和套餐的差别
preset分为以下几种:
{ "presets": [ ["env", { "targets": "> 0.25%, not dead" }] ] }
{ "presets": [ ["env", { "targets": { "chrome": "58", "ie": "11" } }] ] }
{ "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "safari >= 7"] } }] ] }
语法可以参考browserslist
amd
umd
systemjs
commonjs
false
{ "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" }, { "libraryName": "mint-ui", "style": true } ] ] }
babel-plugin-component部分源码:
babel-plugin-component
var _options = options, _options$libDir = _options.libDir,//这是组件所在根目录下的路径element-ui/lib/ libDir = _options$libDir === void 0 ? 'lib' : _options$libDir, _options$libraryName = _options.libraryName,//这是ui库的名字--elementui libraryName = _options$libraryName === void 0 ? defaultLibraryName : _options$libraryName, _options$style = _options.style, style = _options$style === void 0 ? true : _options$style, styleLibrary = _options.styleLibrary,//这是引入组件时,所需要引入对应组件样式的配置对象 _options$root = _options.root, root = _options$root === void 0 ? '' : _options$root, _options$camel2Dash = _options.camel2Dash, camel2Dash = _options$camel2Dash === void 0 ? true : _options$camel2Dash; var styleLibraryName = options.styleLibraryName;//这是组件所需样式的路径(相对于上面的lib) var _root = root; var isBaseStyle = true; var modulePathTpl; var styleRoot; var mixin = false; var ext = options.ext || '.css';//这是加载样式的后缀,默认css
所以,如果在.babelrc文件配置过styleLibraryName属性的,就不需要在全局引入element的css样式。
背景: Babel 把 Javascript 语法 分为 syntax 和 api:
api指那些我们可以通过 函数重新覆盖的语法 ,类似 includes,map,includes,Promise,凡是我们能想到重写的都可以归属到 api
syntax像箭头函数,let,const,class, 依赖注入Decorators,等等这些,我们在 Javascript 在运行是无法重写的,想象下,在不支持的浏览器里不管怎么样,你都用不了 let 这个关键字
babel 默认只转换syntax层语法,而不转换新的 API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转码。对于API, Babel 把这个放在了 单独放在了 polyfill 这个模块处理。
使用时,在所有代码运行之前增加 require('babel-polyfill')。或者更常规的操作是在 webpack.config.js 中将 babel-polyfill 作为第一个 entry。
require('babel-polyfill')
babel-polyfill主要有两个缺点:
babel-polyfill
改进方案: 使用useBuiltIns设置:取值可以是usage(按需引入), entry(全部引入,会根据browserlist 过滤出需要的polyfill)和false。默认值为false。Babel 7该配置才会生效
usage
entry
{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage" } ] ] }
使用上面的配置,打包后如下:
为了解决babel-polyfill会污染全局空间的问题,有了babel-plugin-transform-runtime。babel-plugin-transform-runtime的目的是为您的代码创建沙盒环境,不会污染全局空间和内置对象原型。它适用于开发供其他人使用的库,或者如果您无法准确控制代码运行的环境。
babel-plugin-transform-runtime
{ "presets": [ [ "env" ] ], "plugins": ["transform-runtime"] }
babel配置如上,打包后的结果:
例如,你的代码中用了Promise,babel-plugin-transform-runtime会自动重写你使用Promise的代码,转换为使用babel-runtime导出(export)的Promise-like对象。 所以plugin-transform-runtime一般用于开发(devDependencies),而runtime自身用于部署的代码(dependencies),两者配合来一起工作。
Promise
babel-runtime内部集成了:
babel-runtime
core-js
regenerator
helpers
babel-runtime缺点: babel-plugin-transform-runtime不支持实例方法 (例如 [1,2,3].includes(1)),这时还是需要使用babel-polyfill
*总结:Babel 只是转换syntax层语法,所有需要 @babel/polyfill 来处理API兼容,又因为 polyfill 体积太大,所以通过 preset的 useBuiltIns 来实现按需加载,再接着为了满足 npm 组件开发的需要 出现了@babel/runtime 来做隔离。*
Babel的插件模块需要你暴露一个function,function内返回visitor,visitor是对各类型的AST节点做处理的地方。类似这样:
visitor
module.export = function(babel){ return { visitor: { // ...你的插件代码 } } }
通过AST explorer知道要处理的AST节点,如下:
基本流程:
BinaryExpression(path) { ... }
babel-types
var t = require('babel-types'); if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) { ... }
var t = require('babel-types'); ... path.replaceWith(t.numericLiteral(result));
【参考】 前端工程师需要了解的 Babel 知识 一口(很长的)气了解 babel Babel学习系列4-polyfill和runtime差别(必看) @babel/polyfill 与 @babel/plugin-transform-runtime 详解 你真的会用 Babel 吗? 了解 Babel 6 & 7 生态 深入浅出的webpack构建工具---babel之配置文件.babelrc(三)
The text was updated successfully, but these errors were encountered:
No branches or pull requests
关于Babel
Babel 编译的三个阶段
Babel 的编译过程可以分为三个阶段:
1. 解析(Parsing)
使用 babylon 解析器对输入的源代码字符串进行解析并生成初始 AST。整个解析过程分为两个步骤:1)词法分析(lexical analyzer或scanner):将整个代码字符串分割成语法单元数组在线分词工具。2)语法分析(syntax analyzer):它会将词法分析出来的数组转化成树形的表达形式。同时,验证语法,语法如果有错的话,抛出语法错误。
简单来说语法分析是对语句和表达式识别,这是个递归过程,在解析中,Babel 会在解析每个语句和表达式的过程中设置一个暂存器,用来暂存当前读取到的语法单元,如果解析失败,就会返回之前的暂存点,再按照另一种方式进行解析,如果解析成功,则将暂存点销毁,不断重复以上操作,直到最后生成对应的语法树。
*可以通过astexplorer.net 网站在线生成抽象语法树AST*
2. 转换(Transformation)
babel中最核心的是 babel-core ,它向外暴露出 babel.transform 接口,遍历 AST 树并应用各 transformers(plugin)。
3. 生成(Code Generation)
利用 babel-generator 将 AST 树生成转码后的代码
Babel使用
将插件的名字增加到配置文件中 (根目录下创建.babelrc或者 package.json的babel中),然后
npm install babel-plugin-xxx
进行安装。示例:
env
:Babel 将根据当前环境来开启 env 下的配置。当前环境可以使用 process.env.BABEL_ENV 来获得。 如果 BABEL_ENV 不可用,将会替换成 NODE_ENV,并且如果后者也没有设置,那么缺省值是development
。Presets
因为一套类似es2015的规范,包含了很多个转译插件。为了每次要开发者一个个添加并安装转译插件问题,babel提供了一组插件的集合。理解为单点和套餐的差别
preset分为以下几种:
env配置参数
语法可以参考browserslist
amd
,umd
,systemjs
,commonjs
和false
。转换es6模块语法到其他模块规范, false不会转换。默认为commonjs
。执行顺序
其他工具
babel-plugin-component
部分源码:所以,如果在.babelrc文件配置过styleLibraryName属性的,就不需要在全局引入element的css样式。
polyfill和runtime差别
背景:
Babel 把 Javascript 语法 分为 syntax 和 api:
api指那些我们可以通过 函数重新覆盖的语法 ,类似 includes,map,includes,Promise,凡是我们能想到重写的都可以归属到 api
syntax像箭头函数,let,const,class, 依赖注入Decorators,等等这些,我们在 Javascript 在运行是无法重写的,想象下,在不支持的浏览器里不管怎么样,你都用不了 let 这个关键字
babel 默认只转换syntax层语法,而不转换新的 API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转码。对于API, Babel 把这个放在了 单独放在了 polyfill 这个模块处理。
polyfill(垫片)
使用时,在所有代码运行之前增加
require('babel-polyfill')
。或者更常规的操作是在 webpack.config.js 中将 babel-polyfill 作为第一个 entry。babel-polyfill
主要有两个缺点:babel-polyfill
会导致打出来的包非常大,因为 babel-polyfill 是一个整体,把所有方法都加到原型链上。babel-polyfill
会污染全局变量,给很多类的原型链上都作了修改,如果我们开发的也是一个类库供其他开发者使用,这种情况就会变得非常不可控。改进方案:
使用useBuiltIns设置:取值可以是
usage
(按需引入),entry
(全部引入,会根据browserlist 过滤出需要的polyfill)和false
。默认值为false
。Babel 7该配置才会生效使用上面的配置,打包后如下:
runtime
为了解决
babel-polyfill
会污染全局空间的问题,有了babel-plugin-transform-runtime
。babel-plugin-transform-runtime
的目的是为您的代码创建沙盒环境,不会污染全局空间和内置对象原型。它适用于开发供其他人使用的库,或者如果您无法准确控制代码运行的环境。babel配置如上,打包后的结果:
例如,你的代码中用了
Promise
,babel-plugin-transform-runtime会自动重写你使用Promise的代码,转换为使用babel-runtime导出(export)的Promise-like对象。 所以plugin-transform-runtime一般用于开发(devDependencies),而runtime自身用于部署的代码(dependencies),两者配合来一起工作。babel-runtime
内部集成了:core-js
: 转换一些内置类 (Promise, Symbols等等) 和静态方法 (Array.from 等)。它几乎包含了所有 JavaScript 最新标准的垫片。regenerator
:作为 core-js 的拾遗补漏,主要是 generator/yield 和 async/await 两组的支持。helpers
: babel的辅助函数,例如 toArray函数, jsx转化函数。babel-runtime
缺点:babel-plugin-transform-runtime
不支持实例方法 (例如 [1,2,3].includes(1)),这时还是需要使用babel-polyfill
*总结:Babel 只是转换syntax层语法,所有需要 @babel/polyfill 来处理API兼容,又因为 polyfill 体积太大,所以通过 preset的 useBuiltIns 来实现按需加载,再接着为了满足 npm 组件开发的需要 出现了@babel/runtime 来做隔离。*
Babel插件开发流程
Babel的插件模块需要你暴露一个function,function内返回
visitor
,visitor
是对各类型的AST节点做处理的地方。类似这样:通过AST explorer知道要处理的AST节点,如下:
基本流程:
babel-types
)babel-types
),replaceWith(替换)【参考】
前端工程师需要了解的 Babel 知识
一口(很长的)气了解 babel
Babel学习系列4-polyfill和runtime差别(必看)
@babel/polyfill 与 @babel/plugin-transform-runtime 详解
你真的会用 Babel 吗?
了解 Babel 6 & 7 生态
深入浅出的webpack构建工具---babel之配置文件.babelrc(三)
The text was updated successfully, but these errors were encountered: