最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 为啥套娃?聊聊 babel、jscodeshift 和阿里妈妈的 gogocode

    正文概述 掘金(zxg_神说要有光)   2021-06-26   796

    首先,我是《babel 插件通关秘籍》 掘金小册的作者,对 babel 有源码级的掌握,算是有资格讨论这个话题。

    本来会探讨以下话题:

    • babel 是怎么转换代码的
    • jscodeshift 是怎么转换代码的
    • babel 和 jscodeshift 的区别
    • 为什么不推荐 gogocode

    babel 是怎么转换代码的

    babel 编译流程分为 3 步: parse、transform、generate

    • parse:把源码转成 AST,babel parser(babylon) 支持 esnext 语法,可通过插件支持 typescript、jsx、flow 等语法
    • transform:对 AST 进行转换,通过 visitor 对不同的 AST 进行处理
    • generate:打印转换后的 AST 为目标代码,并生成 sourcemap

    为啥套娃?聊聊 babel、jscodeshift 和阿里妈妈的 gogocode

    转换插件这样写(小册中的一个 linter 的案例):

    const { declare } = require('@babel/helper-plugin-utils');
    
    const noFuncAssignLint = declare((api, options, dirname) => {
        api.assertVersion(7);
    
        return {
            pre(file) {
                file.set('errors', []);
            },
            visitor: {
                AssignmentExpression(path, state) {
                    const errors = state.file.get('errors');
                    const assignTarget = path.get('left').toString()
                    const binding = path.scope.getBinding(assignTarget);
                    if (binding) {
                        if (binding.path.isFunctionDeclaration() || binding.path.isFunctionExpression()) {
                            const tmp = Error.stackTraceLimit;
                            Error.stackTraceLimit = 0;
                            errors.push(path.buildCodeFrameError('can not reassign to function', Error));
                            Error.stackTraceLimit = tmp;
                        }
                    }
                }
            },
            post(file) {
                console.log(file.get('errors'));
            }
        }
    });
    
    module.exports = noFuncAssignLint;
    

    声明 visitor 函数,然后在遍历的过程中会被调用,其中可以拿到 path 和 state 的 api:

    path 是节点之间的关系,每个 path关联父节点和当前节点,path 对象构成一条从当前节点到根结点的路径。state 是遍历过程中的共享数据的机制。

    为啥套娃?聊聊 babel、jscodeshift 和阿里妈妈的 gogocode

    通过 path 的一系列增删改查 AST 的 api 来完成 transform。

    比如下列 api:

    getSibling(key) 
    getNextSibling()
    getPrevSibling()
    getAllPrevSiblings()
    getAllNextSiblings()
    isXxx(opts)
    assertXxx(opts)
    
    insertBefore(nodes)
    insertAfter(nodes)
    replaceWith(replacement)
    replaceWithMultiple(nodes)
    replaceWithSourceString(replacement)
    remove()
    

    jscodeshift 是怎么转换代码的

    jscodeshift 也是代码转换的工具,但是 api 风格不同,是主动查找 AST,然后修改成新的 AST,最后生成代码的形式:

    module.exports = function(fileInfo, api) {
      return api.jscodeshift(fileInfo.source)
        .findVariableDeclarators('foo')
        .renameTo('bar')
        .toSource();
    }
    

    jscodeshift 的优势是更简洁。

    但是 jscodeshift 能代替 babel 么? 可以看下大牛给出的答案:

    babel 和 jscodeshift 的不同

    jscodeshift 的 parser 是 recast,曾经有 babel 的维护者想结合这两者:

    github.com/facebook/js…

    利用 recast 做 parse,然后基于 babel parser 做转换。

    下面有一个很精彩的回复,明确了 babel 和 jscodeshift 的不同:

    为啥套娃?聊聊 babel、jscodeshift 和阿里妈妈的 gogocode

    我来梳理一下:

    babel 的 transform api 是visitor 风格,也就是声明对什么 ast 做什么处理,然后在遍历过程中被调用,这种不和具体遍历方式耦合的写法是一种设计模式(访问者模式),处理再复杂的场景也能应对。就是处理简单场景显得稍微啰嗦点。

    jscodeshift 是 collection 风格,类似 jquery,主动查找 ast,放到集合中操作,适合处理简单场景,要知道每种 ast 是怎么查找到的,然后做转换,要处理很多很多 case,万一查找路径不对,那可能就漏掉了一些情况,比起 babel 来,很难在复杂场景下没有 bug。

    就像 jquery 和 mvvm 的区别一样,复杂场景还是 mvvm 的方式(babel)靠谱,不会漏掉一些 dom 没处理。(只是一个类比)

    所以,简单场景可以用 jscodeshift,而所有场景都可以用 babel。

    babel 的 visitor 的优点就是设计模式中访问者模式的优点,不和具体遍历方式耦合易于复用。

    为什么不推荐 gogocode

    gogocode 是这两天阿里妈妈出的 ast 修改工具,基于 babel 做了一层封装,说是简化了 ast 操作。

    api 类似这样:

    $(code)
      .find('var a = 1')
      .attr('declarations.0.id.name', 'c')
      .root()
      .generate();
    

    没错,基于 babel 的 visitor 风格的 api 封装出了 jscodeshift 的 collection 风格的 api。

    本来 babel 的 visitor 虽然写起来麻烦一些,但是所有路径都能够处理到,而改成 collection 风格之后,一旦落掉了某条路径没错里,就会 bug。处理的 case 特别多,不适合复杂场景。

    babel 本来的 visitor 模式是一种优点,结果又在上层封装出了 collection api。。如果想这么封装,为啥不直接基于 jscodeshift 呢。。。我没看懂这波操作。

    为啥套娃?聊聊 babel、jscodeshift 和阿里妈妈的 gogocode

    我不看好这个 babel 套娃,我没有自信保证复杂场景下能够处理所有路径而不遗漏 case,复杂场景我选择直接用 babel 的 api。

    总结

    babel 是访问者设计模式的实现,分离了遍历方式和对 AST 的操作,使得操作可以复用,jscodeshift 是 collection 风格,类似 jquery,复杂场景容易落掉 case。

    gogocode 基于babel 实现了 collection 风格,不看好,容易落下 case。

    一句话总结: 简单场景可以用 jscodeshift,所有场景都可以用 babel,不怎么推荐 gogocode。

    最后打波广告,想学 babel 的可以看我的小册《babel 插件通关秘籍》,手写babel 的案例代码已经 push,小册马上更新。


    起源地下载网 » 为啥套娃?聊聊 babel、jscodeshift 和阿里妈妈的 gogocode

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元