react
说明
此文全是在index.js中的文件,除了独立js文件那块
一、 组件
import React from 'react';
import ReactDOM from 'react-dom';
1.函数组件
大写字母开头,必须要有返回
function Hello(){
return (
<div>这是第一个函数组件Hello,平常函数</div>
)
}
const Hello = () => <div>这是第一个函数组件Hello,箭头函数</div>
ReactDOM.render(<Hello />,document.getElementById('root'))
2.类组件:
大写开头,继承React.Component父类,必须提供render方法且有返回值
class Hello extends React.Component{
render(){
return (
<div>这是第一个类组件</div>
)
}
}
ReactDOM.render(<Hello />,document.getElementById('root'))
3.独立js文件
抽离js文件到独立文件中,新建一个Hello.js
import React from 'react';
//创建组件
class Hello extends React.Component{
render(){
return (
<div>这是第一个抽离到js文件中的类组件</div>
)
}
}
// 导出组件
export default Hello
index.js先导入组件再使用
import Hello from './Hello';
ReactDOM.render(<Hello></Hello>, document.getElementById('root'))
4. React 事件绑定
方法一,通过类组件(有状态组件,动)
class App extends React.Component{
//事件处理程序
handleClick(){
console.log('点我了,哈哈哈')
}
render(){
return (
<button onClick={this.handleClick}>点我 点我</button>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
方法二,通过函数组件(无状态组件,静)
function App(){
function handleClick(){
console.log('基于函数组件点击')
}
return (
<button onClick={handleClick}>点我 点我</button>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
5.事件对象,e
class App extends React.Component{
handleClick(e){
//阻止浏览器默认行为
e.preventDefault()
console.log('a标签的单击事件触发了')
}
render(){
return (
<a href='http://lichee.top' onClick={this.handleClick}>lichee.top</a>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
6.state的基本使用
class App extends React.Component {
// constructor(){
// super()
// // 初始化state
// this.state = {
// count:0
// }
// }
//简化写法
state = {
count:52
}
render(){
return (
<div>
<h1>计数器:{this.state.count}</h1>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
7.setState的修改
修改状态,更新UI
class App extends React.Component {
state = {
count:0
}
render(){
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={()=>{
this.setState({
count:this.state.count+1
})
}}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
8.解决this指向
class App extends React.Component {
state = {
count:0
}
constructor(){
super()
this.onIncrement = this.onIncrement.bind(this)
}
// onIncrement(){
// this.setState({
// count:this.state.count+1
// })
// }
// 3.class实例方法来解决this指向
onIncrement =() =>{
this.setState({
count:this.state.count+1
})
}
render(){
return (
<div>
<h1>计数器:{this.state.count}</h1>
{/* <button onClick={this.onIncrement}>+1</button> */}
{/* 1.箭头函数解决this指向问题 */}
{/* <button onClick={() =>this.onIncrement()}>+1</button> */}
{/* 2.通过funcatopn.prototype.bind()解决 */}
<button onClick={this.onIncrement}>+1</button>
{/* <button onClick={()=>{
this.setState({
count:this.state.count+1
})
}}>+1</button> */}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
9.表单受控组件
class App extends React.Component{
state= {
txt:''
}
handleChange = e =>{
this.setState({
txt: e.target.value
})
}
render(){
return (
<div>
<input type="text" value={this.state.txt} onChange={this.handleChange}></input>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
多种表单
input、富文本、下拉框、多选框
class App extends React.Component{
state= {
txt:'',
content:'',
city:'bj',
isChecked:false
}
handleChange = e =>{
this.setState({
txt: e.target.value
})
}
//处理富文本框
handleCondent = e =>{
this.setState({
content: e.target.value
})
}
handleCity = e=>{
this.setState({
city: e.target.value
})
}
handleChecked=e=>{
this.setState({
isChecked: e.target.checked
})
}
render(){
return (
<div>
<input type="text" value={this.state.txt} onChange={this.handleChange}></input>
<br />
<textarea value={this.state.content} onChange={this.handleCondent}></textarea>
<br />
<select value={this.state.city} onChange={this.handleCity}>
<option value='sh'>上海</option>
<option value='bj'>北京</option>
<option value='gz'>广州</option>
</select>
<br />
<input type="checkbox" checked={this.state.isChecked} onChange={this.handleChecked}/>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
优化
class App extends React.Component{
state= {
txt:'',
content:'',
city:'bj',
isChecked:false
}
handleForm = e =>{
//获取当前DOM对象
const target = e.target
const value = target.type === 'checkbox'
? target.defaultChecked
: target.value
const name = target.name
this.setState({
[name]: value
})
}
render(){
return (
<div>
<input type="text" name='txt' value={this.state.txt} onChange={this.handleForm}></input>
<br />
<textarea name='content' value={this.state.content} onChange={this.handleForm}></textarea>
<br />
<select name='city' value={this.state.city} onChange={this.handleForm}>
<option value='sh'>上海</option>
<option value='bj'>北京</option>
<option value='gz'>广州</option>
</select>
<br />
<input type="checkbox" name='isChecked' defaultChecked={this.state.isChecked} onChange={this.handleChecked}/>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
10.非受控组件
class App extends React.Component{
constructor(){
super()
//创建ref
this.txtRef = React.createRef()
}
getTxt = ()=>{
console.log('文本框的值为:',this.txtRef.current.value)
}
render (){
return(
<div>
<input type="text" ref={this.txtRef}/>
<br />
<button onClick={this.getTxt}>获取值</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
11.综合案例(评论)
class App extends React.Component{
state = {
comments:[
{id:1,name:'jake',content:'沙发!!'},
{id:2,name:'jake1',content:'沙发1!!'},
{id:3,name:'jake3',content:'沙发2!!'},
{id:4,name:'jake5',content:'沙发3!!'}
],
//评论人
userName:'',
//评论内容
userContent:''
}
renderList(){
return this.state.comments.length === 0?(
<div className="no-commit" >暂无评论,快去评论吧</div>
):(
<ul>
{this.state.comments.map(item => (
<li key={item.id}>
<h3>评论人 {item.name}</h3>
<p>评论内容 {item.content}</p>
</li>
))}
</ul>
)
}
//处理表单数据
handleForm = (e) =>{
const {name,value} = e.target
this.setState({
[name]:value
})
}
//发表评论
addcommit=()=>{
const {comments,userName,userContent} = this.state
if (userName.trim()==='' || userName.trim() === ''){
alert("请输入内容!")
return
}
console.log(userName,userContent)
const newComments = [{
id : Math.random(),
name: userName,
content: userContent
},
...comments
]
this.setState({
comments:newComments,
userName:'',
userContent:''
})
}
render(){
const {userName,userContent} = this.state
return(
<div className="app">
<div>
<input className='user' type='text' placeholder="请输入评论人" value={userName} name='userName' onChange={this.handleForm}/>
<br />
<textarea
className='content'
cols = '30'
rows = '10'
placeholder="请输入评论内容"
value={userContent}
name='userContent'
onChange={this.handleForm}
/>
<br />
<button onClick={this.addcommit}>发表评论</button>
</div>
{this.renderList()}
{/* {this.state.comments.length === 0?(
<div className="no-commit" >暂无评论</div>
):(
<ul>
{this.state.comments.map(item => (
<li key={item.id}>
<h3>评论人 {item.name}</h3>
<p>评论内容 {item.content}</p>
</li>
))}
</ul>
)} */}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
二、组件进阶
1. props
函数组件接收
// 2.接收数据
const Hello = props =>{
//props是一个对象
console.log(props)
return (
<div>
<h1>props:{props.name}</h1>
</div>
)
}
//1. 传递数据
ReactDOM.render(<Hello name="jack" age={19} />,document.getElementById('root'))
类组件接收
class Hello extends React.Component{
render(){
console.log(this.props)
return (
<div>
<h1>props: {this.props.age}</h1>
</div>
)
}
}
//1. 传递数据
ReactDOM.render(<Hello name="jack" age={19} />,document.getElementById('root'))
可以传任意类型数据
const Hello = props =>{
console.log('props',props)
props.fn()
return(
<div>
<h1>props:</h1>
{props.tag}
</div>
)
}
ReactDOM.render(
<Hello
name="rose"
age ={19}
colors={['red','green','blue']}
fn = {() =>console.log('这是一个函数')}
tag = {<h1>HHH111</h1>}
/>,
document.getElementById('root')
)
2.父组件–>子组件
//父组件
class Parent extends React.Component{
state={
lastName:"王"
}
render(){
return (
<div className="parent">
父组件:
<Child name={this.state.lastName}/>
</div>
)
}
}
//子组件
const Child=(props)=>{
console.log('子组件:',props);
return(
<div className="child">
<p>子组件,接收到父组件的数据:{props.name}</p>
</div>
)
}
ReactDOM.render(<Parent />,document.getElementById('root'))
3.子组件–>父组件
思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数。
//父组件
class Parent extends React.Component{
state={
parentMsg:''
}
//提供回调函数,用来接收数据
getChildMsg = data =>{
console.log("接收到子组件数据:",data)
this.setState({
parentMsg:data
})
}
render(){
return(
<div className="parent">
父组件:{this.state.parentMsg}
<Child getMsg={this.getChildMsg}/>
</div>
)
}
}
//子组件
class Child extends React.Component{
state={
msg:"play"
}
handleClick=()=>{
//子组件调用度组件传过来的回调函数
this.props.getMsg(this.state.msg)
}
render(){
return(
<div className="child">
子组件:<button onClick={this.handleClick}>点我 点我</button>
</div>
)
}
}
ReactDOM.render(<Parent />,document.getElementById('root'))
4.兄弟组件
class Counter extends React.Component{
//提供共享状态
state ={
count:0
}
//提供修改状态的方法
onIncrement = () =>{
this.setState({
count:this.state.count+1
})
}
render(){
return(
<div>
<Child1 count={this.state.count}/>
<Child2 onIncrement={this.onIncrement}/>
</div>
)
}
}
const Child1 = props =>{
return <h1>计数器:{props.count}</h1>
}
const Child2 = props=>{
return <button onClick={()=>props.onIncrement()}>+1</button>
}
ReactDOM.render(<Counter />,document.getElementById('root'))
5.context 跨组件传递
不用一层一层props
总结:
-
如果两个组件是远方亲戚(比如,嵌套多层)可以使用Context实现组件通讯
-
Context提供 了两个组件: Provider和Consumer
-
Provider组件 :用来提供数据
-
Consumer组件 :用来消费数据
// 创建context得到两个组件
const {Provider, Consumer} = React.createContext()
class App extends React.Component{
render(){
return (
<Provider value="pink">
<div className="app">
<Node />
</div>
</Provider>
)
}
}
const Node = props=>{
return (
<div className="node">
<SubNode />
</div>
)
}
const SubNode = props =>{
return (
<div className='subnode'>
<Child />
</div>
)
}
const Child = props =>{
return (
<div className='child'>
<Consumer>
{data =><span>我是子节点 -- {data}</span>}
</Consumer>
</div>
)
}
ReactDOM.render(<App />,document.getElementById('root'))
6.props(chrildren)
我是子节点以这样子的会默认产生一个children
children属性:表示组件标签的子节点。当组件标签有子节点时, props就会有该属性
children属性与普通的props-样,值可以是任意值(文本、React元素、 组件,甚至是函数)
const App =props=>{
console.log(props);
return (
<div>
<h1>组件标签子节点:</h1>
{props.children}
</div>
)
}
ReactDOM.render(<App>我是子节点</App> ,document.getElementById('root'))
7.props(校验)
使用步骤
1.安装包 prop-types ( yarn add prop-types / npm i props- types )
2.导入prop-types包
import PropTypes from 'prop-types'
const App =props=>{
const arr = props.colors
const lis = arr.map((item, index) =><li key={index}>{item}</li> )
return <ul>{lis}</ul>
}
//添加propos校验
App.propTypes = {
colors: PropTypes.array
}
ReactDOM.render(<App colors={19} />,document.getElementById('root'))
约束规则
- 常见类型: array、 bool、 func、 number. object. string
- React元素类型 : element
- 必填项: isRequired
- 特定结构的对象: shape({ })
8.props(默认值)
const App=props=>{
console.log(props);
return(
<div>
<h1>props默认值为:{props.pageSize}</h1>
</div>
)
}
App.defaultProps={
pageSize:10
}
ReactDOM.render(<App />,document.getElementById('root'))
9.生命周期
只有类组件才有生命周期。
class App extends React.Component {
constructor(props) {
super(props )
console.warn( '生命周期钩子函数: constructor' )
}
componentDidMount(){
console.warn( '生命周期钩子函数: componentDidMount' )
}
render() {
console.warn( '生命周期钩子函数: render' )
return (
<div>
<h1>统计豆豆被打的次数: </h1>
<button id="btn">打豆豆</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root' ))
10.props(获取鼠标位置)
组件复用
class Mouse extends React.Component{
state ={
x:0,
y:0
}
//监听鼠标移动的事件处理
handleMouseMove=e=>{
this.setState({
x:e.clientX,
y:e.clientY
})
}
//监听鼠标移动事件
componentDidMount(){
window.addEventListener('mousemove',this.handleMouseMove)
}
render(){
return this.props.render(this.state)
}
}
class App extends React.Component{
render(){
return (
<div>
<h1>render props 模式</h1>
<Mouse render={(mouse)=><p>鼠标位置,{mouse.x},{mouse.y}</p>} />
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
猫捉老鼠
import img from './images/logo192.png'
class Mouse extends React.Component{
state ={
x:0,
y:0
}
//监听鼠标移动的事件处理
handleMouseMove=e=>{
this.setState({
x:e.clientX,
y:e.clientY
})
}
//监听鼠标移动事件
componentDidMount(){
window.addEventListener('mousemove',this.handleMouseMove)
}
render(){
return this.props.render(this.state)
}
}
class App extends React.Component{
render(){
return (
<div>
<h1>render props 模式</h1>
<Mouse render={mouse=>{
return(
<p>
鼠标位置,{mouse.x},{mouse.y}
</p>)
}}
/>
{/**猫捉老鼠 */}
<Mouse render={mouse =>{
return (
<img src={img} alt="猫" style={{
position:'absolute',
top:mouse.y -96,
left:mouse.x -96
}}/>
)
}
}/>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
render props
chrildren实现render props
import img from './images/logo192.png'
import PropTypes from 'prop-types'
class Mouse extends React.Component{
state ={
x:0,
y:0
}
//监听鼠标移动的事件处理
handleMouseMove=e=>{
this.setState({
x:e.clientX,
y:e.clientY
})
}
//监听鼠标移动事件
componentDidMount(){
window.addEventListener('mousemove',this.handleMouseMove)
}
//卸载时解除事件绑定
componentWillUnmount(){
window.removeEventListener('mousemove',this.handleMouseMove)
}
render(){
return this.props.children(this.state)
}
}
Mouse.propTypes = {
children:PropTypes.func.isRequired
}
class App extends React.Component{
render(){
return (
<div>
<h1>render props 模式</h1>
{/* <Mouse render={mouse=>{
return(
<p>
鼠标位置,{mouse.x},{mouse.y}
</p>)
}}
/> */}
<Mouse>
{
mouse=>{
return (
<p>
鼠标位置,{mouse.x},{mouse.y}
</p>
)
}
}
</Mouse>
{/**猫捉老鼠 */}
{/* <Mouse render={mouse =>{
return (
<img src={img} alt="猫" style={{
position:'absolute',
top:mouse.y -96,
left:mouse.x -96
}}/>
)
}
}/> */}
<Mouse>
{
mouse =>{
return (
<img src={img} alt="猫" style={{
position:'absolute',
top:mouse.y -96,
left:mouse.x -96
}}/>
)
}
}
</Mouse>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
2.高阶组件
三、React原理
1.setState()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XJhaDEiB-1624269396777)(https://github.com/Lichee-cpu/images/raw/master/images/images20210621111745.png)]
2.JSX转化过程
3.组件更新
4.性能优化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PgdrZd6P-1624269396791)(https://github.com/Lichee-cpu/images/raw/master/images/images20210621133749.png)]
5.虚拟DOM和Diff算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i8hAZDoI-1624269396803)(https://github.com/Lichee-cpu/images/raw/master/images/images20210621134852.png)]
四、路由
1.基本使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VxmSf0B7-1624269396804)(https://github.com/Lichee-cpu/images/raw/master/images/images20210621170906.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z2FOLrPC-1624269396807)(https://github.com/Lichee-cpu/images/raw/master/images/images20210621170958.png)]
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router,Route,Link} from 'react-router-dom'
const First= ()=><p>页面一内容</p>
const App=()=>(
<Router>
<div>
<h1>React 路由基础</h1>
{/* 指定入口 */}
<Link to='/first'>页面一</Link>
{/* 指定出口 */}
<Route path='/first' component={First}></Route>
</div>
</Router>
)
ReactDOM.render(<App></App>,document.getElementById('root'))
2.常用组件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-brZAp5k5-1624269396812)(https://github.com/Lichee-cpu/images/raw/master/images/images20210621171350.png)]
3.编程式导航
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-guE1Ta9W-1624269396814)(https://github.com/Lichee-cpu/images/raw/master/images/images20210621173704.png)]
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router,Route,Link} from 'react-router-dom'
class Login extends React.Component{
handleLogin = () =>{
this.props.history.push('/home')
}
render(){
return (
<div>
<p>登录页面:</p>
<button onClick={this.handleLogin}>登录</button>
</div>
)
}
}
const Home =(props)=>{
const handleBack=()=>{
props.history.go(-1)
}
return (
<div>
<h2>这是后台主页</h2>
<button onClick={handleBack}> 返回登录页面</button>
</div>
)
}
const App = () =>(
<Router>
<div>
<h1>编程式导航:</h1>
<Link to='/login'>去登录</Link>
<Route path="/login" component={Login}></Route>
<Route path="/home" component={Home}></Route>
</div>
</Router>
)
ReactDOM.render(<App></App>,document.getElementById('root'))
4.默认路由
默认路由path为:/
<Route path="/" component={Home} />
5.匹配模式
模糊匹配
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0oDcMlG1-1624269396818)(https://github.com/Lichee-cpu/images/raw/master/images/images20210621174628.png)]
精确匹配
给Route组件添加exact属性,让其变为精确匹配模式
精确匹配:只有当path和pathname完全匹配时才会展示该路由
//此时,该组件只能匹配pathname="/"这一种情况
<Route exact path='/' component={Home}></Route>