react项目开发中遇到的问题

前言

做为一个前端爱好者来讲,都想在react上一试生手,那么在搭建react项目开发时,确定会有这样或者那样的问题,尤为是对初学者来讲,下面就我的在开发过程当中遇到的问题总结一下,好在有google帮我解决了各类问题。本人项目的技术栈为react+redux+router+ant ui +webpackjavascript

export * from 'x-module'在配置babel-plugin-transform-runtime插件下致使不可用

export * from 'x-moudule'是es6经常使用的语法糖,在es6中使用它应该在正常不过。乖乖的,在项目跑起来后一直报错,死活通不过。
浏览器控制台报错,webpack的babel loader编译时也有报错:You may need an appropriate loader to handle this file type.html


错误信息出现的模块自己没有错误,主要是由于引用的文件constants/index.js中有export * from ...提供对外接口致使。经过babel编辑也出现错误,由此能够推断是babel编译es6语法时出现的问题。前端

是什么致使babel出现这个编译错误问题呢?java

经过google发现原来是babel-plugin-transform-runtime的bug致使,用的版本为*6.15.0*尚未修复。

既然没有修复,那么根据这篇讨论有两种方式来解决这个问题:node

  • Comment this line 'defineProperty: "object/define-property"' in babel-plugin-transform-runtime/lib/definitions.js can temporarily solve this problem.
    即在node_modules/babel-plugin-transform-runtime/lib/definitions.js中找到这行defineProperty: "object/define-property"将其注释掉
    react

  • 由于最新 babel-plugin-transform-runtime版本增长了一个polyfill的配置项,能够将其设置false来禁止加载core-js也能解决咱们的问题
"plugins": [
    ["transform-runtime", { "polyfill": false }]
  ]

react router Link不起做用

在项目中用到的路由跳转时,有时用到react-router提供的Link组件,可是使用Link时须要注意一些细节,不然一不当心就掉坑了。webpack

本人在项目中将页面公共的导航部分提取做为顶级组件,而后将其子组件做为内容区域的展现内容,而子组件使用了Router来进行路由,部分代码以下git

//App.jsx的render方法
render(){
        return (
            <MainLayout> //注意这个地方,MainLayout组件是做为Router的父组件
                <Router history={history}>
                    <Route path="/" component={Home} />
                    <Route path="/dataSource/create" component={CreateForm} />
                    <Route path="/about" component={About} />
                </Router>
           </MainLayout>
    );
    }

   //MainLayout的render方法
render(){
    return(
        <aside className="ant-layout-sider">
              <div className="ant-layout-logo"></div>
              <Menu mode={mode} theme="dark">
                    <SubMenu key="index_1">
                        <Menu.Item key="index_1_1">
                            <Link to="/dataSource/create">建立数据</Link>
                        </Menu.Item>
                        .
                        .
                        .
              </Menu>
            </aside>
    )
}

当点击导航进行路由时,控制报错提示程序员

Link.js:126 Uncaught TypeError: Cannot read property 'push' of undefined

为啥会出现这种状况呢?经过这篇讨论找到答案:es6

使用Link组件必须做为react-router提供的Router组件的子组件,也就是说,Link必须位于Router内部,不然Link不起做用

正因如此:
在执行Link组件的内部点击事件处理函数时,由于获取不到router信息致使执行这行代码this.context.router.push(_location);出错。由于Link不在任何Router内部

如何优雅组织每一个页面共有的部分,如导航和header、footer

在一个web应用系统中,尤为是企业级后台应用系统中,页面导航是一个页面不可或缺的部分。通常的后台应用有三个大的部分组成。就拿本人系统来讲吧,页面上面有header部分,页面左侧有导航部分,页面中间有主内容展现区域

那么问题来了,对于初次搭建react项目来讲,如何优雅组织一个页面中各自独有的主内容组件和公共组件部分呢?

下面就本人摸索的过程来进行描述。

开始抽取公共的导航组件MainLayout,具体render方法以下:

render() {
    const {layout, actions} = this.props;
    return (
        <div className={layout.collapse ? "ant-layout-aside ant-layout-aside-collapse" : "ant-layout-aside"}>
           <Header userName={layout.userName}/>  //页面顶部header部分,抽取一个组件
            <Aside collapse={layout.collapse} actions={actions}/> //页面左侧导航部分,抽取一个组件
            <div className="ant-layout-main">
                 <div className="ant-layout-main-header"></div>
                 <div className="ant-layout-container">
                    <div className="ant-layout-content">
                        {this.props.children}    //MainLayout组件的全部子组件做为页面的主内容展现组件
                    </div>
                 </div>
             </div>
       </div>
        );
}

而后,全部主内容区组件被包裹在MainLayout组件中,如系统首页主内容区组件的render方法以下:

render(){
        return(
            <MainLayout>
                <div className="welcome-pic">
                    <div>欢迎来到xxx平台</div>
                </div>
            </MainLayout>
        )
    }

最后,你会发现每一个主内容区域要引入MainLayout模块来配置每一个页面公共部分,这样作能够实现功能,可是对于有这洁癖的程序员来讲,这实在是太low,由于每一个页面重复着引入与本页面主内容区域没有太大关系的无用功。

一直没有找到更好的组织方式时,忽然看到react-router的路由配置一节里讲到路由能够嵌套,嵌套的路由对应的展现组件能够做为被嵌套路由对应组件的子组件。看到这里我就有了更好的解决方案,在配置应用路由时,让MainLayout做为根路由,全部路由都做为它的子路由。

render(){
        return (
            <Router history={history}>
                <Route path="/" component={MainLayout}>
                    <IndexRoute component={Home}/>
                    <Route path="/dataSource/list" component={DataSourceList} />
                    <Route path="/dataSource/create" component={QueryForm} />
                </Route>
            </Router>
    );
}

使用react-router的browserHistory配置路由历史记录时刷新或者单独打开一个页面时出现NOT FOUND

react-router有三种记录路由历史记录的方式:hashHistorybrowserHistorycreateMemoryHistory。他们的区别能够自行google。

单说一下browserHistory,他是根据HTML5的history API来实现的,基于浏览器浏览记录来实现路由的。它会新建立一个浏览器浏览记录形式的路由URL。

react-router官方推荐browserHistory做为路由的历史记录,缘由主要是:

  • browserHistory能够支持服务端渲染,hashHistory却不能
  • browserHistory可以有一个更干净的URL环境

可是browserHistory在某个指定的路由刷新或者新打开时,会出现not found的状况,缘由以下:

因为是单页面应用,而browserHistory是基于浏览器浏览记录来进行路由的,你刷新一个页面或者导航到某个页面的路由时,至关于新打开了一个单页应用,而要刷新或者要打开的页面是这个单页应用的第一个页面,这时这个新的单页应用尚未浏览记录,因此出现not found状况。

解决这种状况就须要进行服务端改造,具体有两种方法,详情请猛戳服务端改造

模块按需加载

应用系统比较大的时候,若是一次性把全部路由信息都进行加载完,会致使单页应用首屏加载文件过大,可能会出现白屏的状况;另外有些可能绝大部分状况下不会用到的模块都加载进来。

这种状况的解决办法尽量实现模块的按需加载。配合着webpack,能够很容易实现模块的按需加载。

require.ensure(["module-a", "module-b"], function(require) {
  var a = require("module-a");
  // ...
});

例如本人在项目中使用的按需加载,当点击页面上按钮时,按需加载Todo组件模块并展现在页面中。

_onClick(){
        let self = this;
        let text = 'hello boy! welcome to the world of react!';
        require.ensure(['../todos/Todo.jsx'], function(require){
            var Todo = require('../todos/Todo.jsx');
            self.Todo = Todo;
            self.props.actions.setMessage(text);
        })
        // this.props.actions.setMessage(text);
    }
    render(){
        let {welcomeText} =  this.props;
        let Todo = this.Todo;
        return(
            <div>
                <Button type="large" onClick={this._onClick.bind(this)}>获取欢迎词</Button>
                <span>{this.props.welcomeText}</span>
                {
                    Todo ? <Todo/> : ''
                }

            </div>
        )
    }

使用require.ensure方法能够实现模块的按需加载,例如上面例子在依赖module-amodule-b模块,webpack会将两者打包成一个单独的文件进行按需加载,从而实现模块的按需加载。具体能够参考这篇文章深刻浅出React(二):React开发神器Webpack

html-webpack-plugin与atool-build混用的坑

具体的能够这篇文章file-loader引发的html-webpack-plugin坑

defaultValue与value相关的受控组件与非受控组件

具体能够参考本人这篇总结:浅谈react的受控组件与非受控组件

调用组件的setState报警告called setState() on an unmounted component

这个主要是发生在异步处理的状况下,例如事件或者异步请求的状况,这时在回调函数中调用component的setState方法时,可能会出现当前组件尚未mounted到dom中,此时调用该方法会报以下错误警告:

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the LineSuggest component.

因为ES6下的react component是extends React.Component,因此component的isMount()方法不可用

虽然isMount()方法为一个反模式,不推荐;可是为了解决问题,为此网上有一些对应的解决方法,以下

  • componentDidMount函数中设置一个flag, 而后在componentWillUnMount中重置该flag,具体代码以下:
componentDidMount(){
        this.mounted = true; //flag
        listener = document.body.addEventListener('click', ()=>{
            if(this.mounted){
                this.setState({open: false});
            }
        }, false)
    }

    componentWillUnMount(){
        this.mounted = false; //重置flag
        listener && document.body.removeEventListener('click', listener, false);
    }

该种状况具体能够参考这里

  • hack一个isMount方法。
    因为React.findDOMNode(component)是在component mounted时才能正常使用的方法,不然会抛异常;因此利用这个状况能够hack一个方法,具体以下:
function isMounted (component) {
  // exceptions for flow control :(
  try {
    React.findDOMNode(component);
    return true;
  } catch (e) {
    // Error: Invariant Violation: Component (with keys: props,context,state,refs,_reactInternalInstance) contains `render` method but is not mounted in the DOM
    return false;
  }  
};

该种状况具体能够参考这里

未完待续

相关文章
相关标签/搜索