工程化

356 阅读6分钟
Babel的原理是什么?

babel 的转译过程也分为三个阶段,这三步具体是: 1、解析 Parse: 将代码解析⽣成抽象语法树( 即AST ),即词法分析与语法分析的过程 2、转换 Transform: 对于 AST 进⾏变换⼀系列的操作,babel 接受得到 AST 并通过 babel-traverse 对其进⾏遍历,在此过程中进⾏添加、更新及移除等操作 3、⽣成 Generate: 将变换后的 AST 再转换为 JS 代码, 使⽤到的模块是 babel-generator

image.png

如何写一个babel插件?

Babel解析成AST,然后插件更改AST,最后由Babel输出代码

那么Babel的插件模块需要你暴露⼀个function,function内返回visitor

module.export = function(babel){
    return {
        visitor: {}
    }
}

visitor是对各类型的AST节点做处理的地⽅,我们可以把Babel转换的结果打印出来,这样方便知道Babel⽣成了的AST有哪些节点。或者参考 AST explorer

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));
            let parentPath = path.parentPath;
            // 向上遍历父节点
            parentPath && visitor.BinaryExpression.call(this,parentPath);
        }
    }
};

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;

git工作流

GitFlow 是由 Vincent Driessen 提出的⼀个 git操作流程标准。包含如下⼏个关键分⽀:

名称说明
master主分支
develop主开发分支,包含确定即将发布的代码
feature新功能分支,一般一个新功能对应一个分支,对于功能的拆分需要比较合理,以避免后面不必要的代码冲突
release发布分支,发布时候用的分支,一般测试时候发现的bug在这个分支进行修复
hotfixhotfix分支,紧急修bug的时候用
GitFlow 的优势
  • 并⾏开发:GitFlow 可以很⽅便的实现并⾏开发:每个新功能都会建⽴⼀个新的 feature 分⽀,从⽽和已经完成的功能隔离开来,⽽且只有在新功能完成开发的情况下,其对应的 feature 分⽀才会合并到主开发分⽀上(也就是我们经常说的 develop 分⽀)。另外,如果你正在开发某个功能,同时⼜有⼀个新的功能需要开发,你只需要提交当前 feature 的代码,然后创建另外⼀个 feature 分⽀并完成新功能开发。然后再切回之前的 feature 分⽀即可继续完成之前功能的开发。

  • 协作开发:GitFlow 还⽀持多⼈协同开发,因为每个 feature 分⽀上改动的代码都只是为了让某个新的 feature 可以独⽴运⾏。同时我们也很容易知道每个⼈都在⼲啥。

  • 发布阶段:当⼀个新 feature 开发完成的时候,它会被合并到 develop 分⽀,这个分⽀主要⽤来暂时保存那些还没有发布的内容,所以如果需要再开发新的 feature ,我们只需要从 develop 分⽀创建新分⽀,即可包含所有已经完成的 feature 。

  • ⽀持紧急修复:GitFlow 还包含了 hotfix 分⽀。这种类型的分⽀是从某个已经发布的 tag 上创建出来并做⼀个紧急的修复,⽽且这个紧急修复只影响这个已经发布的 tag,⽽不会影响到你正在开发的新 feature 。

注意:

1、feature 分⽀都是从 develop 分⽀创建,完成后再合并到 develop 分⽀上,等待发布。

2、当需要发布时,我们从 develop 分⽀创建⼀个 release 分⽀

3、然后这个 release 分⽀会发布到测试环境进⾏测试,如果发现问题就在这个分⽀直接进⾏修复。在所有问题修复之前,我们会不停的重复发布->测试->修复->重新发布->重新测试这个流程。发布结束后,这个 release 分⽀会合并到 develop 和 master 分⽀,从⽽保证不会有代码丢失。

4、master 分⽀只跟踪已经发布的代码,合并到 master 上的 commit 只能来⾃ release 分⽀和 hotfix 分⽀。

5、hotfix 分⽀的作⽤是紧急修复⼀些 Bug。

它们都是从 master 分⽀上的某个 tag 建⽴,修复结束后再合并到 develop 和 master 分⽀上。

参考软大神 Git工作流程

rebase 与 merge的区别?

git rebase 和 git merge ⼀样都是⽤于从⼀个分⽀获取并且合并到当前分⽀.

假设⼀个场景,就是我们开发的[feature/todo]分⽀要合并到master主分⽀,那么⽤rebase或者merge有什么不同呢?

image.png

  • marge 特点:⾃动创建⼀个新的commit 如果合并的时候遇到冲突,仅需要修改后重新commit
  • 优点:记录了真实的commit情况,包括每个分⽀的详情
  • 缺点:因为每次merge会⾃动产⽣⼀个merge commit,所以在使⽤⼀些git 的GUI tools,特别是commit⽐较频繁时,看到分⽀很杂乱。

image.png

  • rebase 特点:会合并之前的commit历史
  • 优点:得到更简洁的项⽬历史,去掉了merge commit
  • 缺点:如果合并出现代码问题不容易定位,因为re-write了history

因此,当需要保留详细的合并信息的时候建议使⽤git merge,特别是需要将分⽀合并进⼊master分⽀时;当发现⾃⼰修改某个功能时,频繁进⾏了git commit提交时,发现其实过多的提交信息没有必要时,可以尝试git rebase.

git reset、git revert 和 git checkout 有什么区别

git仓库三个组成部分:⼯作区(Working Directory)、暂存区(Stage)和历史记录区(History)。

  • ⼯作区:在 git 管理下的正常⽬录都算是⼯作区,我们平时的编辑⼯作都是在⼯作区完成
  • 暂存区:临时区域。⾥⾯存放将要提交⽂件的快照
  • 历史记录区:git commit 后的记录区

看下三个区的关系:

image.png

git reset、git revert 和 git checkout的共同点:⽤来撤销代码仓库中的某些更改。

然后是不同点:

⾸先,从 commit 层⾯来说:

  • git reset 可以将⼀个分⽀的末端指向之前的⼀个 commit。然后再下次 git 执⾏垃圾回收的时候,会把这个 commit 之后的 commit 都扔掉。git reset 还⽀持三种标记,⽤来标记 reset 指令影响的范围:

    • --mixed:会影响到暂存区和历史记录区。也是默认选项
    • --soft:只影响历史记录区
    • --hard:影响⼯作区、暂存区和历史记录区

注意: 因为 git reset 是直接删除 commit 记录,从⽽会影响到其他开发⼈员的分⽀,所以不要在公共分⽀(⽐如develop)做这个操作。

  • git checkout 可以将 HEAD 移到⼀个新的分⽀,并更新⼯作⽬录。因为可能会覆盖本地的修改,所以执⾏这个指令之前,你需要 stash 或者 commit 暂存区和⼯作区的更改。
  • git revert 和 git reset 的⽬的是⼀样的,但是做法不同,它会以创建新的 commit 的⽅式来撤销 commit,这样能保留之前的 commit 历史,⽐较安全。另外,同样因为可能会覆盖本地的修改,所以执⾏这个指令之前,你需要stash 或者 commit 暂存区和⼯作区的更改。

然后,从⽂件层⾯来说:

  • git reset 只是把⽂件从历史记录区拿到暂存区,不影响⼯作区的内容,⽽且不⽀持 --mixed、--soft 和 --hard。
  • git checkout 则是把⽂件从历史记录拿到⼯作区,不影响暂存区的内容。
  • git revert 不⽀持⽂件层⾯的操作。