上篇:【react】react18的学习(一)-基本配置
本篇内容:函数组件、插槽实现、类组件
六、组件
1、函数组件(静态组件,没有this)
命名:大驼峰;
传props值:同普通标签,除字符串,用{}
形式传递;
接收props值:
渲染:
- 同样,跟元素一起,先由
babel-preset-react-app
将标签转为React.createElement()
; - 机制:函数组件标签为函数类型,与普通标签不同,
React.createElement()
处理函数标签,先执行将props传入函数,然后执行函数,生成虚拟DOM; - 再将虚拟DOM传给
root.render()
,渲染成真实DOM;
props 介绍
+ 在组件被解析成虚拟DOM后,标签上的属性和子标签就被放在虚拟DOM对象的props里了;
+ props对象被冻结,只读;
+ 用于父子传值;
+ 校验功能:import PropTypes from 'prop-types'
+ 插槽功能: let { children } = props
// 插槽
import React from 'react';
let { children } = props
// 默认children可能为数组[](传多标签)、对象{}(传单标签)、字符串""(传值)
// 使用React.Children,不会改变props中的children
children = React.Children.toArray(children)
console.log(children);//转为数组
// 具名插槽如下图
// views/Demo.jsx
// package.json中配置自动引入
// import React from 'react';
import PropTypes from 'prop-types'
// props 初始值
Demo.defaultProps = {
age: 10
}
// props 校验
Demo.propTypes = {
title: PropTypes.string.isRequired,
age: PropTypes.number
}
export default function Demo(props) {
console.log(props);
let { title, age, style, children } = props
age = 19
return (
<div style={style}>
年龄:{age},标题:{title}
{children}
</div>
);
}
---------------------------------------------------------------------------------
// 入口文件:index.jsx
import Demo from './views/Demo'
root.render(
<Demo title={'aa'} age={18} style={{ color: 'red' }}>
<span>123</span>
<span>456</span>
</Demo>
)
// babel解析
React.createElement(
Demo,
{
age: 10,
style: {
color: "red"
}
}
);
// React.createElement()执行,生成虚拟DOM
{
$$typeof:Symbol(react.element)
key:null,
ref:null,
type: f Demo(),
props:{
age:10;
style:{
color:"red"
}
},
...
}
2、类组件(动态组件,有生命周期等,当作构造函数,new创建类实例执行)
extends继承
1、通过React.Component.call(this)
,给实例赋私有属性:props、refs、context、updater
2、将类的原型对象的原型链指向:Parent.prototype.__proto__ == Reaact.Component.prototype
,这样实例可以沿着原型链访问;
首次渲染过程:
getDefaultProps
:初始化属性、进行属性校验、执行rendergetInitialState
:初始化状态 state,在实例上挂载,默认null(没有双向绑定,没有响应式set,状态更改不会自动更新视图,需要使用官方组件原型上的方法:this.setState()、this.state + this.forceUpdate()
)- 生命周期1:
UNSAFE_componentWillMount
- 生命周期2:
render函数
,触发子组件渲染,返回虚拟Dom,并开始渲染(不是root.render()方法,这个方法没有返回值,是入口文件用于触发组件) - 生命周期3:
componentDidMount
,可以获取真实Dom
组件更新
方式一:组件state触发更新
- 生命周期4:
shouldComponentUpdate(nextProps, nextState, nextContext)
,通过强制更新组件直接跳过该周期函数; - 生命周期5:
UNSAFE_componentWillUpdate(nextProps, nextState, nextContext)
- 生命周期2:
render
,就是最初写的,触发子组件更新渲染 - 生命周期6:
componentDidUpdate(preProps, preState, snapshot)
方式二:父组件重新渲染,从而更新子组件
- 生命周期7:
UNSAFE_componentWillReceiveProps(nextProps, nextContext)
最后一个生命周期8: componentWillUnmount
:父组件销毁,先销毁子组件;
生命周期
类组件的首次渲染
getDefaultProps:初始化属性
getInitialState:初始化状态
UNSAFE_componentWillMount:挂载前
render:渲染生成虚拟DOM
componentDidMount:挂载后,可获取真实DOM
// 类组件的更新
shouldComponentUpdate:是否更新
UNSAFE_componentWillUpdate:更新前
render:渲染生成虚拟DOM
componentDidUpdate:更新后
UNSAFE_componentWillReceiveProps:父组件触发子组件更新
componentWillUnmount:销毁前
PureComponent与Component
shouldComponentUpdate:是否更新
- Pure:会在这个周期函数时对props和state实现了一个浅层次的比较(对象中子项是否都一样),如果有任何props或state发生了更改,则返回true;
- 后者默认返回true;
- 浅比较
类组件的 refs
受控组件:通过修改状态更新视图
非受控组件:通过获取真实DOM更新视图;
原生元素真实DOM:三种方式
可在
生命周期 componentDidMount(){}、componentDidUpdate(preProps,preState)()
- ref 为字符串(官方不推荐)
<div ref='vote'>
----
this.refs.[ref名]
- ref 为函数(推荐)
<div ref={x => this.box = x}>
---
this.box
- ref 为
Ref对象
(推荐)
// 生成Ref对象
box = React.createRef()
// 类原型上的方法(类原型上加属性只能用prototype)
render() {
return (
<div ref={this.box}>
----
this.box.current
组件标签上的 ref:
// 类组件:获取实例
<Demo1 ref={x => this.child1 = x} />
// 函数组件:获取某个元素
<Demo2 ref={x => this.child2 = x} />
----
const Demo2 = React.forwardRef((props, ref) => {
return (
<div ref={ref}></div>
);
})
3、Hooks组件(融合两种组件优势)
函数组件:渲染块,机制简单,静态组件;
类组件:功能强大,渲染慢,动态组件;
hooks组件:对函数组件的改造;