Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react webpack babel 三剑客错误处理 #53

Open
hsipeng opened this issue Jun 4, 2018 · 0 comments
Open

react webpack babel 三剑客错误处理 #53

hsipeng opened this issue Jun 4, 2018 · 0 comments

Comments

@hsipeng
Copy link
Owner

hsipeng commented Jun 4, 2018

错误处理,我们首先想到的就是try catch 。

通常原理就是 try catch 捕获组件 render 中的的报错,之后渲染返回新的报错组件。

在当前类定义之后,借助于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);
}

详细参考实现 react-safe-render/index.js at feature/safe-methods · skiano/react-safe-render · GitHub

当然,这种传统方式在使用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);

遗留代码那么多,难道我要一个个去添加这种wrapper?

由react-transform-catch-errors得到的启发,顺着这一点,最后是找到了babel-plugin-react-transform这个插件,刚好就能满足这个需求。

下面详细配置一下babel
.babelrc

{
"presets": ["es2015","react", "stage-0"],
"plugins": [
  "transform-runtime",
  "transform-decorators-legacy",
    "transform-react-jsx-source",
    ["react-transform", {
      "transforms": [{
        "transform": "catchErrors",
        "imports": [
          "react",
          "errorFallback"
        ]
      }]
    }]
],
.....
  }

其中 catchErrors 和 errorFallback 可以在webpack 中配置 alias


resolve: {
        extensions: ['.js','.jsx', '.less'],
        alias: {
           ....
            catchErrors: path.join(pathConfig.src, 'view/error/babel/CatchErrors.js'),
            errorFallback: path.join(pathConfig.src, 'view/error/handle/ErrorFallback.js')
        }
    }

下面贴一下 CatchErrors.js 的详细实现

export default function catchErrors({ filename, components, imports }) {
    const [React, ErrorReporter, reporterOptions] = imports;
    if (!React || !React.Component) {
      throw new Error('imports[0] for react-transform-catch-errors does not look like React.');
    }
    // if (typeof ErrorReporter !== 'function') {
    //   throw new Error('imports[1] for react-transform-catch-errors does not look like a React component.');
    // }
    return function wrapToCatchErrors(ReactClass, componentId) {
      const originalRender = ReactClass.prototype.render;
      Object.defineProperty(ReactClass.prototype, 'render', {
        configurable: true,
        value: function tryRender() {
          try {
            return originalRender.apply(this, arguments);
          } catch (err) {
            setTimeout(() => {
              if (typeof console.reportErrorsAsExceptions !== 'undefined') {
                let prevReportErrorAsExceptions = console.reportErrorsAsExceptions;
                // We're in React Native. Don't throw.
                // Stop react-native from triggering its own error handler
                console.reportErrorsAsExceptions = false;
                // Log an error
                console.error(err);
                // Reactivate it so other errors are still handled
                console.reportErrorsAsExceptions = prevReportErrorAsExceptions;
              } else {
                throw err;
              }
            });
            return React.createElement(ErrorReporter, {
              error: err,
              filename,
              ...reporterOptions
            });
          }
        }
      });
      return ReactClass;
    };
  }

ErrorFallback.js

import React from 'react'

export default class ErrorFallBack extends React.PureComponent {
    constructor(props) {
        super(props);
    }

    render() {
        let {text="组件加载错误",width='100%',height='100px'} = this.props

        return (
            <div style={{width:width,height:height}}>
                <p>
                    <span>{text}</span>
                </p>
            </div>
        )
    }
}

这样的效果就是,当某个组件render 中报错时,组件内容会替换成 ErrorFallback.js 渲染,统一了生产和开发环境。方便业务处理,当然还有 decorator 方式一个一个加到组件上,如果你愿意的话。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant