给react加try-catch

最近在一个使用fis构建的react.js项目里遇到个问题,render函数里如果发生了运行时错误,比如说某个对象没有判断就直接去访问其属性,那我所知道的就是,页面不正常了,特别是有嵌套子组件的时候,我可得一个个一层层去排查判断,去加try-catch。。。

好像react的开发体验不应该是这样子的。

通常来说,使用react的时候都配合以webpack构建,再加个webpack-dev-client,不仅有js live reload还能hot module reload,不离开编辑器的情况下就能一直调试下去。而且当出现运行时错误时,会有明确的error stack打印在页面上。为什么使用fis构建的就不行呢?

先就自己遇到的这个问题来说,我通过多次手动try-catch的方式,找到了render失败的原因,那么这个“手动”的方式是不是可以自动?通常就是monkeypatch,在当前类定义之后,借助于js这种动态修改类定义的特性,可以这样子:

var unsafeCreateClass = React.createClass;
React.createClass = function(spec) {
    var unsafeRender = spec['render'];
    spec['render'] = function() {
        try {
            return unsafeRender.apply(this, arguments);
        } catch(e) {
            console.log(e);
        }
    }

    return unsafeCreateClass.apply(this, arguments);
}

社区的一个详细实现:https://github.com/skiano/react-safe-render/blob/feature/safe-methods/index.js 当然,这种传统方式在使用ES6的组件上是无效的,所以针对另一种写法可以这样子:

class MyComponent extends React.Component {
    render() {
        return <div>render something here</div>;
    }
}
function wrapTryCatch(Component) {
    let oldRender = Component.prototype.render;
    Component.render = function() {
        try {
            oldRender.apply(this, arguments);
        } catch(e) {
            console.log(e);
        }
    }

    return Component;
}

exports default wrapTryCatch(MyComponent);

看起来大同小异,不过需要在每个Component的实现之后再wrap一下,包装出一个新的组件出来。而这种wrapper,借助于es7的新语法,decorator,(引入babel-plugin-transform-decorators)又可以这么写:

@wrapTryCatch
class MyComponent extends React.Component {
    render() {
        return <div>render something here</div>;
    }
}

社区实现:https://www.npmjs.com/package/react-try-catch-render

然而,这还是不能让人满意的,毕竟遗留代码那么多,难道我要一个个去添加这种wrapper? 想想看,现在连decorator这种新语法都能通过babel插件来支持了,为什么不能再通过类似方法来把decorator都自动加进去呢?

事实上,react-try-catch-render(也就是上个例子)这个文档是指出其由react-transform-catch-errors得到的启发,顺着这一点,最后是找到了babel-plugin-react-transform这个插件,刚好就能满足这个需求。而且,它本身已经内置在webpack-dev-client中,所以webpack构建的开发方式才会如此方便看到错误。

按照给出的步骤,自行安装完依赖之后,在fis中对应的babel plugins配置部分添加:

"plugins": [
        ["react-transform", {
          "transforms": [{
            "transform": "react-transform-catch-errors",
            "imports": ["react", "redbox-react"]
          }]
        }]
      ]

配置选项中的imports传入了两个参数,这两个参数是react-transform会传给transform插件使用的,其中redbox-react 是一个自定义的错误处理组件,之前在webpack构建方式下的开发经常看到的红色框框原来就是它了!在实际使用中,可以按需替换,比如说实现badjs上报等。最后试了一下,在fis的构建方式下,也成功看到了红色框框,以后开发过程出现运行错误就页面不会安安静静地失败了。

当然,到这里为止都只是在关心render函数的报错,其它阶段的回调,其实都是类似的实现。

最后一种方式给了很大的启发和想象空间,现在借助于babel的帮助,我们可以在语法层面对js进行增强,在构建阶段就完成对功能的补充,这种方法现在看来,work like a charm!

惹不住还是看了下babel transform插件是如何开发实现的,文档在此。babel作为一个源码转换编译器,是一个源码->ast->源码的过程,而transform插件所做的事就是在ast->源码的阶段。在对AST遍历的过程,按插件接口形式提供visitor,可以细致到对每个AST 节点进行修改(添加,删除,替换等)。所谓的visitor其实就是访问节点时候的钩子/回调。给visitor传入的参数path,给我一种一沙一宇宙的感觉,path提供的属性和操作就可以勾画出整个AST。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

深入Go语言网络库的基础实现

Go语言的出现,让我见到了一门语言把网络编程这件事情给做“正确”了,当然,除了Go语言以外,还有很多语言也把这件事情做”正确”了。我一直坚持着这样的理念——要做...

3087
来自专栏Java帮帮-微信公众号-技术文章全总结

同步与异步/阻塞与非阻塞/回调【面试+工作】

  打个比方,比如我们去购物,如果你去商场实体店买一台空调,当你到了商场看中了一款空调,你就想售货员下单。售货员去仓库帮你调配物品。这天你热的实在不行了。就催着...

762
来自专栏Golang语言社区

Golang 之协程详解

  对于 进程、线程,都是有内核进行调度,有 CPU 时间片的概念,进行 抢占式调度(有多种调度算法)

1485
来自专栏大内老A

《WCF技术剖析》博文系列汇总[持续更新中]

近半年以来,一直忙于我的第一本WCF专著《WCF技术剖析(卷1)》的写作,一直无暇管理自己的Blog。在《WCF技术剖析(卷1)》写作期间,对WCF又有了新的感...

1708
来自专栏钟志远的专栏

【腾讯云的1001种玩法】云服务器搭建Python爬虫环境

在上一篇文章中,我们已经学会了在云服务器上搭建Python环境了,假设你已经在云服务器上搭建好了Python环境,我们将进入下一步:搭建Python爬虫环境。

3.2K2
来自专栏编程

花5分钟教会你用python搭建出一个动态网页,零基础也能学得会

本套录屏教程,是为初学者准备的,确保非计算机专业的也能跟着一步一步玩转python,既培养出兴趣,又玩出点东西。 上一讲介绍了我们如何用python发布一个静态...

1927
来自专栏熊二哥

快速入门系列--WCF--04元数据和异常处理

本章节将进行元数据和异常处理的介绍,这部分内容概念型比较强,可以快速浏览一下就好。 ? 客户端和服务器借助于终结点进行通信,服务的提供者通过一个或者多个终结点...

1848
来自专栏木可大大

漫谈版本控制系统

当我们单独使用这些文件时,按照上述方式可以很好的管理文件,但是,如果现在有两个人同时修改这份文件,那么,其中一人对文件修改的内容会被另一人的内容所覆盖,这是我们...

49117
来自专栏程序员的知识天地

维护Python代码的5种工具

随着软件项目进入“维护模式”,对可读性和编码标准的要求很容易落空(甚至从一开始就没有建立过那些标准)。然而,在代码库中保持一致的代码风格和测试标准能够显著减轻维...

1662

Cloudify中的部署组合

[这篇文章是由DeWayne Filppi撰写的。]

2896

扫码关注云+社区