- babel介绍
babel是目前最流行的JavaScript编译器,最常见的是将es6,es7等语法解析成低端浏览器能够识别的语法(向下兼容)
- babel原理
解析(parse),转换(transform),生成(generate)
说到babel原理我就想到了vue template模版编译原理了
也就是使用了babel针对vue模版字符串进行解析。
原理:解析 ==> 优化 ==> 生成
解析:将vue模版字符串解析成ast树。
优化:找出那些静态节点和静态根节点并打上标记,避免下次重复更新。
生成:使用Ast生成render函数代码。
- 解析器
-
词法环境
-
参数分析
1:函数的在运行的瞬间,生成一个活动对象(Active Object)就是所谓的AO 2:函数接收参数,添加到AO的属性上面,值全部都是undefine,如AO.age=undefine 3:接收实参,形成AO对应的属性值 参考用例: function fn(a){ console.log(a); } /*分析函数参数*/ // 将函数的形参添加为AO属性,属性的默认值为undefined。 fn();// undefined // 接收函数的实参,并覆盖原属性值。 fn(1);// 1 -
变量分析
分析变量声明/分析局部变量: 1:若AO中不存在与声明的变量所对应的属性,则添加AO属性为undefined 2:若AO中已存在与声明的变量所对应的属性,则不做任何修改。 参考用例: function fn(a){ console.log(a);// 1 /*分析变量声明/分析局部变量*/ // 若AO中已存在与声明的变量所对应的属性,则不做任何修改。 var a = 100;// 执行过程:对变量进行赋值 console.log(a);// 100 } fn(1); AO = {} 分析函数参数 AO = {a:undefined} AO = {a:1} 分析局部变量 AO = {a:1} 运行阶段赋值 AO = {a:100} -
函数分析
分析函数的声明: 若AO中存在与函数名所对应的属性,则覆盖原属性为一个函数表达式。 参考用例: function fn(a){ console.log(a);// a(){} /*分析变量声明/分析局部变量*/ // 若AO中已存在与声明的变量所对应的属性,则不做任何修改。 var a = 100;// 执行过程:对变量进行赋值 console.log(a);// 100 /*分析函数声明*/ // 若AO中存在与函数名所对应的属性,则覆盖原属性为一个函数表达式。 function a(){} console.log(a); // 100 } fn(1); AO = {} 分析函数参数 AO = {a:undefined} AO = {a:1} 分析局部变量 AO = {a:1} 运行阶段赋值 AO = {a:100} 分析函数声明 AO = {a:function(){}} 函数声明提升优先高于变量声明提升 总结:作用域链就是函数由内向外所产生的活动对象(AO)的链
-
-
词法分析
词法分析是将字符序列转换为单词(Token)序列的过程 参考用例: let arr= '我是一个词法分析'; let obj = {} 转换成token之后: [ { "type": "Keyword", "value": "let" }, { "type": "Identifier", "value": "arr" }, { "type": "Punctuator", "value": "=" }, { "type": "String", "value": "'我是一个词法分析'" }, { "type": "Punctuator", "value": ";" }, { "type": "Keyword", "value": "let" }, { "type": "Identifier", "value": "obj" }, { "type": "Punctuator", "value": "=" }, { "type": "Punctuator", "value": "{" }, { "type": "Punctuator", "value": "}" } ] -
语法分析
语法分析是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将单词序列组合成语法树 语法分析用例: { "type": "Program", "body": [ { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": { "type": "Identifier", "name": "arr" }, "init": { "type": "Literal", "value": "我是一个词法分析", "raw": "'我是一个词法分析'" } } ], "kind": "let" }, { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": { "type": "Identifier", "name": "obj" }, "init": { "type": "ObjectExpression", "properties": [] } } ], "kind": "let" } ], "sourceType": "script" } 应该能看懂,不需要详细讲解AST在线转换工具:esprima.org/demo/parse.…
-
- 转换
-
实践babel
-
搭建项目工程
1.新建文件夹:npm init初始化项目 2.新建.babelrc文件: { "presets": [], "plugins": [] } 存放在项目的根目录下,该文件用来设置转码规则和插件 3.安装需要的转码包: //ES2015转码规则 npm install --save-dev babel-preset-es2015 ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个 npm install --save-dev babel-preset-stage-0 npm install --save-dev babel-preset-stage-1 npm install --save-dev babel-preset-stage-2 npm install --save-dev babel-preset-stage-3 4.配置.babelrc文件 { "presets": [ "es2015", "react", "stage-2" ], "plugins": [] } 5.安装babel提供的命令行工具: npm install --save-dev babel-cli 6.修改package.json脚本命令: "scripts": { "build": "babel src -d lib" }, 7.执行npm run build (一个简易的babel转码项目搭建成功) -
转码实践(transform)
调用Babel的API进行转码,就要使用babel-core模块 安装下:npm install babel-core --save 参考用例: var babel = require('babel-core'); // 字符串转码 babel.transform('code();', options); // => { code, map, ast } // 文件转码(异步) babel.transformFile('filename.js', options, function(err, result) { result; // => { code, map, ast } }); // 文件转码(同步) babel.transformFileSync('filename.js', options); // => { code, map, ast } // Babel AST转码 babel.transformFromAst(ast, code, options); // => { code, map, ast } transform转码用例: var babel = require('babel-core') var es6Code = 'let x = n => n + 1'; var es5Code = babel.transform(es6Code, { presets: ['es2015'] }).code; 输出转码后的js: "use strict"; var x = function x(n) { return n + 1; }; -
拆分详解:
parser:(输入源码,输出抽象语法树ast) npm install --save-dev @babel/parser var babel = require('@babel/parser'); var es6Code = 'let x = n => n + 1'; var ast = BabelParser.parse(es6Code); transform:(结合babel preset,plugin,转换上述ast,生成新的ast) npm install --save-dev @babel/traverse var traverse = require('@babel/traverse') var new_ast = traverse(ast, { FunctionDeclaration(path) { const node = path.node // 获取函数名称等 path.replaceWith()//替换为新的节点 path.remove() // 删除当前节点 path.skip() 跳过子节点 let copyNode = t.cloneNode(node)//复制当前节点 traverse(copyNode, {}, {}, path)//遍历子树,持续上述操作 } }) generate:(根据新的语法树ast,生成编译后新的代码) npm install --save-dev @babel/generator var generator = require('@babel/generator') var newCode = generate(new_ast); //输出转码后的代码 // 实际上,babel的转换过程就是构建和修改抽象语法树的过程
-
-
上述所有总结:解析 => 转换 => 生成
babel运行机制:
1.经过词法分析
2.经过语法分析
3.得到AST抽象语法树
4.经过babel插件实现递归转换
5.最终得到转换之后的AST抽象语法树
6.通过generate重新将新AST转换成代码输出
** 个人理解笔记文档,勿喷 **