babel 的转译过程也分为三个阶段,这三步具体是:
更具体的原理可以移步如何写一个babel
Babel解析成AST,然后插件更改AST,最后由Babel输出代码
那么Babel的插件模块需要你暴露一个function,function内返回visitor
module.export = function(babel){
return {
visitor:{
}
}
}
visitor是对各类型的AST节点做处理的地方,那么我们怎么知道Babel生成了的AST有哪些节点呢?
很简单,你可以把Babel转换的结果打印出来,或者这里有传送门: AST explorer (opens new window)
这里我们看到 const result = 1 + 2
中的1 + 1
是一个BinaryExpression
节点,那么在visitor中,我们就处理这个节点
var babel = require('babel-core');
var t = require('babel-types');
const visitor = {
BinaryExpression(path) {
const node = path.node;
let result;
// 判断表达式两边,是否都是数字
if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {
// 根据不同的操作符作运算
switch (node.operator) {
case "+":
result = node.left.value + node.right.value;
break
case "-":
result = node.left.value - node.right.value;
break;
case "*":
result = node.left.value * node.right.value;
break;
case "/":
result = node.left.value / node.right.value;
break;
case "**":
let i = node.right.value;
while (--i) {
result = result || node.left.value;
result = result * node.left.value;
}
break;
default:
}
}
// 如果上面的运算有结果的话
if (result !== undefined) {
// 把表达式节点替换成number字面量
path.replaceWith(t.numericLiteral(result));
}
}
};
module.exports = function (babel) {
return {
visitor
};
}
插件写好了,我们运行下插件试试
const babel = require("babel-core");
const result = babel.transform("const result = 1 + 2;",{
plugins:[
require("./index")
]
});
console.log(result.code); // const result = 3;
与预期一致,那么转换 const result = 1 + 2 + 3 + 4 + 5;
呢?
结果是: const result = 3 + 3 + 4 + 5;
这就奇怪了,为什么只计算了1 + 2
之后,就没有继续往下运算了?
我们看一下这个表达式的AST树
你会发现Babel解析成表达式里面再嵌套表达式。
表达式( 表达式( 表达式( 表达式(1 + 2) + 3) + 4) + 5)
而我们的判断条件并不符合所有的,只符合1 + 2
// 判断表达式两边,是否都是数字
if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {}
那么我们得改一改
第一次计算1 + 2
之后,我们会得到这样的表达式
表达式( 表达式( 表达式(3 + 3) + 4) + 5)
其中 3 + 3
又符合了我们的条件, 我们通过向上递归的方式遍历父级节点
又转换成这样:
表达式( 表达式(6 + 4) + 5)
表达式(10 + 5)
15
// 如果上面的运算有结果的话
if (result !== undefined) {
// 把表达式节点替换成number字面量
path.replaceWith(t.numericLiteral(result));
let parentPath = path.parentPath;
// 向上遍历父级节点
parentPath && visitor.BinaryExpression.call(this, parentPath);
}
到这里,我们就得出了结果 const result = 15;
那么其他运算呢:const result = 100 + 10 - 50
>>> const result = 60;
const result = (100 / 2) + 50
>>> const result = 100;
const result = (((100 / 2) + 50 * 2) / 50) ** 2
>>> const result = 9;
上述答案来源于cnode帖子 (opens new window)
更详实的教程移步官方的插件教程 (opens new window)
GitFlow 是由 Vincent Driessen 提出的一个 git操作流程标准。包含如下几个关键分支:
名称 | 说明 |
---|---|
master | 主分支 |
develop | 主开发分支,包含确定即将发布的代码 |
feature | 新功能分支,一般一个新功能对应一个分支,对于功能的拆分需要比较合理,以避免一些后面不必要的代码冲突 |
release | 发布分支,发布时候用的分支,一般测试时候发现的 bug 在这个分支进行修复 |
hotfix | hotfix 分支,紧急修 bug 的时候用 |
GitFlow 的优势有如下几点:
feature
分支,从而和已经完成的功能隔离开来,而且只有在新功能完成开发的情况下,其对应的 feature
分支才会合并到主开发分支上(也就是我们经常说的 develop
分支)。另外,如果你正在开发某个功能,同时又有一个新的功能需要开发,你只需要提交当前 feature
的代码,然后创建另外一个 feature
分支并完成新功能开发。然后再切回之前的 feature
分支即可继续完成之前功能的开发。feature
分支上改动的代码都只是为了让某个新的 feature
可以独立运行。同时我们也很容易知道每个人都在干啥。feature
开发完成的时候,它会被合并到 develop
分支,这个分支主要用来暂时保存那些还没有发布的内容,所以如果需要再开发新的 feature
,我们只需要从 develop
分支创建新分支,即可包含所有已经完成的 feature
。hotfix
分支。这种类型的分支是从某个已经发布的 tag 上创建出来并做一个紧急的修复,而且这个紧急修复只影响这个已经发布的 tag,而不会影响到你正在开发的新 feature
。然后就是 GitFlow 最经典的几张流程图,一定要理解:feature
分支都是从 develop
分支创建,完成后再合并到 develop
分支上,等待发布。
当需要发布时,我们从 develop
分支创建一个 release
分支
然后这个 release
分支会发布到测试环境进行测试,如果发现问题就在这个分支直接进行修复。在所有问题修复之前,我们会不停的重复发布->测试->修复->重新发布->重新测试这个流程。
发布结束后,这个 release
分支会合并到 develop
和 master
分支,从而保证不会有代码丢失。master
分支只跟踪已经发布的代码,合并到 master
上的 commit 只能来自 release
分支和 hotfix
分支。hotfix
分支的作用是紧急修复一些 Bug。
它们都是从 master
分支上的某个 tag 建立,修复结束后再合并到 develop
和 master
分支上。
更多工作流可以参考阮老师的Git 工作流程 (opens new window)
git rebase 和 git merge 一样都是用于从一个分支获取并且合并到当前分支.
假设一个场景,就是我们开发的[feature/todo]分支要合并到master主分支,那么用rebase或者merge有什么不同呢?
因此,当需要保留详细的合并信息的时候建议使用git merge,特别是需要将分支合并进入master分支时;当发现自己修改某个功能时,频繁进行了git commit提交时,发现其实过多的提交信息没有必要时,可以尝试git rebase.
这个问题同样也需要先了解 git 仓库的三个组成部分:工作区(Working Directory)、暂存区(Stage)和历史记录区(History)。
三个区的转换关系以及转换所使用的命令:
git reset、git revert 和 git checkout的共同点:用来撤销代码仓库中的某些更改。
然后是不同点:
首先,从 commit 层面来说:
注意:因为 git reset 是直接删除 commit 记录,从而会影响到其他开发人员的分支,所以不要在公共分支(比如 develop)做这个操作。
然后,从文件层面来说:
← webpack面试题 如何写一个babel →