Open libin1991 opened 6 years ago
每一个的地方,每一种的知识,每一种事物,都是从陌生到熟悉。在这个过程里面,或许能开阔眼界,增长见识,体验乐趣。一切都归于我们的心态与行动。
学习之路不可停止,最近在玩 React。也动手尝试写了一个实例。借此整理总结下初步学习 React 的一些基础知识。因为这几天比较忙,没那么多时间,所以实例和文章没有很细致。如果大家发现文章有错误的地方,请多谅解。有什么更新的建议,欢迎在评论区指出。该文章主要是大概讲下和快速上手使用,如果深入,要靠自己探索,后期我也会补充文章。
1.这里使用 React 版本是 16.4 2.如果大家看的有点蒙圈,可能要去 React 上面看下 React 的基础。 3.代码已经上传到 Github 上面,需要的可以去进行下载,欢迎 star react-demos。 4.建议大家看该文章的时候也打开编辑器,边写边看,思路会清晰很多
1.这里使用 React 版本是 16.4
2.如果大家看的有点蒙圈,可能要去 React 上面看下 React 的基础。
3.代码已经上传到 Github 上面,需要的可以去进行下载,欢迎 star react-demos。
4.建议大家看该文章的时候也打开编辑器,边写边看,思路会清晰很多
学习React之前,一定要对下面两个知识点有所了解。如果不了解下面的知识,请前往下面对应的链接,进行学习。
学习 React 实例之前,一定要对JSX有一定的了解。JSX 可以说是一个语法糖,React 使用来替代常规的 JavaScript。看起来很像 XML 的 JavaScript 语法扩展。
JSX 不是必须的,如下两段代码,是完全等价的,但是使用JSX会更加的清晰,简洁,易懂。 JSX
let el=<div className="author">守候</div> ReactDOM.render( el, document.getElementById('example') ); 复制代码
HTML
let el=React.createElement('div',{className:'suthor'},'守候') ReactDOM.render( el, document.getElementById('example') ); 复制代码
关于 JSX 语法的更多内容,大家请看 React JSX 。这里不做过多的介绍。
组件是 React 最重要的一个概念。比如下面的代码,就可以说是一个组件。
class Author extends React.Component{ render(){ return ( <div className="author">守候</div> ) } } 复制代码
当然也可以使用函数组件的方式定义。
function Author(props) { return <div className='author'>守候</div>; } 复制代码
把组件挂在到页面的 id 为 example 的一个 Dom 元素上面。
ReactDOM.render( <Author />, document.getElementById('example') ); 复制代码
上面这里,就是一个组件,这个组件只有一个元素。组件也可以由多个元素组成
class Author extends React.Component{ render(){ return ( <div className="author"> <span class="author-name">守候</span> <span class="author-gener">男</span> </div> ) } } 复制代码
参考链接:React 组件。
这个例子的运行效果如下,是一个非常简单的实例。下面通过这个实例,接触下 React 的一些基础知识和使用方式。
渲染其实上面例子就已经有了,就是利用 render 函数返回一个组件。之后利用 ReactDOM.render 进行挂载到页面上的特定 Dom 元素里面。
class EquipmentList extends React.Component{ render(){ return ( <div> <a href="javascript:;">歼击机</a> <a href="javascript:;">轰炸机</a> <a href="javascript:;">运输机</a> <ul> <li>歼20</li> <li>轰6K</li> <li>运20</li> </ul> </div> ) } } //挂载到页面里 id 为 example 的元素上面 ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
上面是最基本的一个页面布局,但是页面是静态的,不是根据数据渲染的。下面认识下状态和循环,以数据驱动渲染。
首先来说下状态
引用菜鸟教程的说法:React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
我们把上面的代码,稍微改下,给组件加上状态。
class EquipmentList extends React.Component{ constructor(){ super(); this.state={ post1:'歼20', post2:'轰6K', post3:'运20', } } render(){ return ( <div> <a href="javascript:;">歼击机</a> <a href="javascript:;">轰炸机</a> <a href="javascript:;">运输机</a> <ul> <li>{this.state.post1}</li> <li>{this.state.post2}</li> <li>{this.state.post3}</li> </ul> </div> ) } } ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
这里有3个知识点需要知道 1.super(); 这个一定要调用,这里相当于调用了 React.Component 的 constructor。目的就是初始化 React 组件。 2.this.state 就是组件的状态 3.render 方法里面,输入的是 state (也可以是 props)。输出的就是组件。
这里有3个知识点需要知道
1.super(); 这个一定要调用,这里相当于调用了 React.Component 的 constructor。目的就是初始化 React 组件。
2.this.state 就是组件的状态
3.render 方法里面,输入的是 state (也可以是 props)。输出的就是组件。
页面效果完全一样,至于状态的修改等,下面再实例再提及。
但是这样写代码显然是不优雅的,如果数据一多,工作量就很大,就应该使用循环进行渲染。下面把代码改下。
class EquipmentList extends React.Component{ constructor(){ super(); this.state={ equipmentList:[ { id:1, title:'歼20' }, { id:2, title:'轰6K' }, { id:3, title:'运20' } ], } } render(){ return ( <div> <a href="javascript:;">歼击机</a> <a href="javascript:;">轰炸机</a> <a href="javascript:;">运输机</a> <ul> {this.state.equipmentList.map(item=><li key={item.id}>{item.title}</li>)} </ul> </div> ) } } ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
可能大家会有疑问,为什么li要带上 key 这个属性。是因为 React 是使用 key 属性来标志列表中的所有元素,当列表数据发生变化时,React 通过 key 可以更快的知道哪些元素发生了变化,从而只重新渲染发生变化的元素,提高效率和性能。在列表里面 key 需要唯一,一般是使用 id 作为 key 值,不建议使用 index 作为 key 值。因为如果列表发生了删除,插入等操作,列表要重排。index 值会改变,可能会影响渲染的效率和性能。
接下来就给组件添加时间,点击 a 元素的时候,比如点击‘歼击机’,就应该显示 equipmentList 里面 title 等于‘歼20’的数据,点击‘轰炸机’,就应该显示 equipmentList 里面 title 等于‘轰6K’的数据......
实现这个需求,其实很简单,就是新建一个状态 equipmentListNow ,组件里面只遍历 equipmentListNow。点击‘歼击机’,就把 equipmentList 里面 title ‘歼20’的数据赋值给postListNow,点击‘轰炸机’,就把 equipmentList 里面 title 等于‘轰6K’的数据赋值给postListNow......。
下面简单实现一下
class EquipmentList extends React.Component{ constructor(){ super(); this.state={ equipmentList:[ { id:1, title:'歼20' }, { id:2, title:'轰6K' }, { id:3, title:'运20' } ], equipmentListNow:[] } } switchTab(id){ let _list=this.state.equipmentList.filter(item=>item.id===id); this.setState({ equipmentListNow:_list }) } render(){ return ( <div> <a href="javascript:;" onClick={()=>{ this.switchTab(1); }}>歼击机</a> <a href="javascript:;" onClick={()=>{ this.switchTab(2); }}>轰炸机</a> <a href="javascript:;" onClick={()=>{ this.switchTab(3); }}>运输机</a> <ul> {this.state.equipmentListNow.map(item=><li key={item.id}>{item.title}</li>)} </ul> </div> ) } } ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
如果要修改 state ,只能使用如: this.setState({equipmentListNow:_list}) 改变。切记不能出现如下写法:this.equipmentListNow=_list;
跑起来了,没报错,但是页面上一开始列表没有被渲染出来,要点击的时候才有特定的数据出来(点击第一章的a,出现第一章的数据,以此类推)。原因想必大家也知道,因为在页面初始化的时候,equipmentListNow 只是一个空数组,所以正确的做法是在页面初始化的时候,就把 equipmentList 的值赋给 equipmentListNow。页面初始化赋值,就是下面组件生命周期的内容了。
为了让页面加载后,equipmentListNow 能有初始化的内容,那么需要在生命周期函数里面把 equipmentList 的值赋给 equipmentListNow。
至于生命周期,这里不展开讲,大家可以看下文档:React 组件生命周期。
大家应该知道,这个初始化赋值操作,应该在渲染前就完成。如果在渲染后再操作,那么就相当于页面渲染了第二次。所以我们现在用到的生命周期函数是 componentWillMount 。
class EquipmentList extends React.Component{ constructor(){ super(); this.state={ equipmentList:[ { id:1, title:'歼击机' }, { id:2, title:'轰炸机' }, { id:3, title:'运输机' } ], equipmentListNow:[] } } switchTab(id){ let _list=this.state.equipmentList.filter(item=>item.id===id); this.setState({ equipmentListNow:_list }) } componentDidMount(){ let _list=this.state.equipmentList; this.setState({ equipmentListNow:_list }) } render(){ return ( <div> <a href="javascript:;" onClick={()=>{ this.switchTab(1); }}>歼击机</a> <a href="javascript:;" onClick={()=>{ this.switchTab(2); }}>轰炸机</a> <a href="javascript:;" onClick={()=>{ this.switchTab(3); }}>运输机</a> <ul> {this.state.equipmentListNow.map(item=><li key={item.id}>{item.title}</li>)} </ul> </div> ) } } ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
这样一来,equipmentListNow 在页面渲染前就初始化有值了,页面也正常了。
上面的组件,一行 CSS 都没写,看着就特别难看。下面就添加些样式。
方式1:最简单的方式就是,就是给组件起 class 。在外部写上 CSS 样式。
CSS 代码
body{ font-family: "微软雅黑"; } ul{ margin: 0; padding: 0; } .post-box{ width: 600px; margin: 30px auto; } .post-box a{ display: inline-block; font-size: 14px; margin-right: 10px; width: 80px; height: 30px; line-height: 30px; text-align: center; background: #09f; color: #fff; text-decoration: none; } .post-box li{ list-style-type: none; line-height: 40px; padding-left: 10px; border-bottom: 1px solid #ccc; } 复制代码
JS 代码
在 js class 是关键字,所以要给元素设置 class 属性,需要用 className
render() { return ( {/*给div增加class*/} <div className="post-box"> ... </div> ) } 复制代码
方式2:由于 React 的机制,所以很多时候会使用 css-in-js 这种方式,设置元素的样式,简单来说就是设置元素的内联样式。
render() { return ( <div className="post-box"> {/*给a设置内联样式*/} <a href="javascript:;" onClick={() => { this.switchTab(1); }} style={{background:'#f90'}}>歼击机</a> <a href="javascript:;" onClick={() => { this.switchTab(2); }} style={{background:'#f00'}}>轰炸机</a> <a href="javascript:;" onClick={() => { this.switchTab(3); }} style={{background:'#0f0'}}>运输机</a> <ul> {this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)} </ul> </div> ) } 复制代码
css-in-js 的另一种写法是:声明样式变量
let _style={ background:'#09f', color:'#f3f201' } 复制代码
render() { return ( <div className="post-box"> {/*给a设置内联样式*/} <a href="javascript:;" onClick={() => { this.switchTab(1); }} style={_style}>歼击机</a> <a href="javascript:;" onClick={() => { this.switchTab(2); }} style={_style}>轰炸机</a> <a href="javascript:;" onClick={() => { this.switchTab(3); }} style={_style}>运输机</a> <ul> {this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)} </ul> </div> ) } 复制代码
大家可以看到,上面的组件,是有一个状态 state 的。但大家看了文章开始的例子就知道,并不是所有的组件都是需要 state 的。根据有无 state 。可以把组件区分为有状态组件和无状态组件。把有状态组件和无状态组件合理利用,分工合作,可以说是用好 React 的第一步,下面简单分析下。
不难发现,上面 EquipmentList 组件复用性不强。想要复用,必须把代码拷贝过去,然后再修改 equipmentList 这个状态。
大家应该知道 EquipmentList 要想复用,里面的数组不能写死,只能由外部传入,EquipmentList 通过 props 获取数据。
既然说到了 props 就顺便提下,props 的作用就是把父组件的值传给子组件。props 是一个对象。如下
下面引用 2-2 的一个例子。 ReactDOM.render( <Author name='守候' gender='男'/>, document.getElementById('example') ); 复制代码
那么 Author 里面收到的 props 就是
props={ name:'守候', gender:'男' } 复制代码
使用方式也很简单
class Author extends React.Component{ render(){ return ( <div className="author"> <span class="author-name">{this.props.name}</span> <span class="author-gender">{this.props.gender}</span> </div> ) } } 复制代码
引用菜鸟教程说法:state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。
说了这么多,下面修改下,把 EquipmentList 封装成一个能复用的组件。
class EquipmentList extends React.Component { constructor(props) { super(props); this.state = { //根据props获取数据 equipmentList: this.props.list, equipmentListNow: [] } } switchTab(id) { let _list = this.state.equipmentList.filter(item => item.id === id); this.setState({ equipmentListNow: _list }) } componentDidMount() { let _list = this.state.equipmentList; this.setState({ equipmentListNow: _list }) } render() { return ( <div className='post-box'> <a href="javascript:;" onClick={() => { this.switchTab(1); }}>歼击机</a> <a href="javascript:;" onClick={() => { this.switchTab(2); }}>轰炸机</a> <a href="javascript:;" onClick={() => { this.switchTab(3); }}>运输机</a> <ul> {this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)} </ul> </div> ) } } 复制代码
这里有一个要注意的点 在 constructor 里面,我写的是 super(props) 不是之前那样的 super() 。这样的写的原因就是为了在 constructor 里面可以使用 this.props。如下代码
constructor(props) { super(props); this.state = { //根据props获取数据 equipmentList: this.props.list, equipmentListNow: [] } } 复制代码
如不需要用 this.props 。可以直接写 super(),当然写 super(props) 也没错。
constructor(props) { super(); this.state = { //根据props获取数据 equipmentList: props.list, equipmentListNow: [] } } 复制代码
说了这么多,下面看下怎么使用。
使用方式1,这里只做一个说明,这样子写实际是没什么意思的。
let equipmentList = [ { id: 1, title: '歼20' }, { id: 2, title: '轰6K' }, { id: 3, title: '运20' } ] ReactDOM.render( <EquipmentList list={equipmentList}/>, document.getElementById('example') ); 复制代码
使用方式2
class Example2 extends React.Component { constructor(props) { super(props); this.state = { equipmentList: [ { id: 1, title: '歼20-量产型号' }, { id: 2, title: '轰6K-量产型号' }, { id: 3, title: '运20-量产型号' } ], } } componentDidMount() { let _list = this.state.equipmentList; //两秒后更新数据 setTimeout(() => { _list.forEach((item,index)=>{ item.title='其实这是一艘航空母舰'+index; }); this.setState({ equipmentList: _list }) }, 2000) } render(){ return ( <EquipmentList list={this.state.equipmentList}></EquipmentList> ) } } ReactDOM.render(<Example2/>, document.getElementById('example2') ); 复制代码
这样一来,EquipmentList 变成了无状态组件,Example2 变成了有状态组件。分工方面,EquipmentList 不操作数据的变化,只管数据的渲染;Example2 不关注如何渲染,只控制数据变化,每次变化,使用 setState 更新数据,EquipmentList 的渲染结果就会改变。
看到这可能大家也有感悟了,一般而言,一个页面上绝大部分应该是无状态组件,少部分是有状态组件。
最后提一点,可能大家也有会疑问,如果 props 很多都是重复的值,可不可以设置一个默认值,就可以少传些参数。答案是可以的,比如下面代码,挂载的时候 EquipmentList 没有传值,但是能渲染出来,因为使用了 defaultProps 设置 props 的默认值。
class EquipmentList extends React.Component { constructor(props) { super(props); this.state = { //根据props获取数据 equipmentList: this.props.list, equipmentListNow: [] } } switchTab(id) { let _list = this.state.equipmentList.filter(item => item.id === id); this.setState({ equipmentListNow: _list }) } componentDidMount() { let _list = this.state.equipmentList; this.setState({ equipmentListNow: _list }) } render() { return ( <div> <a href="javascript:;" onClick={() => { this.switchTab(1); }}>歼击机</a> <a href="javascript:;" onClick={() => { this.switchTab(2); }}>轰炸机</a> <a href="javascript:;" onClick={() => { this.switchTab(3); }}>运输机</a> <ul> {this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)} </ul> </div> ) } } //设置 props 默认值 EquipmentList.defaultProps={ list:[ { id: 1, title: '歼20' }, { id: 2, title: '轰6K' }, { id: 3, title: '运20' } ] } ReactDOM.render(<EquipmentList/>, document.getElementById('example') ); 复制代码
好了,这几天对 React 学习的一些总结,就暂时告一段落了。该文章只是针对 React 实现一个非常简单的实例,也很基础。如果要深入,就要大家各自去努力了,在往后深入学习里面,我也会继续写文章,分享。希望和大家有更多的交流,如果大家对文章有什么看法和建议,欢迎指点。
-------------------------华丽的分割线--------------------
想了解更多,和我交流,内推职位,请添加我微信。或者关注我的微信公众号:守候书阁
1.前言
学习之路不可停止,最近在玩 React。也动手尝试写了一个实例。借此整理总结下初步学习 React 的一些基础知识。因为这几天比较忙,没那么多时间,所以实例和文章没有很细致。如果大家发现文章有错误的地方,请多谅解。有什么更新的建议,欢迎在评论区指出。该文章主要是大概讲下和快速上手使用,如果深入,要靠自己探索,后期我也会补充文章。
2.预热知识
学习React之前,一定要对下面两个知识点有所了解。如果不了解下面的知识,请前往下面对应的链接,进行学习。
2-1.JSX
学习 React 实例之前,一定要对JSX有一定的了解。JSX 可以说是一个语法糖,React 使用来替代常规的 JavaScript。看起来很像 XML 的 JavaScript 语法扩展。
JSX 不是必须的,如下两段代码,是完全等价的,但是使用JSX会更加的清晰,简洁,易懂。 JSX
let el=<div className="author">守候</div> ReactDOM.render( el, document.getElementById('example') ); 复制代码
HTML
let el=React.createElement('div',{className:'suthor'},'守候') ReactDOM.render( el, document.getElementById('example') ); 复制代码
关于 JSX 语法的更多内容,大家请看 React JSX 。这里不做过多的介绍。
2-2.组件
组件是 React 最重要的一个概念。比如下面的代码,就可以说是一个组件。
class Author extends React.Component{ render(){ return ( <div className="author">守候</div> ) } } 复制代码
当然也可以使用函数组件的方式定义。
function Author(props) { return <div className='author'>守候</div>; } 复制代码
把组件挂在到页面的 id 为 example 的一个 Dom 元素上面。
ReactDOM.render( <Author />, document.getElementById('example') ); 复制代码
上面这里,就是一个组件,这个组件只有一个元素。组件也可以由多个元素组成
class Author extends React.Component{ render(){ return ( <div className="author"> <span class="author-name">守候</span> <span class="author-gener">男</span> </div> ) } } 复制代码
参考链接:React 组件。
3.实例
这个例子的运行效果如下,是一个非常简单的实例。下面通过这个实例,接触下 React 的一些基础知识和使用方式。
3-1.渲染
渲染其实上面例子就已经有了,就是利用 render 函数返回一个组件。之后利用 ReactDOM.render 进行挂载到页面上的特定 Dom 元素里面。
class EquipmentList extends React.Component{ render(){ return ( <div> <a href="javascript:;">歼击机</a> <a href="javascript:;">轰炸机</a> <a href="javascript:;">运输机</a> <ul> <li>歼20</li> <li>轰6K</li> <li>运20</li> </ul> </div> ) } } //挂载到页面里 id 为 example 的元素上面 ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
3-2.状态和循环
上面是最基本的一个页面布局,但是页面是静态的,不是根据数据渲染的。下面认识下状态和循环,以数据驱动渲染。
首先来说下状态
引用菜鸟教程的说法:React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
我们把上面的代码,稍微改下,给组件加上状态。
class EquipmentList extends React.Component{ constructor(){ super(); this.state={ post1:'歼20', post2:'轰6K', post3:'运20', } } render(){ return ( <div> <a href="javascript:;">歼击机</a> <a href="javascript:;">轰炸机</a> <a href="javascript:;">运输机</a> <ul> <li>{this.state.post1}</li> <li>{this.state.post2}</li> <li>{this.state.post3}</li> </ul> </div> ) } } ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
页面效果完全一样,至于状态的修改等,下面再实例再提及。
但是这样写代码显然是不优雅的,如果数据一多,工作量就很大,就应该使用循环进行渲染。下面把代码改下。
class EquipmentList extends React.Component{ constructor(){ super(); this.state={ equipmentList:[ { id:1, title:'歼20' }, { id:2, title:'轰6K' }, { id:3, title:'运20' } ], } } render(){ return ( <div> <a href="javascript:;">歼击机</a> <a href="javascript:;">轰炸机</a> <a href="javascript:;">运输机</a> <ul> {this.state.equipmentList.map(item=><li key={item.id}>{item.title}</li>)} </ul> </div> ) } } ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
3-3.事件
接下来就给组件添加时间,点击 a 元素的时候,比如点击‘歼击机’,就应该显示 equipmentList 里面 title 等于‘歼20’的数据,点击‘轰炸机’,就应该显示 equipmentList 里面 title 等于‘轰6K’的数据......
实现这个需求,其实很简单,就是新建一个状态 equipmentListNow ,组件里面只遍历 equipmentListNow。点击‘歼击机’,就把 equipmentList 里面 title ‘歼20’的数据赋值给postListNow,点击‘轰炸机’,就把 equipmentList 里面 title 等于‘轰6K’的数据赋值给postListNow......。
下面简单实现一下
class EquipmentList extends React.Component{ constructor(){ super(); this.state={ equipmentList:[ { id:1, title:'歼20' }, { id:2, title:'轰6K' }, { id:3, title:'运20' } ], equipmentListNow:[] } } switchTab(id){ let _list=this.state.equipmentList.filter(item=>item.id===id); this.setState({ equipmentListNow:_list }) } render(){ return ( <div> <a href="javascript:;" onClick={()=>{ this.switchTab(1); }}>歼击机</a> <a href="javascript:;" onClick={()=>{ this.switchTab(2); }}>轰炸机</a> <a href="javascript:;" onClick={()=>{ this.switchTab(3); }}>运输机</a> <ul> {this.state.equipmentListNow.map(item=><li key={item.id}>{item.title}</li>)} </ul> </div> ) } } ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
跑起来了,没报错,但是页面上一开始列表没有被渲染出来,要点击的时候才有特定的数据出来(点击第一章的a,出现第一章的数据,以此类推)。原因想必大家也知道,因为在页面初始化的时候,equipmentListNow 只是一个空数组,所以正确的做法是在页面初始化的时候,就把 equipmentList 的值赋给 equipmentListNow。页面初始化赋值,就是下面组件生命周期的内容了。
3-4.生命周期
为了让页面加载后,equipmentListNow 能有初始化的内容,那么需要在生命周期函数里面把 equipmentList 的值赋给 equipmentListNow。
至于生命周期,这里不展开讲,大家可以看下文档:React 组件生命周期。
大家应该知道,这个初始化赋值操作,应该在渲染前就完成。如果在渲染后再操作,那么就相当于页面渲染了第二次。所以我们现在用到的生命周期函数是 componentWillMount 。
class EquipmentList extends React.Component{ constructor(){ super(); this.state={ equipmentList:[ { id:1, title:'歼击机' }, { id:2, title:'轰炸机' }, { id:3, title:'运输机' } ], equipmentListNow:[] } } switchTab(id){ let _list=this.state.equipmentList.filter(item=>item.id===id); this.setState({ equipmentListNow:_list }) } componentDidMount(){ let _list=this.state.equipmentList; this.setState({ equipmentListNow:_list }) } render(){ return ( <div> <a href="javascript:;" onClick={()=>{ this.switchTab(1); }}>歼击机</a> <a href="javascript:;" onClick={()=>{ this.switchTab(2); }}>轰炸机</a> <a href="javascript:;" onClick={()=>{ this.switchTab(3); }}>运输机</a> <ul> {this.state.equipmentListNow.map(item=><li key={item.id}>{item.title}</li>)} </ul> </div> ) } } ReactDOM.render( <EquipmentList />, document.getElementById('example') ); 复制代码
这样一来,equipmentListNow 在页面渲染前就初始化有值了,页面也正常了。
3-5.组件样式
上面的组件,一行 CSS 都没写,看着就特别难看。下面就添加些样式。
方式1:最简单的方式就是,就是给组件起 class 。在外部写上 CSS 样式。
CSS 代码
body{ font-family: "微软雅黑"; } ul{ margin: 0; padding: 0; } .post-box{ width: 600px; margin: 30px auto; } .post-box a{ display: inline-block; font-size: 14px; margin-right: 10px; width: 80px; height: 30px; line-height: 30px; text-align: center; background: #09f; color: #fff; text-decoration: none; } .post-box li{ list-style-type: none; line-height: 40px; padding-left: 10px; border-bottom: 1px solid #ccc; } 复制代码
JS 代码
render() { return ( {/*给div增加class*/} <div className="post-box"> ... </div> ) } 复制代码
方式2:由于 React 的机制,所以很多时候会使用 css-in-js 这种方式,设置元素的样式,简单来说就是设置元素的内联样式。
render() { return ( <div className="post-box"> {/*给a设置内联样式*/} <a href="javascript:;" onClick={() => { this.switchTab(1); }} style={{background:'#f90'}}>歼击机</a> <a href="javascript:;" onClick={() => { this.switchTab(2); }} style={{background:'#f00'}}>轰炸机</a> <a href="javascript:;" onClick={() => { this.switchTab(3); }} style={{background:'#0f0'}}>运输机</a> <ul> {this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)} </ul> </div> ) } 复制代码
css-in-js 的另一种写法是:声明样式变量
let _style={ background:'#09f', color:'#f3f201' } 复制代码
render() { return ( <div className="post-box"> {/*给a设置内联样式*/} <a href="javascript:;" onClick={() => { this.switchTab(1); }} style={_style}>歼击机</a> <a href="javascript:;" onClick={() => { this.switchTab(2); }} style={_style}>轰炸机</a> <a href="javascript:;" onClick={() => { this.switchTab(3); }} style={_style}>运输机</a> <ul> {this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)} </ul> </div> ) } 复制代码
3-6.有状态组件和无状态组件
大家可以看到,上面的组件,是有一个状态 state 的。但大家看了文章开始的例子就知道,并不是所有的组件都是需要 state 的。根据有无 state 。可以把组件区分为有状态组件和无状态组件。把有状态组件和无状态组件合理利用,分工合作,可以说是用好 React 的第一步,下面简单分析下。
不难发现,上面 EquipmentList 组件复用性不强。想要复用,必须把代码拷贝过去,然后再修改 equipmentList 这个状态。
大家应该知道 EquipmentList 要想复用,里面的数组不能写死,只能由外部传入,EquipmentList 通过 props 获取数据。
既然说到了 props 就顺便提下,props 的作用就是把父组件的值传给子组件。props 是一个对象。如下
下面引用 2-2 的一个例子。 ReactDOM.render( <Author name='守候' gender='男'/>, document.getElementById('example') ); 复制代码
那么 Author 里面收到的 props 就是
props={ name:'守候', gender:'男' } 复制代码
使用方式也很简单
class Author extends React.Component{ render(){ return ( <div className="author"> <span class="author-name">{this.props.name}</span> <span class="author-gender">{this.props.gender}</span> </div> ) } } 复制代码
说了这么多,下面修改下,把 EquipmentList 封装成一个能复用的组件。
class EquipmentList extends React.Component { constructor(props) { super(props); this.state = { //根据props获取数据 equipmentList: this.props.list, equipmentListNow: [] } } switchTab(id) { let _list = this.state.equipmentList.filter(item => item.id === id); this.setState({ equipmentListNow: _list }) } componentDidMount() { let _list = this.state.equipmentList; this.setState({ equipmentListNow: _list }) } render() { return ( <div className='post-box'> <a href="javascript:;" onClick={() => { this.switchTab(1); }}>歼击机</a> <a href="javascript:;" onClick={() => { this.switchTab(2); }}>轰炸机</a> <a href="javascript:;" onClick={() => { this.switchTab(3); }}>运输机</a> <ul> {this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)} </ul> </div> ) } } 复制代码
这里有一个要注意的点 在 constructor 里面,我写的是 super(props) 不是之前那样的 super() 。这样的写的原因就是为了在 constructor 里面可以使用 this.props。如下代码
如不需要用 this.props 。可以直接写 super(),当然写 super(props) 也没错。
说了这么多,下面看下怎么使用。
使用方式1,这里只做一个说明,这样子写实际是没什么意思的。
let equipmentList = [ { id: 1, title: '歼20' }, { id: 2, title: '轰6K' }, { id: 3, title: '运20' } ] ReactDOM.render( <EquipmentList list={equipmentList}/>, document.getElementById('example') ); 复制代码
使用方式2
class Example2 extends React.Component { constructor(props) { super(props); this.state = { equipmentList: [ { id: 1, title: '歼20-量产型号' }, { id: 2, title: '轰6K-量产型号' }, { id: 3, title: '运20-量产型号' } ], } } componentDidMount() { let _list = this.state.equipmentList; //两秒后更新数据 setTimeout(() => { _list.forEach((item,index)=>{ item.title='其实这是一艘航空母舰'+index; }); this.setState({ equipmentList: _list }) }, 2000) } render(){ return ( <EquipmentList list={this.state.equipmentList}></EquipmentList> ) } } ReactDOM.render(<Example2/>, document.getElementById('example2') ); 复制代码
这样一来,EquipmentList 变成了无状态组件,Example2 变成了有状态组件。分工方面,EquipmentList 不操作数据的变化,只管数据的渲染;Example2 不关注如何渲染,只控制数据变化,每次变化,使用 setState 更新数据,EquipmentList 的渲染结果就会改变。
看到这可能大家也有感悟了,一般而言,一个页面上绝大部分应该是无状态组件,少部分是有状态组件。
class EquipmentList extends React.Component { constructor(props) { super(props); this.state = { //根据props获取数据 equipmentList: this.props.list, equipmentListNow: [] } } switchTab(id) { let _list = this.state.equipmentList.filter(item => item.id === id); this.setState({ equipmentListNow: _list }) } componentDidMount() { let _list = this.state.equipmentList; this.setState({ equipmentListNow: _list }) } render() { return ( <div> <a href="javascript:;" onClick={() => { this.switchTab(1); }}>歼击机</a> <a href="javascript:;" onClick={() => { this.switchTab(2); }}>轰炸机</a> <a href="javascript:;" onClick={() => { this.switchTab(3); }}>运输机</a> <ul> {this.state.equipmentListNow.map(item => <li key={item.id}>{item.title}</li>)} </ul> </div> ) } } //设置 props 默认值 EquipmentList.defaultProps={ list:[ { id: 1, title: '歼20' }, { id: 2, title: '轰6K' }, { id: 3, title: '运20' } ] } ReactDOM.render(<EquipmentList/>, document.getElementById('example') ); 复制代码
4.小结
好了,这几天对 React 学习的一些总结,就暂时告一段落了。该文章只是针对 React 实现一个非常简单的实例,也很基础。如果要深入,就要大家各自去努力了,在往后深入学习里面,我也会继续写文章,分享。希望和大家有更多的交流,如果大家对文章有什么看法和建议,欢迎指点。
-------------------------华丽的分割线--------------------
想了解更多,和我交流,内推职位,请添加我微信。或者关注我的微信公众号:守候书阁