1. 创建组件
- 在创建组件的时候,组件的名称必须是大写,在组件引入使用的时候,也是需要大写,【 注:在react中,书写HTML的时候,必须是小写,虽然HTML大小写不区分,但是在react中,大写JSX会认为这个是一个组件的引入】,所以在需要书写HTML的时候,保持小写
- 占位符,在编写【React】代码的时候,在【vue】里面,是使用的是【template】,在react中使用【Fragment】进行占位,【占位符,在真实的渲染时,是不会被渲染成真是的DOM】,react中的Fragment也可以是被代替的,【<></>】进行占位。
import React, { Fragment } 'react'
使用的时候,对组件进行包裹即可,无需书写最外层的div
1.1 函数组件
【 1. 创建函数组件 】
import React from 'react'
function App() {
return (
<div></div>
)
}
export default App
【 2. 组件中使用 】
import App from './App.js'
1.1.1 函数组件之间的数据传递(父传子)
- 父组件通过【属性=值】的方式向子组件传递数据
- 子组件通过【props】接收父组件传递过来的数据
【 1. 单个数据的传递 】
import React from 'react'
function ChildenS(props) { 【 子组件通过props接收参数,也可以在接收父组件参数的时候进行解构{name} 】
const { name } = props 【 或者接收porps,在重新进行解构 】
return (
<div>{name}</div>
)
}
function Father() {
return (
<ChildenS name="给子组件传递数据"></ChildenS>
)
}
【 2. 多个参数的传递,在开发的过程中,可能不仅仅传递一个数据,是多个参数,如何进行传递 】
const obj = {
name: '向子组件传递数据',
data: [
{
id: 1,
name: '向子组件传递数据',
title: 'react'
}
]
}
function Father() {
return (
<ChildenS {...obj}></ChildenS> 【 通过解构语法,将对象传递过去,子组件通过props接收即可 】
)
}
【 3. 如果父组件不传递数据,但是子组件需要使用,可以设置默认值 】
ChildenS.defauleProps = { * 函数组件之间参数传递,不传参数设置默认值
name: '你好',
age: 18
}
1.1.2 函数组件之间的参数传递(子传父,与类组件相同)
1.2 类组件
【 1. 创建类组件 】
import React,{Component} from 'react'
class App extends Component {
render() {
return (
<div></div>
)
}
}
export default App
【 2. 组件中使用 】
import App from './App.js'
1.2.1 类组件的生命周期
- 组件挂载成功后【componentDidMount】
- 依赖于DOM的操作可以在这个声明收起函数进行
- 在挂载完成后,发送网络请求
- 组件更新【componentDidUpdate】
- 会在组件更新后,立即被调用,首次渲染不进行调用
- 当组件更新后,可以去操作DOM
- 组件卸载【 componentWillUnmount 】
- 当组件销毁的之前直接调用
- 在调用方法的时候,必要的操作是【清理操作】
1.2.2 类组件之间数据传递(父传子)
- 父组件通过【属性=值】的方式向子组件传递数据
- 子组件通过【props】接收父组件传递过来的数据
【 1. 单个数据的传递 】
import React,{Component} from 'react'
export default class Father extends Component{
render() {
return (
<div>
<ChildenS name="给子组件传递数据" ></ChildenS> 【 调用子组件 】
</div>
)
}
}
export default class ChildenS extends Component{
render() {
const { name } = this.props; 【子组件通过this.props 接收父类传递过来的数据】
return (
<div>{name}</div>
)
}
}
【 2. 多个参数的传递,在开发的过程中,可能不仅仅传递一个数据,是多个参数,如何进行传递 】
export default class Father extends Component{
render() {
const obj = {
name: '向子组件传递数据',
data: [
{
id: 1,
name: '向子组件传递数据',
title: 'react'
}
]
}
return (
<div>
<ChildenS {...obj}></ChildenS> 【 通过解构语法,将对象传递过去,子组件通过props接收即可 】
</div>
)
}
}
【 3. 父组件没有给子组件传递参数,但是子组件需要使用值,子组件设置默认值 】
export default class ChildenS extends Component{
static defaultProps = { 【使用静态属性进行操作】
name: '设置默认数据'
}
render() {
const { name } = this.props 【需要进行解构,才可以正常使用】
return (
<div>{name}</div>
)
}
}
- 在类组件中,没有【.defauleProps】 的操作,但是类组件中有静态属性的方式,defauleProps
1.2.3 类组件之间参数传递(子传父亲)
- 在React中同样是通过props传递消息,只是让父组件给子组件传递一个回调函数,在子组件中调用这个函数即可;
【 1. 类组件事件传递 】
import React,{Component} from 'react'
export default class Father extends Component{
render() {
return (
<div>
【 属性=值的方式将事件传递给子组件,只不过属性后面传递的是事件 】
<ChildenS handole={this.handole.bind(this)}></ChildenS> 【 调用子组件 】
</div>
)
}
handole (父组件接收到数据) {
【 处理子组件传递过来的数据 】
}
}
export default class ChildenS extends Component{
render() {
const { handole } = this.props;
return (
<div>
<button onClick={() =>handole(给父组件传递数据) }>触发事件</button>
</div>
)
}
}
1.3 PropTypes参数验证(大型推荐使用TypeScript)
- 如果你项目中默认继承了Flow或者TypeScript,那么直接就可以进行类型验证
- 如果项目中没有集成Flow或者TypeScript,可以通过【PropTypes】对参数进行类型显示
【 1. 下载PropTypes npm i prop-types -D】
【 2. 在组件中使用,子组件中使用,设置参数接收的类型 】
import PropTypes from 'prop-types'
import React,{Component} from 'react'
export default class ChildenS extends Component{
render() {
return (
<div></div>
)
}
}
【 2.1 指定接收的数据类型是什么,类型校验(函数组件类组件通用) 】
ChildenS.propTypes = {
num: PropTypes.number, 【指定传递参数的类型】
name: PropTypes.array.isRequired 【指定传参的类型,并且是必须传递的参数】
}
【 2.2 类组件的第二种写法 】
export default class ChildenS extends Component{
static propTypes = {
num: PropTypes.number
}
render() {
return (
const { name } = this.props
<div>{name}</div>
)
}
}
1.4 组件之间传递JSX
- 【针对类组件】,在传递【JSX】的时候,是需要在props里面找到【children】属性里面
import React,{Component} from 'react'
export default class Father extends Component{
render() {
return (
<div>
<ChildenS>
<p>向子组件传递JSX</p> 【向子组件传递JSX】
</ChildenS>
</div>
)
}
}
export default class ChildenS extends Component{
render() {
return {
<div>
{ this.porps.children } 【 拿到父组件传递过来的jsx 】
</div>
}
}
}
- 【函数组件】,直接通过props的【childre】进行接收
- 无论传递多少【JSX】语法,都会被渲染
2. 在React中实现slot(插槽)
- 在react当中,没有【slot】的这个说法,只有在【vue里,才会有slot】,那么,要想在react当中实现Vue里面的插槽功能,该如何实现
- 在react中,可以通过向子组件传递【JSX】的方式,向子组进行传递
【 1. 父组件 】
import React from 'react'
import Home from './Home'
function App() {
const leftSlot = (<div>
<span>我是左边内容</span>
</div>)
const centerSlot = (<div>
<span>我是中间内容</span>
</div>)
const rightSlot = (<div>
<span>我是右边内容</span>
</div>)
return (
<div>
<Home leftSlot={leftSlot} centerSlot={centerSlot} rightSlot={rightSlot} />
<button>父组件按钮</button>
</div>
)
}
export default App
【 子组件,通过props,拿到父组件传递的JSX 】
function Home(props) {
console.log(props);
return (
<div>
<div>{props.leftSlot}</div>
<div>{props.centerSlot}</div>
<div>{props.rightSlot}</div>
</div>
)
}
export default Home
3. 跨组件通信
3.1 多层嵌套组件参数传递
3.2 兄弟组件之间的参数传递
- 兄弟组件之间参数传递,兄弟组件,都有一个父级组件,组件A向组件B传递参数,组件A通过将参数传递给父组件,父组件在通过【props】传递给【组件B】
【 父组件 】
import React,{Component} from 'react'
import Home from './Home'
import Header from './Header'
class App extends Component {
state = {
name: ''
}
getParams(i) {
const name = i.name
this.setState({
name
})
}
render() {
return (
<div>
<Header handole={this.getParams.bind(this)}></Header>
<Home name={this.state.name}/>
</div>
)
}
}
export default App
【 组件A 】
import React,{Component} from 'react'
class Header extends Component {
constructor(props) {
super(props);
this.state = {}
}
componentDidMount () {
this.props.handole({name: '组件A中的数据'})
}
render() {
return (
<div>组件A</div>
)
}
}
export default Header
【 组件B 】
function Home(props) {
return (
<div>
组件B:
<span>{props.name}</span>
</div>
)
}
export default Home
- 【通过发布/订阅进行传递】events
【 1. 下载包 npm install events -S 】
【 2. 引入使用 】
import {EventEmitter} from 'events';
const emitter = new EventEmitter();
【 发布 】
emitter.emit('事件名称',传递的参数)
【 侦听 】
const emitter = new EventEmitter();
emitter.on('事件名',(接收的参数) => {
逻辑代码
})
【 2. 用法2 】
【 eventBus.js 】
import {EventEmitter} from 'events';
export default new EventEmitter();
【 发布 】
import Bus from './eventBus'
Bus.emit('changeSiblingsData', msg);
【 侦听 】
import Bus from './eventBus'
Bus.addListener('changeSiblingsData', (msg) => {
this.setState({
bus: msg,
});
console.log(msg);
});
4. 组件状态(类)
- 【什么是组件的状态】组件的状态是什么,就是某一个组件自己内部私有的数据,也就是状态。 针对单个组件自己内部的数据,当修改组件内部的数据,界面上Dom中的数据就会自动更新
4.1 函数组件与类组件的区别
【对比:】
- 方式一:通过 function构造函数 创建组件。内部没有 state 私有数据,只有 一个 props 来接收外界传递过来的数据。
- 方式二:通过 class 创建子组件。内部除了有 this.props 这个只读属性之外,还有一个专门用于 存放自己私有数据的 this.state 属性,这个 state 是可读可写的。
基于上面的区别,我们可以为这两种创建组件的方式下定义: 使用 function 创建的组件,叫做【无状态组件】;使用 class 创建的组件,叫做【有状态组件】。
- 【本质区别】有状态组件和无状态组件,最本质的区别,就是有无 state 属性。同时, class 创建的组件,有自己的生命周期函数,但是,function 创建的 组件,没有自己的生命周期函数。
- 【什么时候去使用有状态组件,什么时候用无状态组件】
- 如果一个组件需要存放自己的私有数据,或者需要在组件的不同阶段执行不同的业务逻辑,此时,非常适合用 class 创建出来的有状态组件。
- 如果一个组件,只需要根据外界传递过来的 props,渲染固定的页面结构即可的话,此时,非常适合使用 function 创建出来的无状态组件。(使用无状态组件的小小好处: 由于剔除了组件的生命周期,所以,运行速度会相对快一点点)。
4.2 state
- 类组件中,组件私有的状态,在开发时,常常会看见在【constructor】里面定义state,与在外面定义的state,在这里,无论是在外面定义的还是在【构造器】里面定义的state,都是挂载到实例上的,【state】值是一个对象。
import React,{Component} from 'react'
export default class App extends Componet {
state = {
name: '组件私有的数据'
}
render () {
return {
<div>{ this.state.name }</div>
}
}
}
export default class App extends Componet {
constructor () {
this.state = {
name: '组件私有的数据'
}
}
render () {
return {
<div>{ this.state.name }</div>
}
}
}
4.3 更改组件的状态setState
- 【setState】是react提供的一个修改组件状态的方法,通过setState去修改state里面的值
import React,{Component} from 'react'
【 修改后的值,会覆盖原来的值,数据就会被更新到界面Dom元素中。 】
export default class App extends Componet {
state = {
name: '组件私有的数据'
}
render () {
return {
<div>
{ this.state.name }
<button onClick={this.handole}>修改数据</button>
</div>
}
}
handole = () => {
this.setState({
name: '修改state里面的值'
})
}
}
- 【setState】方法的设计,是一个异步的,操作完是不能立刻拿到修改后的值,在使用【setState】的时候,提供两种方式,去获取修改后的值
- 【方式一:使用async/await】
- 【方式二:使用setState的第二个参数,传入一个函数,通过这个回调函数,拿到更新后的值】,第二个参数类似于promise里面的成功回调函数。
import React,{Component} from 'react'
export default class App extends Componet {
state = {
name: '组件私有的数据'
}
render () {
return {
<div>
{ this.state.name }
<button onClick={this.handole}>修改数据</button>
</div>
}
}
handole = async () => { 【 方式一: async/await 】
await this.setState({
name: '修改state里面的值'
})
console.log(this.state.name) // 拿到修改后的值,在做其他的操作
}
handole =() => {
this.setState({
name:'修改state里面的值'
},() =>{
console.log(this.state.name) 【 方式二: setState的第二个参数传入函数,拿到state更改后的值 】
})
}
}
- 在使用【setState】,除了参数可以传递一个【对象】,也可以传递一个【函数】,函数接收参数,【state】当前组件的状态。
import React,{Component} from 'react'
export default class App extends Componet {
state = {
count: 0
}
render() {
return (
<div>
{ this.state.name }
<button onClick={ this.handole }>+</button>
</div>
)
}
handole =()=>{
this.setState((state) =>({ 【 state拿到的是当前组件的私有状态 】
count: state.count + 1
}))
}
}
5. render函数被调用
- 当App组件自己内部的状态进行更改【setState去修改状态】,App组件内部,调用的所有的子组件,子组件里面的render都被调用了,【原因:当更改App组件的状态,App组件内部的render函数,就会被重新调用,当重新调用render函数的时候,发现组件内部有其他的组件调用,就会被重新加载】
- 【解决方式:类组件不直接去继承Component】,通过去继承【 PureComponent 】,可以解决组件的render函数被重新调用,【弊端, PureComponent 只能实现类组件的render函数不被调用】,但是函数组件还是无法设置
【 PureComponent 】会给组件自动生成一个shouldComponentUpdate函数,这个函数可以阻止组件的render函数
重新调用,返回值是布尔值,当返回值为false的时候,会阻止组件的render函数被重新调用
import React,{PureComponent} from 'react'
export default class App extends PureComponent {
render() {
return (
<div></div>
)
}
}
- 函数组件的优化【高阶组件memo】
【 memo作用:阻止函数组件无缘无故的被重新的调用 】
【 momo的使用 】
import React,{memo} from 'react'
【 memo会返回一个新的函数 】
const MemoHeader = memo(function Header() {
retrun (
<div></div>
)
})
export default MemoHeader