React总结和遇到的坑
一、react项目
前端react后端node:https://github.com/GainLoss/react-juejin
前端react后端Pyton:https://github.com/GainLoss/python-react
二、react理解
1.react优点
组件、单向数据、虚拟DOM、生态系统、JSX
组件:一个组件就像一个接受道具作为输入并输出声明的函数接口。 UI应该由尽可能多的组件组成可以最大限度地提高可重用性。
单项数据:React使用单向数据流,因此只会更改数据结果更改UI。如果您使用Redux,则可以在全局应用程序中更改数据存储,组件监听那里的变化。
具体流程:(1)操作通过将数据传输到Dispatcher来启动数据流。(2)Dispatcher接收操作并将数据传递到所有store。(3)store包含应用程序的状态,并在收到相关操作后对其进行更新。(4)视图从store接收数据并重新呈现组件。
虚拟DOM:虚拟DOM允许UI在UI变化中具有高效性。这个组件应呈现的内部和之间的过程实际渲染是什么,它允许React在不同的工作平台。
生态系统:react有丰富的生态,有很多插件可以供开发者使用。
2.react和vue的对比
相同之处:(1)使用虚拟DOM (2)组件化
不同之处:(1)react如果状态变化,是从根节点开始重新渲染;vue是确定到某一个节点需要变化,只是更新这个节点;(2)react是用jsx渲染模板;vue使用的是Template模板渲染,还是Html模板;
选取:react生态比较好,插件多;vue学习难度低;
三、在创建项目的时候,各种坑和总结
关于react-router的坑和总结:
1.react-router现在版本是4,所以如果将嵌套的路由都放一块的话,就会出错:
报这种错误Warning: You should not use <Route component> and <Route children> in the same route;
所以将需要嵌套的路由放到你的组件中
2.在页面有导航的时候,我们需要实现点击导航跳转页面的功能,这时候我们需要在导航的render中这样写:
我们用Link: <li key={index}><Link to="/+{item.name}">{item.name}</Link></li> 而且我们需要引入一个import { Link } from 'react-router-dom'
3.我们想要获取当前页面的路由或者说是浏览器中地址栏的地址:
在当前组件中props里面有当前的地址,找一个恰当的生命周期钩子然后再这个函数中this.props里面就可以获取到,或者可以直接在render中return中元素里的{}里面直接获取
4.react-router路由有两种形式:
HashRouter这种的带hash #
BrowerBouter这种是利用html5中history的,这种是需要服务器端支持的,兼容性不是很好
6.有时候需要实现,根据当前地址实现对应导航显示:
在当前组件中获取this.props,然后将这个值赋给this.state,在需要当前组件中渲染导航组件的位置,在导航组件中写<Nav urlLocation={this.state.url}/>,最后在导航组件中获取到this.props里面包含的就是当前路由地址
7.有时候我们需要实现点击排序,给当前路由添加参数:我用的是Link这种方法
组件里面:
<div style={homeMainLeftTwodiv2}> { this.state.HomeMenutwo.map((item,index)=> <span key={index} className={this.query===item.sort?"homesortactive":''}> <Link to={{pathname:"/home",search: 'sort='+item.sort,}}>{item.name}</Link> <Route path="/home/:id" component={Home}/> </span> ) } </div>
在router路由中:还是和之前一样的
8.想只是改变地址栏的地址而不是调转页面,就比如我当前页面有两个选择,点击选择,改变地址栏地址,但是还是当前的组件
import createHashHistory from 'history/createHashHistory' const history = createHashHistory() //点击一级菜单事件 clickTop(event) { let that = this; let tab = event.currentTarget.dataset.tab; that.setState({ menuOne: tab }) console.log(that.setState) history.push({ pathname: '/home/'+tab, }) } { this.state.HomeMenuTop.map((item, index) => <span key={index} className={this.state.menuOne === item.tab ? "homesortactive" : ""} data-tab={item.tab} onClick={this.clickTop}> {item.name}{this.state.menuOne} </span> ) }
9.react-router2迁移到react-router4的特性:https://github.com/gmfe/Think/issues/6
10.在npm start之后页面出现这种错误Uncaught TypeError: Super expression must either be null or a function, not undefined
原因是:在编写组件的时候React.Component写错了
11.在react中使用事件:
如果没有用bind的话那么点击事件只在页面一开始的时候执行,不会在元素点击的时候执行,只有用this.click.bind(this)就可以,在点击元素的时候也执行这个函数
class FLeft extends React.Component{ constructor(props){ super(props) this.state={ currentIndex:0, } } clickTab(index,e){ this.setState({currentIndex:index}); } render(){ var styles={ width:'100%', height:'300px' } var index=this.state.currentIndex; return ( <div className="f_left"> <textarea className="f_left_text"></textarea> <div className='f_left_text_choose'> <span>图片</span> <span>链接</span> <span>话题</span> <button>发布</button> </div> <div className='choose_tab'> <span onClick={this.clickTab.bind(this,0)} className={this.state.currentIndex===0?'active':''}>推荐</span> <span onClick={this.clickTab.bind(this,1)} className={this.state.currentIndex===1?'active':''}>动态</span> </div> </div> ) } }
12.在react中使用iconfont (Unicode编码)
一般使用的时候直接用<i className="iconfont">编码</i>这样就可以了,但是当你循环出来i标签的时候,那么iconfont使用的是字符串表示unicode编码,那么我们需要改写一下编码
想使用字符串来传递的话,只要将 "" 改为 “\ue655” 即可。
13.在react中循环中设置点击事件
这个里面只有把点击事件设置成异步的才不会循环出全部的元素的点击事件
1 <HomeHeader> 2 { 3 this.props.homeheader.map((item)=>{ 4 var id=item.id; 5 return <HomeHeaderItem onClick={()=>this.props.clickChangeList(id)} key={item.id}><Link to={{pathname:'/home/'+id}}>{item.val}</Link></HomeHeaderItem> 6 }) 7 } 8 </HomeHeader>
14.配置文件的路径:
有时候在一个组件中要引入一个新的组件,可能路径需要写很多的../,现在我们需要设置一个单词表示父级文件夹
背景:"react": "^16.5.2","react-dom": "^16.5.2","react-scripts": "2.0.4"
打开node_modules中的react-script中webpack.config.dev.js这个文件(是开发环境配置),里面的
resolve:{ alias: { 'react-native': 'react-native-web', '@': paths.appSrc //对应的是相同文件夹下面的paths.js文件夹里面 }, }
设置完成之后,在组件中引入import Header from '@/common/header/header.js'
关于在react中引入css或者引入外部文件:
1.在react中怎么引入css文件呢:
{ test:/\.css$/, exclude: /node_modules/, loader: "style-loader!css-loader" }
2.html元素中添加css样式的时候:
两种:一种是class名字填写,但是在jsx中class需要写成className; 二种是在行间写样式style={{}}里面放置样式 ;三种是style={colorstyle}里面放置一个变量
关于react中元素渲染和数据交互的总结:
this.state.HomeMenuone.map((item,index)=>
<span key={index}>{item.name}</span>
)
如果不加的话,会出这种错误
2.在react中引入图片的话,当然之前的直接放在src中也是可以的,但是比较好的方法是:
import img from './right.png' <img src={img}/>
这种方法有可能会出现这种错误:出现Uncaught Error: Module parse failed: Unexpected character '�' (1:0),这种我的解决办法是重新npm install就可以了
3.在前后台交互的时候我用的是fetch实现的:
注意的是在fetch成功调取之后将结构赋给一个数组的时候,在then中需要用这个才能设置好this.setState({result:data}),然后map这个result
4.使用fetch获取后台数据:
fetch("/list/show").then((response)=>{ if(response&&response.status===200){ return response.json(); } }).then((data)=>{ this.setState({result:data}) });
5.上面的是直接获取后台的数据,但是有时候我们需要用fetch给后台传参数(后台用的是express):
我们如果用get就是和上面是一样的,但是如果我们用post是有区别的和之前的写法,有两种写法:
一种:
if(this.props.location.search){ let idValue=this.props.location.search.split('=')[1]; fetch('/list/detail',{method:'POST',headers: { 'Content-Type': 'application/x-www-form-urlencoded' },body: `id=${idValue}`}).then((response)=>{ return response.json() }).then((result)=>{ this.setState({result:result}) }) }
二种:
if(this.props.location.search){ let idValue=this.props.location.search.split('=')[1]; let formData = new FormData(); formData.append("id",123); fetch('/list/detail',{method:'POST',body: formData}).then((response)=>{ return response.json() }).then((result)=>{ this.setState({result:result}) }) }
上面这两种都可以;
另外后台因为用的是express,所以你需要在dev-server.js中添加一个body-parser这个模块是为了解析客户端请求的body的内容,内部使用JSON编码,url编码处理以及对文件的上传处理。
关于react中事件处理的总结:
1.当我们想点击元素,相应的改变state的时候,可以这样:
//constructor this.clickMenu=this.clickMenu.bind(this) //点击事件 clickMenu(event){ var that=this; let sort=event.currentTarget.dataset.sort; that.setState({query:sort}) } // render中 <div style={homeMainLeftTwodiv1}> { this.state.HomeMenuone.map((item,index)=> <span key={index} className={this.state.query===item.sort?"homesortactive":'' } data-sort={item.sort} onClick={this.clickMenu}> <Link to={ {pathname:"/home", search: 'sort='+item.sort,} } >{item.name}</Link> <Route path="/home/:id" component={Home}/> </span> ) } </div>