react脚手架(create-react-app)
1.作用: 帮助我们生成一个通用的目录结构,并且已经将我们所需的工程环境配置好
2.依赖环境
脚手架都是使用node编写的,并且都是基于webpack的;
3.安装node
4.安装脚手架
npm install -g create-react-app
5.检查脚手架版本
create-react-app --version
创建项目
create-react-app 项目名称
跑项目
yarn start
目录
React组件化开发
类组件
定义:
- 组件的名称是大写字符开头的(无论类组件还是函数组件)
- 类组件需要继承自React.Component
- 类组件必须实现render函数
使用class定义一个组件:
constructor是可选的,我们通常在constructor中初始化一些数据
this.state中维护的就是我们组件内部的数据
render()方法是class组件中
函数式组件
/*** * 函数式组件的特点:* 1.没有this对象* 2.没有内部的状态* 3.没有生命周期* */
export default function App(){return (<div>我是function组件</div>)
}
类组件
import React, { Component } from "react";export default class App extends Component {constructor() {super();this.state = {message: 'hello'}}render () {return (<div><div>app组件</div>{/* alt+shift+f 代码格式化*/}<h2>{this.state.message}</h2></div>)}
}
render函数的返回值
- React元素:通常通过JSX创建
- 数组或fragments
- portals:可以渲染子节点到不同的DOM子树中
- 字符串或数值类型
- 布尔类型或null:什么都不渲染
生命周期
很多事物从创建到销毁的整个过程,这个过程称之为生命周期。
constructor
如果不初始化state或不进行方法绑定,则不需要为React组件实现构造函数。
constructor中通常只做两件事情:
- 通过给this.state赋值对象来初始化内部的state
- 为事件绑定实例(this)
componentDidMount
componentDidMount()会在组件挂在后(插入DOM树中)立即调用
操作:
- 依赖于DOM的操作可以在这里进行
- 在此处发送网络请求就最好的地方(官方建议)
- 可以在此处添加一些订阅(会在componentWillUnmount取消订阅)
componentDidUpdate
componentDidUpdate()会在更新后会被立即调用,首次渲染不会执行此方法。
当组件更新后,可以在此处对DOM进行操作。
如果你对更新前后的props进行了比较,也可以选择在此处进行网络请求;(例如,当props未发生变化时,则不会执行网络请求)
componentWillUnmount
componentWillUnmount() 会在组件卸载及销毁之前直接调用。
在此方法中执行必要的清理操作;
例如:清除timer,取消网络请求或清楚
在componentDidMount()中创建的订阅等;
import React, { Component } from 'react'class Cpn extends Component {render () {return <h2>我是cpn组件</h2>}componentWillUnmount () {console.log('我调用了componentWillUnmount')}
}export default class App extends Component {constructor() {super();this.state = {count: 1,isShow: true}console.log('执行了组件的constructor')}render () {console.log('执行了组件的render函数')return (<div>我是App组件<h2>当前计数:{this.state.count}</h2><button onClick={e => { this.increment() }}>+1</button><hr /><button onClick={e => { this.changeShow() }}>切换</button>{this.state.isShow && <Cpn />}</div>)}increment () {this.setState({counter: this.state.counter + 1})}changeShow () {this.setState({isShow: !this.state.isShow})}componentDidMount () {console.log('执行了componentDidMount方法')}componentDidUpdate (previProps,preState,snapshot) {console.log('执行了componentDidUpdate方法')}
}
组件通信
父->子 props
子->父 itemOnclock
1.父传子-类组件
import React, { Component } from 'react'class ChilCpn extends Component{// constructor(props){// super(props);// }render(){const {name,age,height} = this.propsreturn(<h2>子组件展示数据:{name+' '+age+' '+height}</h2>)}
}export default class App extends Component {render() {return (<div><ChilCpn name='guo' age='18' height='1.55'/></div>)}
}
2.父传子-函数组件-- 参数验证
效果:
import React, { Component } from 'react'import PropTypes from 'prop-types'function ChildCpn(props){const {name,age,height} = propsconst {names} = propsreturn (<div><h2>{name+' '+ age+' '+height}</h2>{/*参数验证 */}<ul>{names.map((item,index)=>{return <li>{item}</li>})}</ul></div>)
}
ChildCpn.propTypes = {name:PropTypes.string.isRequired, // 必传age:PropTypes.number,height:PropTypes.number,names:PropTypes.array
}// 默认值
ChildCpn.defaultProps = {name:'wen',age:20,height:1.75,names:['111']
}export default class App extends Component {render() {return (<div><ChildCpn name='guo' age={18} height={1.55} names={['abc','mab']}/><ChildCpn/></div>)}
}
类组件的参数校验
class ChildCpn extends Component{// es6中class fields写法static propTypes = {}static defaultProps = {}
}
3.子组件传递父组件
import React, { Component } from 'react'class CounterButton extends Component{render(){const {onClick} = this.props;return <button onClick= {onClick}>+1</button>}
}export default class App extends Component {constructor(props){super(props);this.state = {counter:0}}render() {return (<div><h2>当前计数{this.state.counter}</h2><button onClick= {e=>this.increment()}>+</button><CounterButton onClick={this.increment.bind(this)} /><CounterButton onClick={e=>this.increment()} /></div>)}increment(){this.setState({counter:this.state.counter + 1})}
}
4.组件通信案例-选项卡
APP.js
import React, { Component } from 'react'import TabControl from "./TabControl"export default class App extends Component {constructor(props){super(props);this.titles = ['精选','流行','新款']this.state = {currentTitle:'精选'}}render() {const {currentTitle} = this.statereturn (<div><TabControl itemClick={index=>this.itemClick(index)} titles={this.titles}/><h2>{currentTitle}</h2></div>)}itemClick(index){console.log(index)this.setState({currentTitle:this.titles[index]})}
}
tabcontrol.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'export default class TabControl extends Component {constructor() {super()this.state = {currentIndex: 1}}render () {const { titles } = this.props;const { currentIndex } = this.statereturn (<div className="tab-control">{titles.map((item, index) => {return (<div key={index}className={'tab-item ' + (index === currentIndex ? 'active' : '')}onClick={e => this.itemClick(index)}><span>{item}</span></div>)})}</div>)}itemClick (index) {this.setState({currentIndex: index})const {itemClick} = this.propsitemClick(index)}
}
TabControl.propTypes = {titles: PropTypes.array.isRequired
}
5.类似插槽
效果
App.js
import React, { Component } from 'react'import NavBar from './NavBar'
export default class App extends Component {render() {return (<div><NavBar leftSlot={<span>aaa123</span>}centerSlot= {<strong>bbb</strong>}rightSlot= {<span>123</span>}/></div>)}
}
NavBar.js
import React, { Component } from 'react'export default class NavBar extends Component {render () {const {leftSlot,centerSlot,rightSlot} = this.props;return (<div className="nav-bar"><div className="nav-left">{leftSlot}</div><div className="nav-center">{centerSlot}</div><div className="nav-right">{rightSlot}</div></div>)}
}
6.跨组件通信-古老一层一层传递
anli.js
import React, { Component } from 'react'function ProfileHeader(props){return(<div><h2>用户昵称1:{props.nickname}</h2><h2>粉丝等级:{props.level}</h2></div>)
}function Profile(props){return(<div>{/* <ProfileHeader nickname={props.nickname} level= {props.level}></ProfileHeader> */}{/* 属性展开 */}<ProfileHeader {...props}/><ul><li>设置1</li><li>设置2</li><li>设置3</li><li>设置4</li></ul></div>)
}export default class anli extends Component {constructor(props){super(props);this.state = {nickname:'ming',level:9}}render() {// const {nickname,level} = this.statereturn (<div>{/* <Profile nickname= {nickname} level={level}></Profile> */}<Profile {...this.state}></Profile></div>)}
}
6.跨组件通信-context
React.createContext
创建一个需要共享的Context对象:
如果一个组件订阅了Context,那么这个组件会从离自身最近的那个匹配的Provider中读取当前的context值;
defaultValue是组件在顶层查找过程中没有找到对应的Provider,那么久使用默认值
const MyContext = React.createContext(defaultValue);
Context.Provider
每个Context对象都会返回一个Provider React组件,它允许消费组件订阅Context的变化:
Provider接收一个value属性,传递给消费组件;
一个Provider可以和多个消费组件有对应关系
多个Provider也可以嵌套使用,里层的会覆盖外层的数据;
当Provider的value值发生变化时,它内部的所有消费组件都会重新渲染;
Class.contextType
挂载在class上的contextType属性会被重赋值为一个由React.createContext()创建的Context对象;
这能让你使用this.context来消费最近Context上的那个值;
你可以在任何生命周期中访问到它,包括render函数中
效果:
anli.js
import React, { Component } from 'react'//1.创建Context对象
const UserContext = React.createContext({nickname: 'lan',level: 3
})class ProfileHeader extends Component {render () {console.log(this.context)return (<div><h2>用户昵称:{this.context.nickname}</h2><h2>粉丝等级:{this.context.level}</h2></div>)}
}
ProfileHeader.contextType = UserContextfunction Profile (props) {return (<div><ProfileHeader></ProfileHeader><ul><li>设置1</li><li>设置2</li><li>设置3</li><li>设置4</li></ul></div>)
}export default class anli extends Component {constructor(props) {super(props);this.state = {nickname: 'ming',level: 9}}render () {return (<div>{/* 第二步 */}<UserContext.Provider value={this.state}><Profile /></UserContext.Provider></div>)}
}
函数用法-其余代码参考上面代码
function ProfileHeader () {return (<UserContext.Consumer>{value => {return (<div><h2>用户昵称:{value.nickname}</h2><h2>粉丝等级:{value.level}</h2></div>)}}</UserContext.Consumer>)
}