-
React入门
-
安装官方脚手架 ,后边的脚手架更加好用,但由于官方推荐开始先试试
npm install -g create-react-app -
初始化—创建项目
create-react-app 项目名 -
运行
npm start
-
-
JSX:看起来是js和html的混合体,但实际上html也是由js实现
React设计之初,就是使用JSX来描述UI,react只做逻辑层,reactDom去做渲染实际的dom,如果换到移动端,就用别的渲染库
import React from 'react'; import ReactDOM from 'react-dom'; class App extends Component { render() { return ( <div className="App"> Hello world! </div> ); } } ReactDOM.render( <App />, // document.getElementById('root') //这里的root元素是在index.html中的dom元素 ); -
组件定义
在上边的代码中我们也可以看到class类在react中就是组件 ,然后使用对应的标签
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render( <App />, document.getElementById('root') );import React, { Component } from 'react'; import './App.css'; class App extends Component { render() { return ( <div className="App"> Hello world! </div> ); } } export default App; -
state和setState
-
使用{ }来渲染变量
class App extends Component { render() { const name = '你好啊'; return ( <div className="App"> <p>{name}</p> </div> ); } } -
但是如果数据需要修改,并且同时页面响应变化,就需要将数据放在state中,并且使用setState来修改数据,super(props)是调用父级的construtor,来初始化各种生命周期啊,this指向等等数据
class App extends Component { constructor(props) { super(props); this.state = { name:'开课吧' } } render() { return ( <div className="App"> <p>{ this.state.name }</p> </div> ); } } -
使用setState来修改数据 ,使用this.state每次传的是一个新对象,这样好处就是可以做时间旅行
class App extends Component { constructor(props) { super(props); this.state = { name:'开课吧' }; setTimeout(()=>{ this.setState({ name:'开课吧真不错' }) }) } render() { return ( <div className="App"> <p>{ this.state.name }</p> </div> ); } }
-
-
属性传递 props
<App title="今天开始React" />, //使用的时候render() { return ( <div className="App"> <p>{this.props.title}</p> </div> ); } -
条件渲染和循环
-
条件渲染
class App extends Component { constructor(props) { super(props); this.state = { showTitle:true }; },2000) setTimeout(() => { this.setState({ showTitle: false // 2秒之后改变成隐藏 }) },2000) } render() { return ( <div className="App"> {this.state.showTitle?<p>{this.props.title}</p>:null} <!--其实这相当于v-if和v-else null那块是else的内容 --> {this.state.showTitle&&<p>{this.props.title}</p>} <!--直接这样写就是v-if功能 --> </div> ); } } -
循环
this.state = { goodlist:[ {text:'百万年薪架构师',price:100,id:1}, {text:'web全栈架构师',price:100,id:2}, {text:'Python',price:100,id:3} ] }; render() { return ( <div className="App"> <ul> {this.state.goodlist.map(good=>{ return <li key={good.id}> <span>{good.text}</span> <span>¥{good.price}</span> </li> })} </ul> </div> ); }
-
-
数据的管理和绑定
-
数据的绑定
- 在react中由于强制的单向数据流,数据绑定的input不能像vue中的那样双向数据修改
<input type="text" value={this.state.add} onChange={(e)=>{ this.setState({ add:e.target.value //这样就是实现了input的双向绑定 }) }} /> {/*由于value绑定了this.state react是强制的单项数据流,那么value就会被this.state数据监控,现在state的值不变value就不变*/} {/*当我们进行输入的时候会触发一个onchang事件 ,我们利用这个实现我们的双向绑定*/} {/*利用e.target.value 获得我们输入的值,然后进行对state中数据的修改,然后我们的input中value也会自动进行修改*/}- 实现将input中的数据,添加到上面state的goodlist数据中去
<button onClick={()=>{this.handleAdd()}}>添加</button>handleAdd(){ this.setState({ goodlist:[...this.state.goodlist,{text:this.state.add,price:20}], add:'' }) } -
事件绑定
上边的实现将input数据添加到goodlist中去的时候,那个onChange写的太繁琐对不对,我们想到把函数拿出去写,但是会报错,是因为this指向问题,因为当使用onChange={this.handleChange}这种写法时,把handleChange这个函数当成参数传递给了当前组件,组件再去调用自己的内部的this,取向值已经变了,因为这个函数的是给别人用的(谁用的这个函数,this指向谁)
onChange={this.handleChange}handleChange(e){ this.setState({ add:e.target.value }) }解决上边的问题有三种方案
-
在constructor(props) {}的时候
this.handleChange = this.handleChange.bind(this); // 这个函数任何时候执行,this都是我们当前组件的this -
利用箭头函数,解决this指向问题
<input type="text" value={this.state.add} onChange={(e)=>this.handleChange(e)} /> //但是如果传参的话 onClick={this.handleClick(i)}直接这样写就直接执行了 //onClick = {this.handleClick} //但是 这个函数 需要携带一个 i 的参数过去 //所以 就要用一个匿名函数把i 带过去啊。。 //onClick = {()=> this.handleClick(i)} //这样闭包 让 i 对 renderSquare 的i 保持引用 //-----传参数的函数需要两次箭头函数 -
直接在onChange时候使用bind(this),并不推荐
<input type="text" value={this.state.add} onChange={(this.handleChange.bind(this)} //和我们上边你的添加按钮一样 /> -
定义handleChang的时候直接使用箭头函数
handleChange=(e)=>{ this.setState({ add:e.target.value }) };
-
-
-
函数组件 :
如果一个组件只根据props进行渲染,没有内部的state,我们完全可以使用函数式组件的形式来实现
其实认真观察 函数式组件和class 组件的 props 是有区别的函数式props是通过参数传递就没有this
class的组件是有this的一般都是this.props
// 函数式组件 function Title(props) { // React的函数式组件props就是传递的值props.title也能直接取到 return <div> <h2>{props.title}</h2> <hr/> </div> } <Title title={this.props.title} /> //使用的时候和组件一样 -
组件通信
通过props进行通信
import React from 'react' function Totol({cart}){ return <span> 总价:{cart.reduce((sum,a)=>{ return sum += a.price*a.count },0)} </span> } class Cart extends React.Component{ render() { return <div> <table border="1"> <thead> <tr> <th>商品名</th> <th>价格</th> <th>数量</th> <th>总价</th> </tr> </thead> <tbody> {this.props.data.map(good=>{ return<tr key={good.text}> <td>{good.text}</td> <td>{good.price}</td> <td>{good.count}</td> <td>{good.count*good.price}</td> </tr> })} {/*使用组件做一个总价*/} <tr> <td colSpan="4" align="right"> <Totol cart={this.props.data} /> </td> </tr> </tbody> </table> </div> } } export default CartAddCart(good){ let cartIndex; const cartGood = this.state.cart.find((value,index) => { if (good.text === value.text) { cartIndex = index; return true } }); if (cartGood){ // 我们要知道我们找到的是第几个,因为我们不能直接修改 let newCart = [...this.state.cart]; newCart[cartIndex].count+=1; this.setState({ cart:newCart }) } else{ this.setState({ cart:[...this.state.cart,{ text:good.text, price:good.price, count:1 }] }) } } <Cart data={this.state.cart}/> -
利用react的每一次都需要setState使用一个新的对象{实现类似时间旅行功能
//新加一个state的cartHistory:[]来记录 在每次操作都将操作的数据记录,然后点击返回对应步骤的cartHistory this.setState({ cartHistory:[...this.state.cartHistory,this.state.cart] }) resetCart(i){ //点击的事件处理 this.setState({ cart:this.state.cartHistory[i] }) } -
虚拟DOM
虚拟DOM是在DOM的基础上建立了一个抽象层,对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到DOM中。
在React中,render执行的结果得到的并不是真正的DOM节点,而仅仅是JavaScript对象,称之为虚拟DOM。
虚拟DOM具有批处理和高效的Diff算法,可以无需担心性能问题而随时“刷新”整个页面,因为虚拟DOM可以确保只对界面上真正变化的部分进行实际的DOM操作。
- 传统APP:
- React App:
- innerHTML:render html字符串 + 重新创建所有的DOM元素
- 虚拟DOM:render 虚拟DOM + diff + 更新必要的DOM元素
- 原理:React会在内存中维护一个虚拟DOM树,对这个树进行读或写,实际上是对虚拟DOM进行。当数据变化时,React会自动更新虚拟DOM,然后将新的虚拟DOM和旧的虚拟DOM进行对比,找到变更的部分,得出一个diff,然后将diff放到一个队列里,最终批量更新这些diff到DOM中。
-
生命周期
React 生命周期分为三种状态 1. 初始化 2.更新 3.销毁
-
初始化
-
getDefaultProps()
设置默认的props,也可以用defaultProps设置组件的默认属性 这个方法只会调用一次,该组件类的所有后续应用,getDefaultPops 将不会再被调用,其返回的对象可以用于设置默认的 props(properties的缩写) 值。 -
getInitialState()
这个方法的调用有且只有一次,用来初始化每个实例的 state,在这个方法里,可以访问组件的 props。每一个React组件都有自己的 state,其与 props 的区别在于 state只存在组件的内部,props 在所有实例中共享。 getInitialState 和 getDefaultPops 的调用是有区别的,getDefaultPops 是对于组件类来说只调用一次,后续该类的应用都不会被调用,而 getInitialState 是对于每个组件实例来讲都会调用,并且只调一次。 -
componentWillMount()
该方法在 首次(也就是只调用一次) 渲染之前调用,也是再 render 方法调用之前修改 state 的最后一次机会。 -
render()
该方法会创建一个虚拟DOM,用来表示组件的输出。对于一个组件来讲,render方法是唯一一个必需的方法。render方法需要满足下面几点: 1.只能通过 this.props 和 this.state 访问数据(不能修改) 2.可以返回 null,false 或者任何React组件 3.只能出现一个顶级组件,不能返回一组元素 4.不能改变组件的状态 5.不能修改DOM的输出 render方法返回的结果并不是真正的DOM元素,而是一个虚拟的表现,类似于一个DOM tree的结构的对象。react之所以效率高,就是这个原因。 -
componentDidMount()
组件渲染之后调用,只调用一次
-
-
更新 :
此时组件已经渲染好并且用户可以与它进行交互,比如鼠标点击,手指点按,或者其它的一些事件,导致应用状态的改变,你将会看到下面的方法依次被调用
-
componentWillReceiveProps(nextProps)
组件的 props 属性可以通过父组件来更改,这时,componentWillReceiveProps 将来被调用。可以在这个方法里更新 state,以触发 render 方法重新渲染组件。 -
shouldComponentUpdate(nextProps, nextState)
react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候 该方法是非必须的,并且大多数情况下没有在开发中使用。 -
componentWillUpdata(nextProps, nextState)
这个方法和 componentWillMount 类似,在组件接收到了新的 props 或者 state 即将进行重新渲染前,componentWillUpdate(object nextProps, object nextState) 会被调用,注意不要在此方面里再去更新 props 或者 state。 -
render()
更新之后再次进行组件渲染 -
componentDidUpdate()
这个方法和 componentDidMount 类似,在组件重新被渲染之后,componentDidUpdate(object prevProps, object prevState) 会被调用。可以在这里访问并修改 DOM
-
-
销毁
-
componentWillUnmount()
每当React使用完一个组件,这个组件必须从 DOM 中卸载后被销毁,此时 componentWillUnmout 会被执行,完成所有的清理和销毁工作,在 componentDidMount 中添加的任务都需要再该方法中撤销,如创建的定时器或事件监听器。
-
-