写了这么久React,该来一些总结了
前言
学习React也有将近一个月的时间,坎坎坷坷,对于熟悉一个框架来说,基础知识还是很重要的,这里的基础只是并不是React的基础只是,而是以往的一些开发思想和经验,以前写过一些html链接一个JavaScript代码,遇到类似的组件和功能,又要copy一份再修改,导致代码臃肿,用原生的方法来实现组件化又显得很繁琐,如果单单只是个展示页面还没有什么,当遇到一些交互复杂的需求,这个时候密集性操作DOM会让代码可读性一落千丈,基本上就是一次性代码,下次再维护更新就好比重构一样复杂。因此这也是为什么会出现React这样以“数据驱动”为原则的前端框架,这里我主要总结了自己学习的一些心得和自己想法。
组件和容器
这一点我觉得如果将组件称为木偶组件,将容器称为智能组件会更加贴切一点,木偶是什么呢,就是它是死板的不会动的,只有你驱动它,他才会动,也就是我们平常说的样式组件,只关心样式不关心逻辑。智能组件就是一个“又自己逻辑想法的”组件,它负责页面的逻辑交互、数据获取等较为复杂的事情。我们用一段代码来看:
特点: 接收父级传来的props改变样式,没有自己的状态和逻辑。
1 2 3 4
| const Button = ({children}, context) => <button style={{background: context.color}}> {children} </button>;
|
特点: 继承了React的Component,有自己的状态(state),有自己的声明周期,向子组件(木偶组件)传递数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class SidebarContainer extends React.Component { constructor(props){ super(props) this.setState({ items:[] }) } componentDidMount() { fetch('/url') .then(res => { this.setState({ items: res.items, }) }) } render() { return ( <Sidebar> {this.state.items.map(item => ( <SidebarItem data={this.state.items}>{item}</SidebarItem> ))} </Sidebar> ); } }
|
在实际项目开发中,通常会在src目录中创建一个container文件夹和components文件夹,用来区分木偶智能组件,明确划分了他们的界限,也方便以后的维护。
列表的唯一id:key
刚刚接触React的时候,写循环列表总是会忘记写上key,每次看到控制台报错才意识到,但是那时候压根不知道为啥要带上这么一个key,感觉也没啥用呀,后来谷歌后才发现,React是个非常注重效率的框架,Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化,进一步提升虚拟DOM的效率,因此应当给数组中的每一个元素赋予一个确定的标识。
一个元素的key最好就是在列表中拥有唯一的字符串,通常我使用mongo数据库传来的id做为key,这样就能确保每个都不一样,如果是在没办法,也能用序列号索引index作为key。
1 2 3 4 5
| const todoItems = todos.map((todo) => <li key={todo.id}> {todo.text} </li> );
|
或者:
1 2 3 4 5
| const todoItems = todos.map((todo, index) => <li key={index}> {todo.text} </li> );
|
在HTML中写JavaScript
如果用传统是想来看这一点,我我敢保证你分分钟会被谴责,因为传统的文波开发理念是逻辑与html完全分离,但是React打破了这一常规,开始阶段也被骂的很惨,但是慢慢的程序员发现这是一件非常便利的事情,比如说:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const arr = [ { name: "huajinbo", sex: 0 }, { name: "zhuzhu", sex: 1 } ]; <div> {arr.map(v => { return v.sex === 0 ? <p>{v.name}</p> : <h1>{v.name}</h1>; })} </div>
|
如果说让我选择,我当然会使用JavaScript in HTML这一方式,真的很大程度上提高了开发效率。
表单的最佳事件
在React中处理表单是件非常优雅的事情,我们可以根据不同的需求,选择不同的方式,以下分别介绍这两种方式:
受控表单
受控是指这个表单的一切,都受到react的监控与驱动,我们看下面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| import React from "react";
class App extends React.Component { constructor(props) { super(props); this.state = { email: "", errMsg: "" };
this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); }
handleChange(event) { var emailValue = event.target.value this.setState({ email: emailValue }); var reg=/^([a-zA-Z0-9]+[-_.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[-_.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,6}$/ if (emailValue === "") { this.setState({ errMsg: '邮箱不为空' }) } else if (!reg.test(emailValue)) { this.setState({ errMsg: '邮箱格式不对' }) } else { this.setState({ errMsg: '' }) } }
handleSubmit(event) { alert("Your email is: " + this.state.email); event.preventDefault(); } render() { return ( <div> <form onSubmit={this.handleSubmit}> <label> Email: <input type="text" value={this.state.email} onChange={this.handleChange} /> </label> <input type="submit" value="Submit" /> <p style={{color:'red'}}>{this.state.errMsg}</p> </form> </div> ); } } export default App;
|
演示:

demo略长,我们可以看到在input中监控了这个输入框的的输入变化,实时获得input输入的数据,根据这个数据,我们能够实时地判断输入是否符合规范,做到实时给出错误提醒,当然这个例子并不是完美,和真正表单还差一点,我理解的完美表单是:般输入框在第一次输入时候并不会验证,输入完成后,光标移出输入框,判断是否符合要求,若错误,用户再次输入,这个时候就采用了实时验证提醒错误的标识。不知道是否正确,因为没有机会接触大项目,所以有错误还希望小伙伴们提出。
非受控表单
非受控是值,输入框并不会受React对其的控制,这是第二种选择,我们来看看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import React from "react";
class App extends React.Component { constructor(props) { super(props); this.state = { email: "", errMsg:"" }; this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(event) { event.preventDefault(); console.log(this.refs.emailInput.value) let emailValue = this.refs.emailInput.value var reg=/^([a-zA-Z0-9]+[-_.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[-_.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,6}$/ if (emailValue === "") { this.setState({ errMsg: '邮箱不为空' }) } else if (!reg.test(emailValue)) { this.setState({ errMsg: '邮箱格式不对' }) } else { this.setState({ errMsg: '' }) } } render() { return ( <div> <form onSubmit={this.handleSubmit}> <label> Email: <input type="text" ref="emailInput" /> </label> <input type="submit" value="Submit" /> <p style={{color:'red'}}>{this.state.errMsg}</p> </form> </div> ); } } export default App;
|
演示:

这种方法无法实时获取输入数据,所以并不能向上面那样实时提供错误提醒,并且还直接用到了ref,这是React并不推荐的,即不推荐直接操作真实DOM,但是经过我几个小项目的尝试,有时候受控组件因为state渲染组件的问题会导致input失去焦点或者页面卡顿,这个时候如果你或许能试试这种“备胎”方式。
多个受控组件
如果你有多个受控的input组件,那么我建议你这么写,即美观又符合不写重复代码的原则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); }
<form> <label> Is going: <input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange} /> </label> <br /> <label> Number of guests: <input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange} /> </label> </form>
|
让协作更规范的propTypes
之前和小伙伴协作开发一个小项目,他写后端,我写前端,开发前就约定好传来前端的数据,这么做极大地降低了我和小伙伴友情破灭的概率,但是,如果是和小伙伴一起开发前端项目呢,比如你写的一个木偶组件需要给你的小伙伴倒入到容器组件中,这个时候,同样需要这么一套规范,它就是让协作更规范的propTypes,一来降低了代码出错的概率,而来也与小伙伴形成一个约定,何乐而不为呢?
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import PropTypes from 'prop-types';
class Greeting extends React.Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } }
Greeting.propTypes = { name: PropTypes.string };
|
常用的几种验证:
1 2 3 4 5 6 7
| optionalArray: PropTypes.array, optionalBool: PropTypes.bool, optionalFunc: PropTypes.func, optionalNumber: PropTypes.number, optionalObject: PropTypes.object, optionalString: PropTypes.string, optionalSymbol: PropTypes.symbol,
|
设置props默认值
一般来说props是要设置默认值的,特别是像一些需要遍历渲染出列表的组件,设置props值是非常重要的,因此如果传入了非数组(网络请求如还未获取到的数据),map遍历是会出错的,在es5是这样设置的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Greeting extends React.Component { render() { return ( <div> {arr.map(v=>( )) </div> ); } } Greeting.defaultProps = { arr: [] };
|
ES6后,我们可以在赋值的时候直接设置默认值如
1 2 3 4 5 6 7 8 9 10 11 12
| class Greeting extends React.Component { const { name=[] } = this.props render() { return ( <div> {arr.map(v=>( )) </div> ); } }
|
减少不必要的节点:Fragment
在HTML中嵌套过多层的组件事件非常不好的事情,降低了可读性不说,而且性能也不好,所以在React16.2中推出了Fragment,它就是一个“空标签”包装,在DOM树中不会渲染出任何东西,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React from 'react'
class TestCom extends React.Component{ render(){ return( <React.Fragment> <h1>111</h1> <h1>222</h1> </React.Fragment> ) } }
export default TestCom
|
其实也可以这么写:
1 2 3 4 5 6 7 8
| render(){ return( <> <h1>111</h1> <h1>222</h1> </> ) }
|
只不过现在版本的webpack还不支持而已,会直接报错。
总结
基本上以上内容在文档上都有写,但是有些细节上的问题,我这里还是提出来了,一来巩固自己的知识,而来分享给大家,或许能帮助你少踩几个坑呢~
one today is worth two tomorrows. :)