react项目开发中遇到的问题
前言
作为一个前端爱好者来说,都想在react上一试生手,那么在搭建react项目开发时,肯定会有这样或者那样的问题,尤其是对初学者来说,下面就个人在开发过程中遇到的问题总结一下,好在有google帮我解决了各种问题。本人项目的技术栈为react+redux+router+ant ui +webpack
。
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.
。
错误信息出现的模块本身没有错误,主要是因为引用的文件constants/index.js
中有export * from ...
提供对外接口导致。通过babel编辑也出现错误,由此可以推断是babel编译es6语法时出现的问题。
是什么导致babel出现这个编译错误问题呢?
通过google发现原来是babel-plugin-transform-runtime的bug导致,用的版本为*6.15.0*还没有修复。
既然没有修复,那么根据这篇讨论有两种方式来解决这个问题:
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"
将其注释掉因为最新 babel-plugin-transform-runtime版本增加了一个polyfill的配置项,可以将其设置false来禁止加载core-js也能解决我们的问题
"plugins": [
["transform-runtime", { "polyfill": false }]
]
react router Link不起作用
在项目中用到的路由跳转时,有时用到react-router提供的Link组件,但是使用Link时需要注意一些细节,否则一不小心就掉坑了。
本人在项目中将页面公共的导航部分提取作为顶级组件,然后将其子组件作为内容区域的展示内容,而子组件使用了Router来进行路由,部分代码如下
//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
为啥会出现这种情况呢?通过这篇讨论找到答案:
使用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有三种记录路由历史记录的方式:hashHistory
、browserHistory
和createMemoryHistory
。他们的区别可以自行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-a
和module-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;
}
};
该种情况具体可以参考这里
未完待续
react项目开发中遇到的问题的更多相关文章
- 手把手教你用webpack3搭建react项目(开发环境和生产环境)(一)
开发环境和生产环境整个配置源码在github上,源码地址:github-webpack-react 如果觉得有帮助,点个Star谢谢!! (一)是开发环境,(二)是生产环境. 一.首先创建packag ...
- React Native开发中自动打包脚本
React Native开发中自动打包脚本 在日常的RN开发中,我们避免不了需要将我们编写的代码编译成安装包,然后生成二维码,供需要测试的人员扫描下载.但是对于非原生的开发人员来说,可能不知如何使用X ...
- 团队项目开发中,常见的版本控制有svn,git
团队项目开发中,常见的版本控制有svn,git
- react + antiDesign开发中遇到的问题记录
react + antiDesign开发中遇到的问题记录 一:页面中子路由失效: antiDesign的官方实例中,会把路由重复的地方给去重,而且路由匹配模式不是严格模式.所以我们需要在util.js ...
- 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获
项目开发中的一些注意事项以及技巧总结 1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...
- Angular 项目开发中父子组件传参
在项目开发中经常会遇到 组件之间传参的问题.今天总结下在使用angular的项目中父子组件传参的问题: 1.父组件向子组件传参: 然后在父组件中 然后在父组件的html中 然后就可以在子组件中使用了 ...
- 《Maven在Java项目开发中的应用》论文笔记(十七)
标题:Maven在Java项目开发中的应用 一.基本信息 时间:2019 来源:山西农业大学 关键词:Maven:Java Web:仓库:开发人员:极限编程; 二.研究内容 1.Maven 基本原理概 ...
- 炼金术(1): 识别项目开发中的ProtoType、Demo、MVP
软件开发是很分裂的,只有不断使用原则和规律,才能带来质量. 只要不是玩具性质的项目,项目应该可以大概划分为0-1,1-10,10-100,100-1000四个种重要阶段.其中,0-1是原型验证性的:1 ...
- 项目开发中的git简单使用
原文地址: https://www.zhuyilong.fun/tech/the-blog-git.html 示例远程仓库地址: https://github.com/zhu-longge/gitWo ...
随机推荐
- Git使用基础篇(zz)
Git使用基础篇 您的评价: 收藏该经验 Git是一个分布式的版本控制工具,本篇文章从介绍Git开始,重点在于介绍Git的基本命令和使用技巧,让你尝试使用Git的同时,体 ...
- spring学习七 spring和dynamic project进行整合
spring和web项目进行整合,其实就是在项目启动时,就创建spring容器,然后在servlet中使用spring容器进行开. 注意:为了页面可以访问到servlet,因此servlet必须放进t ...
- mybatis学习四 mybatis的三种查询方式
<select id="selAll" resultType="com.caopeng.pojo.Flower"> select * from fl ...
- linux下设置mysql表名不区分大小写
原文:http://blog.csdn.net/johnsonvily/article/details/6703902 1.Linux下mysql安装完后是默认:区分表名的大小写,不区分列名的大小写: ...
- Centos7 yum install vim 出现“could not retrieve mirrorlist”
ps:来源 https://www.cnblogs.com/justphp/p/5959655.html 办法一:改dns解析 vim /etc/resolv.conf 添加: nameserver ...
- C++STL queue
queue队列 先进先出 queue<int> q1; q1.push();//插入元素 q1.front();//求队头元素 q1.pop();//删除队头元素 q1.empty();/ ...
- SpringBoot2.0.2 Application调用的三种方式
一.注解 @SpringBootApplication 点开查看源码是由多个注解合成的注解,其中主要的注解有: @SpringBootConfigurati ...
- VIP之Clipper
裁剪器II提供方法从视频流中选择有效区域并丢弃剩余部分. 指定有效区域的方式是从到边界的偏移量,或者给出有效区左上角的像素坐标和有效区的宽及高度. 裁剪器IP核通过读取Avalon-ST视频流中的控制 ...
- progress 进度条
进度条. 属性名 类型 默认值 说明 percent Float 无 百分比0~100 show-info Boolean false 在进度条右侧显示百分比 stroke-width Numb ...
- POJ2289 Jamie's Contact Groups(二分图多重匹配)
Jamie's Contact Groups Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 7721 Accepted: ...