学和应用react有一年多了,最近想在梳理一下react基础知识,夯实根底,激流勇进~
对于reacr-router,redux,redux-saga后续都会缓缓输入,心愿各位看官老爷继续关注~~要是能给个赞激励一下就更赞了~
react基础知识速览
1、什么是JSX?
一个JSX语法的示例,如下所示
const element = <h1>Hello, world!</h1>;
这种语法模式,既不是HTML,也不是字符串,而是称之为JSX,是React里用来形容UI和款式的语法,JSX最终会被编译为非法的JS语句调用(编译器在遇到{
时采纳JS语法进行解析,遇到<
就采纳HTML规定进行解析)
2、嵌入表达式
JSX中,能够应用花括号{}
嵌入任意的JavaScript非法表达式,如:2 + 2
、user.firstName
、formatName(user)
都是非法的。示例如:
const user = { firstName: 'Zhang', lastName : 'Busong'};const elem = ( <h1>Hello, {formatName(user)}</h1>);/*这里的(),实际上是可选的,然而React举荐退出(),这样子就会被视为一个表达式,而不会导致主动插入分号的问题*/ReactDOM.render( element, document.getElementById('app'))
React实战视频解说:进入学习
3、JSX也是一种表达式
JSX自身也是一种表达式,所以它能够像其余表达式一样,用于给一个变量赋值、作为函数实参、作为函数返回值,等等。如:
function getGreeting(user) { if (user) { return <h1>Hello, {formatName(user)}</h1> } return <h1>Hello, Guest!</h1>;}
留神: 1、在JSX中,申明属性时不要应用引号,如果申明属性的时候应用引号,那么将被作为字符串解析,而不会被作为一个表达式解析,如:
<div firstName="{user.firstName}" lastName={user.lastName}></div>
解析后,能够失去:
<div firstName="{user.firstName}" lastName="Lau"></div>
因而,当咱们须要应用一个字符串字面量的时候,能够应用引号,然而如果要作为表达式解析的时候,则不该当应用引号
2、在JSX中,有些属性名称须要进行非凡解决。如class
应该用className
代替,tabindex
则用tabIndex
代替。这是因为JSX实质上更靠近于JavaScript,而class
是JavaScript中的保留字。同时,应该应用camelCase
来命名一个属性,而不是应用HTML的属性命名形式
3、JSX自身曾经做了防注入解决,对于那些不是明确编写的HTML代码,是不会被解析为HTML DOM的,ReactDOM会将他们一律视为字符串,在渲染实现前就转化为字符串,所以能够避免XSS攻打
4、如果JSX标签是闭合的,那么结尾须要用/>
,另外,JSX标签是能够相互嵌套的,这和HTML里是一样的
4、JSX本质
JSX通过babel编译,而babel实际上把JSX编译给React.createElement()
调用。如下JSX代码:
const element = ( <h1 className="greeting"> Hello, world! </h1>);
是等同于以下的语句的:
const elem = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!');
React.createElement()
办法会首先进行一些防止BUG的查看,而后返回相似以下例子的对象:
const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world' }}
这样的对象,则称为React元素
,代表所有出现在屏幕上的货色。React正是通过读取这些对象来构建DOM,并且保持数据和UI同步的
5、元素渲染
元素(elements
)是形成React利用的最小单元,元素形容了想要在屏幕中看到的内容,如:
const element = <h1>Hello, world</h1>;
和DOM元素不同的是,React元素是纯对象,创立的代价低。并且React会进行优化解决,只把有必要的变动更新到DOM上。此外,元素和组件的概念,是不一样的,组件是由元素组成的。
6、将元素渲染进DOM
在React中,应用ReactDOM.render()
办法来将React元素渲染进一个DOM中。如:
ReactDOM.render( element, document.getElementById('root'))
React元素是不可变的,所以一旦一个元素创立实现后,咱们是无奈扭转其内容或者属性的。一个元素就像是动画里的一帧,它代表UI在某一时间点的样子。如果非要应用元素来形成可变动的UI界面,就须要应用setInterval
了,如:
function tick() { const element = ( <div>Now is {new Date().toLocaleTimeString()}</div> ); ReactDOM.render( element, document.getElementById('root') );}setInterval(tick, 1000);
在理论开发中,大多数React利用只会调用一次ReactDOM.render()
,所以更好的形式是应用有状态组件
7、组件和Props
组件(component
)可能将UI划分为独立的、可复用的局部,这样咱们就只需专一于构建每一个独自的部件。
从概念上看,组件就像是函数:承受任意的输出(称为属性,Props
),返回React元素。React中有两种定义组件的形式:函数定义
和类定义
1、函数定义组件
这种形式是最简略的定义组件的形式,就像写一个JS函数一样,如:
function Welcome (props) { return <h1>Hello, {props.name}</h1>;;}
2、类定义组件
还能够应用ES6里的类来定义一个组件,如下所示:
class Welcome extends React.Component { render () { return <h1>Hello, {this.props.name}<h1>; }}
这种形式比起函数定义
形式则更加灵便
3、组件渲染
先前,咱们遇到的React元素只是出现一个DOM标签,如:
const element = <div />
然而,React元素也能够是用户自定义的组件
,如:
const element = <Welcome name="Tom" />
Welcome组件中申明了一个属性name="Tom"
,而这个属性,将以props.name
的形式传递给组件,如下形式:
function Welcome (props) { return <h1>Hello, {props.name}</h1>;}
此时,对于以下的代码:
ReactDOM.render( <Welcome name="张不怂" />, document.getElementById('root'))
最终就会以<h1>Hello, 张不怂</h1>
的形式出现。在这个过程中,产生了如下的事件:
- 对
<Welcome name="张不怂" />
元素调用了ReactDOM.render()
丰盛 - React将
{ name: '张不怂' }
作为props实参来调用Welcome组件 - Welcome实现渲染,返回
<h1>Hello, 张不怂</h1>
元素 - ReactDOM计算最小更新代价,而后更新DOM
4、组合组件
组件是能够组合的。即组件外部能够援用其余组件,如:
function Welcome (props) { return <h1>Hello, {props.name}</h1>;}function App () { return ( <div> <Welcome name="Tom" /> <Welcome name="Jack" /> <Welcome name="Mike" /> </div> )}ReactDOM.render( <App />, document.getElementById('root'))
留神: 在React中,组件必须返回繁多
的根元素,这也是为什么App组件中须要用<div>
标签包裹的起因。如以下的形式,是谬误的(因为它有3个根元素):
function App () { return ( <Welcome name="Tom" /> <Welcome name="Jack" /> <Welcome name="Mike" /> )}
5、属性是只读的
思考以下这种状况:
function sum (a, b) { return a + b;}
这种函数称为纯函数
:它不扭转本人的输出值,且总是对雷同的输出返回雷同的后果。
与之对抗的,则是非纯函数
,如:
function withdraw (account, amount) { account.total -= amount;}
非纯函数
在函数内扭转了输出的参数。在React中,无论是通过function
还是class
申明组件,咱们都不应该批改它本身的属性(props
)。尽管React相当灵便,然而它也有一个严格的规定:所有的React组件都必须像纯函数那样来应用它们的props
8、State与生命周期
应用类定义组件
有一些额定的益处,如领有本地状态
这一个性。
以下是一个类定义组件
class Clock extends React.Component { render () { return ( <div> <h1>Hello, world!</h1> <h2>Now is {this.props.date.toLocaleTimeString()}</h2> </div> ); }}
须要留神的有:
类名
即为组件名
(无论是函数定义组件还是类定义组件,组件名称的首字母都必须大写,并且继承自React.Component
)- 应用
render()
办法,用来返回须要出现的内容
1、在类中退出state
state是属于一个组件本身的。咱们能够在类的构造函数constructor
中来初始化状态,如:
constructor (props) { super(props) this.state = { date: new Date() }}
如此一来,咱们就能够在render()
函数中应用this.state.xxx
来援用一个状态
2、生命周期
在利用里,往往都会有许许多多的组件。在组件销毁后,回收和开释它们所占据的资源十分重要。
在时钟利用
的例子里,咱们须要在第一次渲染到DOM的时候设置一个定时器,并且须要在相应的DOM销毁后,革除这个定时器。那么,这种状况下,React为咱们提供了生命周期的钩子函数,不便咱们进行应用。在React中,生命周期分为:
1)Mount
已插入实在DOM
2)Update
正在从新渲染
3)Unmount
已移出实在DOM
而相应的,生命周期钩子函数有:
componentWillMount
componentDidMount
componentWillUpdate(newProps, nextState)
componentDidUpdate(prevProps, prevState)
componentWillUnmount()
此外,还有两种非凡状态的处理函数:
componentWillReceiveProps(nextProps)
已加载的组件收到新的参数时调动shouldComponentUpdate(nextProps, nextState)
组件判断是否从新渲染时调用
因而,基于生命周期钩子函数,咱们能够实现一个时钟利用如下:
class Clock extends React.Component { constructor (props) { super(props); this.state = { date: new Date() } } tick () { this.setState({ date: new Date() }); } componentDidMount () { this.timerId = setInterval(() => { this.tick() }, 1000); } componentWillUnmount () { clearInterval(this.timerId); } render () { return ( <div>Now is {this.state.date.toLocaleTimeString()}</div> ); }}
须要留神的是:
1)render()
里用不到的state
,不应该申明在state
里
2)不能间接应用this.state.xxx = xxx
的形式来扭转一个state
的值,应该应用this.setState()
。如:
setName () { this.setState({ name: '张不怂' })}
this.setState()
会主动笼罩this.state
里相应的属性,并触发render()
从新渲染。
3)状态更新可能是异步的 React能够将多个setState()
调用合并成一个调用来晋升性能。且因为this.props
和this.state
可能是异步更新的,所以不应该依附它们的值来计算下一个状态。这种状况下,能够给setState
传入一个函数,如:
this.setState((prevState, props) => ({ counter: prevState.counter + props.increment}));
9、事件处理
React元素的事件与DOM元素相似,不过也有一些区别,如:
1)React事件应用camelCase
命名(onClick
),而不是全小写的模式(onclick
)
2)应用JSX,传入的是事件的句柄,而不是一个字符串
如以下的HTML:
<button onclick="increment()">ADD</button>
应用React的形式形容如:
<button onClick={increment}>ADD</button>
还有一个不同在于,在原生DOM中,咱们能够通过返回false
来阻止默认行为,然而这在React中是行不通的,在React中须要明确应用preventDefault()
来阻止默认行为。如:
function ActionLink () { function handleClick (e) { e.preventDefault(); alert('Hello, world!'); } return ( <a href="#" onClick={handleClick}>Click Me</a> );}
这里,事件回调函数里的event
是通过React非凡解决过的(遵循W3C规范),所以咱们能够释怀地应用它,而不必放心跨浏览器的兼容性问题。 留神: 在应用事件回调函数的时候,咱们须要特地留神this
的指向问题,因为在React里,除了构造函数和生命周期钩子函数里会主动绑定this为以后组件外,其余的都不会主动绑定this
的指向为以后组件,因而须要咱们本人留神好this的绑定问题,
通常而言,在一个类形式申明的组件里应用事件回调,咱们需要在组件的constructor
里绑定回调办法的this指向,如:
class Counter extends React.Component { constructor (props) { super(props); this.state = { counter: 0 } // 在这里绑定指向 this.increment = this.increment.bind(this); } increment () { this.setState({ counter: this.state.counter + 1 }); } render () { return ( <div> The counter now is: {this.state.counter} <button onClick={this.increment}>+1</button> </div> ); }}
当然,咱们还有另外一种办法来应用箭头函数绑定指向,就是应用实验性
的属性初始化语法,如:
class Counter extends React.Component { increment: () => { this.setState({ counter: this.state.counter + 1 }); } // ...}
3)像事件处理程序传递参数
咱们能够为事件处理程序传递额定的参数,形式有以下两种:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button><button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
须要留神的是,应用箭头函数的状况下,参数e
要显式传递,而应用bind的状况下,则无需显式传递(参数e
会作为最初一个参数传递给事件处理程序)
10、条件渲染
在React里,咱们能够创立不同的组件来封装咱们须要的性能。咱们也能够依据组件的状态,只渲染组件中的一部分内容,而条件渲染就是为此而筹备的。在React中,咱们能够像在JavaScript中写条件语句一样地写条件渲染语句,如:
function Greet(props) { const isLogined = props.isLogined; if (isLogined) { return <div>Hello !</div>; } return <div>Please sign in</div>;}ReactDOM.render( <Greet isLogined={true} />, document.getElementById('root'));
这将渲染出:
<div>Hello !</div>
1、应用变量来存储元素
咱们也能够应用变量来存储元素,如:
function LogBtn(props) { var button; const isLogined = props.isLogined; if (isLogined) { button = <button>退出</button> } else { button = <button>登陆</button> } return <div>You can {button}</div>;}ReactDOM.render( <LogBtn isLogined={false} />, document.getElementById('root'));
2、应用&&运算符进行渲染
因为JavaScript语法看待&&
运算符的性质,咱们也能够应用&&运算符来实现条件渲染,如:
function LogBtn(props) { var button; const isLogined = props.isLogined; return ( <div>Hello {!isLogined && ( <button>请登陆</button> )} </div> )}
当props.isLogined
为false的时候,就会渲染出:
<div>Hello <button>请登录</button></div>
3、应用三目运算符进行渲染
咱们可能曾经发现了,其实JSX能够像一个表达式那样子灵便应用,所以,咱们天然也能够应用三目运算符进行渲染,如:
function LogBtn (props) { const isLogined = props.isLogined; return ( <div>You can <button>{isLogined ? '退出' : '登陆'}</button> </div> )}
4、阻止整个组件的渲染
有时候,咱们心愿是整个组件都不渲染,而不仅仅是部分不渲染,那么这种状况下,咱们就能够在render()
函数里返回一个null
,来实现咱们想要的成果,如:
function LogBtn (props) { const isLogined = props.isLogined; const isShow = props.isShow; if (isShow) { return ( <div>You can <button>{isLogined ? '退出' : '登陆'}</button> </div> ) } return null;}
留神: 组件里返回null
不会影响组件生命周期的触发,如componentWillUpdate
和componentDidUpdate
依然会被调用
11、列表渲染与keys
在JavaScript中,咱们能够应用map()
函数来对一个数组列表进行操作,如:
const numbers = [1, 2, 3, 4, 5];const doubled = numbers.map(number => number*2);console.log(doubled); // 失去[2, 4, 6, 8, 10]
同样的,在React里,咱们也能够应用map()
来进行列表渲染,如:
const numbers = [1, 2, 3, 4, 5];const listItems = numbers.map(number => { return ( <li>{number}</li> )});ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('root'))
这将失去:
<ul><li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li></ul>
当然,咱们还能够进行更好的封装,如:
function NumberList (props) { const numbers = props.numbers; const listItems = numbers.map(number => { return ( <li>{number}</li> ) }); return <ul>{listItems}</ul>}
当咱们运行以上的代码的时候,会发现控制台提醒:Each child in an array or iterator should have a unique "key" prop
,因而,咱们须要为列表项的每一个项调配一个key
,来解决这个问题,通常而言,咱们能够应用以下几种形式来提供key
:
- 应用数据项本身的ID,如
<li key={item.itemId}>
- 应用索引下标(
index
),如:
const listItems = numbers.map((number, index) => { <li key={index}>{number}</li>});
然而React不举荐在须要从新排序的列表里应用索引下标,因为会导致变得很慢。
留神: 只有在一个项的同胞里辨别彼此的时候,才须要应用到key,key不须要全局惟一,只须要在一个数组外部辨别彼此时惟一便可。key的作用是给React一个提醒,而不会传递给组件。如果咱们在组件内须要同样的一个值,能够换个名字传递,如:
const content = posts.map(post => ( <Post key={post.id} id={post.id} title={post.title} />));
12、表单
表单和其余的React中的DOM元素有所不同,因为表单元素生来就是为了保留一些外部状态。在React中,表单和HTML中的表单略有不同
1、受控组件
HTML中,<input>
、<textarea>
、<select>
这类表单元素会维持本身状态,并依据用户输出进行更新。不过React中,可变的状态通常保留在组件的this.state
中,且只能用setState()
办法进行更新,如:
class NameForm extends React.Component { constructor (props) { super(props); this.state = { value: '' } this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange (event) { this.setState({ value: event.target.value }); } handleSubmit (event) { alert('Your name is '+this.state.value); event.preventDefault(); } render () { return ( <form onSubmit={this.handleSubmit}> Name: <input type="text" value={this.state.value} onChange={this.handleChange} /> <input type="submit" value="Submit" /> </form> ) }}
和HTML中不同的是,React中的textarea
并不需要写成<textarea></textarea>
的模式,而是写成<textarea value="" ... />
的模式便可。而对于HTML中的select
标签,通常做法是:
<select> <option value="A">A</option> <option value="B" selected>B</option> <option value="C">C</option></select>
然而React中,不须要在须要选中的option
处退出selected
,而只须要传入一个value,就会主动依据value来选中相应的选项,如:
<select value="C"> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option></select>
那么如上述例子,C所在的这个option
就会被选中
2、多个输出的解决办法
通常一个表单都有多个输出,如果咱们为每一个输出增加处理事件,那么将会十分繁琐。好的一个解决办法是,应用name,而后依据event.target.name
来抉择做什么。如:
class Form extends React.Component { constructor (props) { super(props); this.state = { name: '', gender: '男', attend: false, profile: '' }; this.handleInputChange = this.handleInputChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleInputChange (event) { const target = event.target; const value = target.type==='checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); } handleSubmit (event) { this.setState({ profile: `姓名:${this.state.name},${this.state.gender},${this.state.attend ? '加入' : '不加入'}流动` }); event.preventDefault(); } render () { return ( <form> <p>姓名:<input name="name" value={this.state.name} onChange={this.handleInputChange} /></p> <p>性别: <select name="gender" value={this.state.gender} onChange={this.handleInputChange}> <option value="男">男</option> <option value="女">女</option> </select> </p> <p>是否加入:<input name="attend" type="checkbox" onChange={this.handleInputChange} checked={this.state.attend} /></p> <input type="submit" value="Submit" onClick={this.handleSubmit} /> <p>您的报名信息:{this.state.profile}</p> </form> ) }}
3、非受控组件
大多数状况下,应用受控组件
实现表单是首选,在受控组件中,表单数据是交由React组件解决的。如果想要让表单数据由DOM解决(即数据不保留在React的状态里,而是保留在DOM中),那么能够应用非受控组件
,应用非受控组件
,能够无需为每个状态更新编写事件处理程序,应用ref
即可实现,如:
class NameForm extends React.Component { constrcutor(props) { super(props) } handleSubmit(event) { console.log('A name was submitted: ', this.input.value) event.preventDefault() } render() { return ( <form onSubmit={this.handleSubmit}> <label> Name: <input type="text" ref={input => this.input = input} /> </label> <input type="submit" value="submit" /> </form> ) }}
对于非受控组件
,如果要指定默认值,那么能够应用defaultValue
,如:
<input type="text" defaultValue="Hello" ref={input => this.input = input} />
相应的,type="checkbox"
和type="radio"
,则应用defaultChecked
13、状态晋升
当须要几个组件共用状态数据的时候,能够应用状态晋升技术。核心思想在于:把数据抽离到最近的独特父组件,父组件治理状态(state),而后通过属性(props)传递给子组件。如实现一个货币转换的组件,能够如下:
1、首先定义转换函数
function USD2RMB (amount) { return amount * 6.7925;}function RMB2USD (amount) { return amount * 0.1472;}function convert (amount, typeFn) { return typeFn(amount);}
2、定义组件
咱们心愿在RMB的输出表单上上输出的时候,USD的输出表单上的数值也同步更新,这种状况下,如果RMB组件本人治理本人的状态,是很难以实现的,因而,咱们须要让这个状态晋升自父组件进行治理。如下:
class CurrencyInput extends React.Component { constructor (props) { super(props) this.handleChange = this.handleChange.bind(this) } handleChange (event) { this.props.onInputChange(event.target.value) } render () { const value = this.props.value const type = this.props.type return ( <p>{type}: <input type="text" value={value} onChange={this.handleChange} /></p> ); }}
最初定义一个独特的父组件,如下:
class CurrencyConvert extends Component { constructor (props) { super(props); this.state = { type: 'RMB', amount: 0 } this.handleRMBChange = this.handleRMBChange.bind(this); this.handleUSDChange = this.handleUSDChange.bind(this); } handleRMBChange (amount) { this.setState({ type: 'RMB', amount }); } handleUSDChange (amount) { this.setState({ type: 'USD', amount }); } render () { const type = this.state.type; const amount = this.state.amount; const RMB = type==='RMB' ? amount : convert(amount, USB2RMB); const USD = type==='USD' ? amount : convert(amount, RMB2USB); return ( <div> <p>Please Input:</p> <CurrencyInput type="RMB" value={RMB} onInputChange={this.handleRMBChange} /> <CurrencyInput type="USD" value={USD} onInputChange={this.handleUSDChange} /> </div> ); }}
14、组合vs继承
React推崇更多的是应用组合,而非应用继承。对于一些应用场景,React给出的倡议如下:
1、蕴含关系
当父组件不晓得子组件可能的内容是什么的时候,能够应用props.children
,如:
function Article (props) { return ( <section> <aside>侧边栏</aside> <article>{props.children}</article> </section> );}function App () { return ( <Article>这是一篇文章</Article> );}
这将渲染失去:
<section> <aside>侧边栏</aside> <article>这是一篇文章</article></section>
咱们还能够自定义名称,因为JSX实际上会被转化为非法的JS表达式,所以,还能够有:
function Article (props) { return ( <section> <aside>{props.aside}</aside> <article>{props.children}</article> </section> );}function App () { return ( <Article aside={ <h1>这是一个侧栏</h1> }>这是一篇文章</Article> );}
这将渲染失去:
<section> <aside><h1>这是一个侧栏</h1></aside> <article>这是一篇文章</article></section>
2、何时应用继承?
在Facebook的网站上,应用了数以千计的组件,然而实践证明还没有发现须要应用继承能力解决的状况。
属性和组合为咱们提供了清晰的、平安的形式来自定义组件的款式和行为,组件能够承受任意元素,包含:根本数据类型、React元素、函数。
如果要在组件之间复用UI无关的性能,那么应该将其提取到独自的JavaScript模块中,这样子能够在不对组件进行扩大的前提下导入并应用函数、对象、类