本文在github做了收录 github.com/Michael-lzg…
React 的特点
- 应用 JSX 语法 创立组件,实现组件化开发,为函数式的 UI 编程形式关上了大门
- 性能高的让人称誉:通过 diff 算法 和 虚构 DOM 实现视图的高效更新
为什么要用 React
- 应用组件化开发方式,合乎古代 Web 开发的趋势
- 技术成熟,社区欠缺,配件齐全,实用于大型 Web 我的项目(生态系统健全)
- 由 Facebook 专门的团队保护,技术支持牢靠
- ReactNative - Learn once, write anywhere: Build mobile apps with React
- 应用形式简略,性能十分高,反对服务端渲染
- React 十分火,从技术角度,能够满足好奇心,进步技术水平;从职业角度,有利于求职和降职,有利于参加后劲大的我的项目
创立组件的两种形式
- 通过 JS 函数 创立(无状态组件)
- 通过 class 创立(有状态组件)
JS 函数创立
- 函数名称必须为大写字母结尾,React 通过这个特点来判断是不是一个组件
- 函数必须有返回值,返回值能够是:JSX 对象或 null
- 返回的 JSX,必须有一个根元素
- 组件的返回值应用()包裹,防止换行问题
function LearningList(props) { return ( <div className="list"> <h1>LearningList for {props.name}</h1> <ul> <li>Vue</li> <li>React</li> <li>Angular</li> </ul> </div> )}ReactDOM.render(<Welcome name="lzg" />, document.getElementById('app'))
class 创立
在 es6 中 class 仅仅是一个语法糖,不是真正的类,实质上还是构造函数+原型 实现继承
class LearningList extends React.Component { constructor(props) { super(props) } // class创立的组件中 必须有render办法 且显示return一个react对象或者null render() { return ( <div className="list"> <h1>LearningList for {props.name}</h1> <ul> <li>Vue</li> <li>React</li> <li>Angular</li> </ul> </div> ) }}
jsx 语法
jsx 语法是一种 JavaScript 语法扩大,在 React 中能够不便地用来形容 UI。比方上面就是一段 jsx 语法
const element = <h1>Hello, world!</h1>
下面的代码本质上等价于:
var element = React.createElement('h1', null, 'Hello, world!')
jsx 的书写标准
- jsx 的顶层只能有一个根元素,所以咱们很多时候会在外层包裹一个 div(或者应用 Fragment)
- jsx 中的标签能够是单标签,也能够是双标签;如果是单标签,必须以/>结尾
- jsx 的外层包裹一个小括号(),这样能够不便浏览,并且 jsx 能够进行换行书写
- 在{}外部,能够写任何合乎 JS 标准的代码;如果要写正文,正文必须放到 {} 外部
jsx 中嵌入表达式
在 jsx 语法中,你能够在大括号内搁置任何无效的 JavaScript 表达式,它能够是
- 运算表达式
- 三元运算符
- 执行一个函数
class App extends React.Component { constructor(props) { super(props) this.state = { firstName: 'kobe', lastName: 'bryant', age: 20, } } sayHello(name) { return 'Hello ' + name } render() { return ( <div> <h2>{this.state.firstName + ' ' + this.state.lastName}</h2> <h2>{this.state.age >= 18 ? '成年人' : '未成年人'}</h2> <h2>{this.sayHello('lzg')}</h2> </div> ) }}
jsx 条件渲染
1、两个组件二选一的渲染
class HelloMessage extends React.Component { render() { let userMessage if (this.props.loggedIn) { userMessage = ( <h1>Welcome back!</h1> ) } else { userMessage = ( <h1>Please sign up.</h1>; ) } return ( <div> <h1>My Super React App</h1> {userMessage} </div> ) }}
2、一个组件有无的渲染
function MessageList(props) { const unreadMessages = props.unreadMessages return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2>You have {unreadMessages.length} unread messages.</h2>} </div> )}
jsx 列表循环
在jsx语法中,循环渲染是利用数组的遍历 map() 办法返回一个汇合。
遍历时必须有惟一索引 key 进步遍历的效率。一个元素的 key 最好是这个元素在列表中领有的一个举世无双的字符串,万不得已能够应用 index。
class Admin extends Component { constructor(props) { super(props) this.state = { menus: [ { icon: require('../imag/home/ic_noti_block@2x.png'), title: '菜单1' }, { icon: require('../imag/home/ic_noti_block@2x.png'), title: '菜单2' }, { icon: require('../imag/home/ic_noti_block@2x.png'), title: '菜单3' }, { icon: require('../imag/home/ic_noti_block@2x.png'), title: '菜单4' }, ], } } renderList(value, index) { return ( <li key={index}> <p>张三</p> <p>18岁</p> </li> ) } render() { return ( <div> <ul> {[1, 2, 3, 4, 5].map((value, index) => { return this.renderList(value, index) })} </ul> <ul> {this.state.menus.map((value, index) => { return ( <li key={index}> <img src={value.icon} width="30" /> <div>{value.title}</div> </li> ) })} </ul> </div> ) }}
接收数据 props
咱们想要在组件之间进行传值,那么 props 属性就起到了这个作用。react 的每个组件都能够承受一个 props 参数,它是一个对象,蕴含了所有你对这个组件的配置。
特点
- React 把传递给组件的属性转化为一个对象并交给 props
- props 是只读的,无奈给 props 增加或批改属性
function Person(props) { return ( <ul> <li>姓名: {props.name}</li> <li>年龄: {props.age}</li> </ul> )}ReactDOM.render(<Welcome name="lzg" age="18" />, document.getElementById('app'))
组件状态 state
如果须要定义组件的自定义属性,须要在组件的 constructor 构造函数外面去定义 state
特点
- 只有通过 class 创立的组件才具备 state
- state 是公有的,齐全由组件来管制
- 不要在 state 中增加 render() 办法中不须要的数据,会影响渲染性能!
- 不要在 render() 办法中调用 setState() 办法来批改 state 的值
state 和 props 的区别
props 是组件对外的接口,state 是组件对内的接口。两者次要的区别: state 是可变的,是组件外部保护的一组返回 ui 组件的汇合,而 props 是组件的只读属性,组件内不能间接批改 props,只能在组件的下层批改。
创立 state
如果须要定义组件的自定义属性,在组件的 constructor 构造函数外面去定义 state
class Mycom extends React.Component { constructor(props) { super(props) //给this.state赋值一个对象,对象的属性就是组件的自定义属性 this.state = { name: 'lzg', } }}
批改 state
不能间接去批改 state 的值,否则数据无奈驱动关联,须要应用 setState,setState 办法接管一个参数,参数为一个对象,相似于小程序原生的 setData。
// 谬误形式this.state.name = 'lzg'// 正确形式this.setState({ name: 'lzg' })
应用 this.setState()的第二种模式总是更平安的,因为更新的 props 和状态是异步的。这里,咱们依据这些 props 更新状态。
// 谬误形式this.setState({ total: this.state.total + this.props.count,})// 正确形式this.setState((state, props) => { total: state.total + props.count})
另外,setState 还能够接管第二个参数,第二个参数为一个回调函数
this.setState( { name: 'lzg', }, () => { console.log('state值批改胜利,当初的name值为' + this.state.name) })
为啥不能间接批改 state,要 setState 一下呢?setState 做的事件不仅仅只是批改了 this.state 的值,另外最重要的是它会触发 React 的更新机制,会进行 diff ,而后将 patch 局部更新到实在 dom 里。
事件绑定
1、 在调用的时候应用 bind 绑定 this
class Foo extends React.Component { handleClick() { this.setState({ name: 'lzg' }) } render() { return <button onClick={this.handleClick.bind(this)}>Click me</button> }}
2、在构造函数中应用 bind 绑定 this
class Foo extends React.Component { constuctor(props) { super(props) this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState({ name: 'lzg' }) } render() { return <button onClick={this.handleClick}>Click me</button> }}
3、应用箭头函数绑定 this
class Foo extends React.Component { handleClick() { this.setState({ name: 'lzg' }) } render() { return <button onClick={(e) => this.handleClick(e)}>Click me</button> }}
4、public class fields 型
class Foo extends React.Component { handleClick = () => { this.setState({ name: 'lzg' }) } render() { return <button onClick={this.handleClick}>Click me</button> }}
react 款式
- 间接写行内款式:
export const Footer = () => { return <div style={{{color:'orange', fontSize:'12px'}}>All Rights Reserved 2019</div>}
- 抽离为对象模式
import React from 'react'const footerStyle = { backgroundColor: 'green', fontSize: '12px', color: 'orange', fontWeight: 'bold',}export const Footer = () => { return <div style={footerStyle}>All Rights Reserved 2019</div>}
- 应用样式表定义款式:
import '../css/comment.css'export const Footer = () => { return <div className="footer">All Rights Reserved 2019</div>}
生命周期函数
组件的生命周期蕴含三个阶段:创立阶段(Mounting)、运行和交互阶段(Updating)、卸载阶段(Unmounting)
Mounting 顺次调用以下函数
- constructor():ES6 类的构造函数(为了初始化 state 或绑定 this)
- getInitialState():ES5 中初始化 state。
- getDefaultProps():ES5 中初始化 props。在 ES6 中应用 defaultProps()办法。
- componentWillMount():在组件被挂载前调用。只执行一次。
- render():渲染组件,必须实现该办法。
- componentDidMount():在组件装载后调用。这时曾经生成了实在的 DOM 节点。只执行一次。
Updating 顺次调用以下函数
- componentWillReceiveProps() 组件承受到新的 props 前触发这个办法
- shouldComponentUpdate() 依据这个办法的返回值决定是否从新渲染组件,返回 true 从新渲染,否则不渲染
- componentWillUpdate() 组件将要更新
- render() 从新渲染组件,与 Mounting 阶段的 render 是同一个函数
- componentDidUpdate() 组件曾经被更新
Unmounting
- componentWillUnmount() 卸载组件;革除定时器,革除 dom
PropTypes
随着工夫的推移,应用程序会变得越来越大,因而类型查看十分重要。PropTypes 为组件提供类型查看,并为其余开发人员提供很好的文档。如果 react 我的项目不应用 Typescript,倡议为组件增加 PropTypes。
// 旧的写法class PropTypeOne extends React.Component { render() { return ( <div> <div>{this.props.name}</div> <div>{this.props.email}</div> </div> ) }}PropTypeOne.propTypes = { name: PropTypes.string, email: function (props, propName, componentName) { if (!/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/.test(props[propName])) { return new Error('组件' + componentName + '里的属性' + propName + '不合乎邮箱的格局') } },}// 新的写法class PropTypeTwo extends React.Component { static propTypes = { name: PropTypes.string, } render() { return ( <div> <div>{this.props.name}</div> </div> ) }}
当传入的 prop 值类型不正确时,JavaScript 控制台将会显示正告。出于性能方面的思考,propTypes 仅在开发模式下进行查看。
您能够通过配置特定的 defaultProps 属性来定义 props 的默认值:
class Greeting extends React.Component { render() { return <h1>Hello, {this.props.name}</h1> }}// 指定 props 的默认值:Greeting.defaultProps = { name: 'Stranger',}// 渲染出 "Hello, Stranger":ReactDOM.render(<Greeting />, document.getElementById('app'))
受控与非受控组件
受控组件
咱们要常常应用表单来收集用户输出,例如 <input>
<select>
<textearea>
等元素都要绑定一个 change 事件,当表单的状态发生变化,就会触发 onChange 事件,更新组件的 state。这种组件在 React 中被称为受控组件,在受控组件中,组件渲染出的状态与他的 value 或 checked 属性绝对应,react 通过这种形式打消了组件的部分状态,使整个状态可控。react 官网同样举荐应用受控表单组件。
import React, { Component } from 'react'export default class MyInput extends Component { handleContentChange = (e) => { this.setState({ content: e.target.value, }) } render() { return ( <div> <input type="text" value={this.state.value} onChange={this.handleContentChange} /> </div> ) }}
受控组件更新 state 的流程:
- 能够通过初始 state 中设置表单的默认值
- 每当表单的值发生变化时,调用 onChange 事件处理器
- 事件处理器通过事件对象 e 拿到扭转后的状态,并更新组件的 state
- 一旦通过 setState 办法更新 state,就会触发视图的从新渲染,实现表单组件的更新
react 中数据是单项流动的,从示例中,咱们看进去表单的数据来源于组件的 state,并通过 props 传入,这也称为单向数据绑定。而后咱们又通过 onChange 事件处理器将新的数据写回到 state,实现了双向数据绑定。
非受控组件
如果一个表单组件没有 value props(单选和复选按钮对应的是 checked props)时,就能够称为非受控组件.在非受控组件中,咱们能够应用一个 ref 来从 DOM 取得表单值。而不是为每个状态更新编写一个事件处理程序。
class NameForm extends React.Component { constructor(props) { super(props) this.handleSubmit = this.handleSubmit.bind(this) } handleSubmit(event) { alert('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> ) }}
ref
React 反对一种十分非凡的属性 Ref ,你能够用来绑定到 render() 输入的任何组件上。
字符串用法
通过 this.refs['inputRef']
来拜访
<input ref="inputRef" />
ref 作为回调函数
class AutoFocusTextInput extends Component { componentDidMount(){ this.textInput.focus(); } render(){ return ( <Input ref={(input) => { this.textInput = input }}> ) }}
父组件的 ref 回调函数能够应用子组件的 DOM。
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> )}class Parent extends React.Component { render() { return <CustomTextInput inputRef={(el) => (this.inputElement = el)} /> }}
React.createRef()
在 React 16.3 版本后,应用此办法来创立 ref。将其赋值给一个变量,通过 ref 挂载在 dom 节点或组件上,该 ref 的 current 属性 将能拿到 dom 节点或组件的实例
class Child extends React.Component { constructor(props) { super(props) this.myRef = React.createRef() } componentDidMount() { console.log(this.myRef.current) } render() { return <input ref={this.myRef} /> }}
react-router
react-router-dom 是应用程序中路由的库。 React 库中没有路由性能,须要独自装置 react-router-dom。
react-router-dom 提供两个路由器 BrowserRouter 和 HashRoauter。前者基于 url 的 pathname 段,后者基于 hash 段。
根本应用:
npm install react-router-dom --save
import { BrowserRouter, Route, Link, Redirect } from 'react-router-dom'class MyRouter extends React.Component { render() { return ( <BrowserRouter> <Link to="/">router1</Link> <Link to="/router2">router2</Link> <Link to="/router3">router3</Link> <hr /> <Redirect exact from="/" to="/router1" /> <Route path="/router1" component={router1}></Route> <Route path="/router2" component={router2}></Route> <Route path="/router3" component={router3}></Route> </BrowserRouter> ) }}
路由跳转
1、用 Link 标签跳转
import { BrowserRouter, Route, Link, Redirect } from 'react-router-dom'class MyRouter extends React.Component { render() { return ( <BrowserRouter> <Link to="/">router1</Link> <Link to="/router2">router2</Link> <Link to="/router3">router3</Link> <hr /> <Redirect exact from="/" to="/router1" /> <Route path="/router1" component={router1}></Route> <Route path="/router2" component={router2}></Route> <Route path="/router3" component={router3}></Route> </BrowserRouter> ) }}
2、编程式导航
- 路由组件能够间接从 this.props.history 上拿到 history
- 非路由组件无奈间接拿到 history,须要配合 withRouter
this.props.history.push(url)this.props.history.go(-1)
路由传参
1.params
<Route path='/path/:name' component={Path}/><link to="/path/123">xxx</Link>this.props.history.push({pathname:"/path/" + name});// 读取参数用:this.props.match.params.name
2.query
<Route path='/query' component={Query}/><Link to={{ pathname : '/query' , query : { name : 'sunny' }}}></Link>this.props.history.push({pathname:"/query",query: { name : 'sunny' }});// 读取参数用:this.props.location.query.name
3.state
<Route path='/sort ' component={Sort}/><Link to={{ pathname : '/sort ' , state : { name : 'sunny' }}}></Link>this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }});// 读取参数用: this.props.location.query.state
4.search
<Route path='/web/search ' component={Search}/><link to="web/search?id=12121212">xxx</Link>this.props.history.push({pathname:`/web/search?id ${row.id}`});// 读取参数用: this.props.location.search
路由守卫
Route 组件能够接管一个 Component 组件,当 path 匹配上的时候,这个 Route 组件就会被渲染进去。咱们还能够在门路匹配之后做一点事件,这一点相似于 Vue 中的路由守卫。
用到的还是 Route 这个组件,只不过这次组件不通过 Component 去传递数据,通过 render 属性。
import { Route } from 'react-router-dom'function Custom() { return ( <Route path="/index" Render={() => { //isLogin判断用户是否登录,如果登录了渲染首页,没有登录渲染登录 if (isLogin) { return <Index></Index> } else { return <Login></Login> } }} /> )}
withRouter
高阶组件中的 withRouter, 作用是将一个组件包裹进 Route 外面, 而后 react-router 的三个对象 history, location, match 就会被放进这个组件的 props 属性中。
默认状况下必须是通过路由匹配渲染的组件才存在 this.props
,才领有路由参数,能力应用编程式导航的写法,执行 this.props.history.push('/detail')
跳转到对应路由的页面,然而不是所有组件都间接与路由相连(通过路由跳转到此组件)的,当这些组件须要路由参数时,应用 withRouter 就能够给此组件传入路由参数,此时就能够应用 this.props。
import React,{Component} from 'react'import {Switch,Route,NavLink,Redirect,withRouter} from 'react-router-dom' //引入withRouterimport One from './One'import NotFound from './NotFound'class App extends Component{ //此时能力获取this.props,蕴含(history, match, location)三个对象 console.log(this.props); //输入{match: {…}, location: {…}, history: {…}, 等} render(){ return (<div className='app'> <NavLink to='/one/users'>用户列表</NavLink> <NavLink to='/one/companies'>公司列表</NavLink> <Switch> <Route path='/one/:type?' component={One} /> <Redirect from='/' to='/one' exact /> <Route component={NotFound} /> </Switch> </div>) }}export default withRouter(App); //这里要执行一下WithRouter
举荐文章
谈谈数据状态治理和实现一个简易版vuex
总结18个webpack插件,总会有你想要的!
搭建一个 vue-cli4+webpack 挪动端框架(开箱即用)
从零构建到优化一个相似vue-cli的脚手架
封装一个toast和dialog组件并公布到npm
从零开始构建一个webpack我的项目
总结几个webpack打包优化的办法
总结vue常识体系之高级利用篇
总结vue常识体系之实用技巧
总结vue常识体系之根底入门篇
总结挪动端H5开发罕用技巧(干货满满哦!)