函数组件概念
创建一个函数并返回jsx元素或者虚拟DOM对象
函数组件是静态组件,特性:
- 不具备状态、生命周期函数、ref等内容
- 第一次渲染完毕,除非父组件控制其重新渲染,否则内容不会再更新
- 优势:渲染速度快
- 弊端:静态组件,无法实现组件动态更新
组件创建
// 函数表达式
const DemoOne = function DemoOne(props) {
return <div>DemoOne</div>
}
export default DemoOne;
// 或者
export default function DemoOne(props) {
return <div>DemoOne</div>
}
组件调用
基于ES6Module规范,导入创建的组件「可以忽略.jsx后缀名」,然后像写标签一样调用这个组件即可
// 引入
import DemoOne from '@/views/DemoOne';
// 调用
<Component/> 单闭合调用
<Component> ... </Component> 双闭合调用
调用组件的时候,我们可以给调用的组件设置(传递)各种各样的属性
<DemoOne title="标题" x={10} data={[100, 200]} className="box" style={{ fontSize: '20px' }} />
如果设置的属性值不是字符串格式,需要基于“{}胡子语法”进行嵌套 调用组件的时候,我们可以把一些数据/信息基于属性props的方式,传递给组件!!
渲染机制
以下面这段代码编译过程为例:
// JSX
<DemoOne title="标题" x={10} data={[100, 200]} className="box" style={{ fontSize: '20px' }} />
// React.createElement()
React.createElement(Demo, {
title: "\u6807\u9898",
x: 10,
data: [100, 200],
className: "box",
style: {
fontSize: '20px'
}
})
// virtualDom 格式
{
$$typeof: Symbol(react.element),
key: null,
props: {title: '标题', x: 10, data: Array(2), className: 'box', style: {…}},
ref: null,
type: ƒ Demo(props),
_owner: FiberNode {tag: 0, key: null, stateNode: null, elementType: ƒ, type: ƒ, …},
_store: {validated: false}
}
过程: @1 基于babel-preset-react-app把调用的组件转换为createElement格式
React.createElement(DemoOne, {
title: "\u6211\u662F\u6807\u9898",
x: 10,
data: [100, 200],
className: "box",
style: {
fontSize: '20px'
}
})
@2 把createElement方法执行,创建出一个virtualDOM对象!!
{
$$typeof: Symbol(react.element),
key: null,
props: {title: '我是标题', x: 10, data: 数组, className: 'box', style: {fontSize: '20px'}}, //如果有子节点「双闭合调用」,则也包含children!!
ref: null,
type: DemoOne
}
@3 基于root.render把virtualDOM变为真实的DOM
type值不再是一个字符串,而是一个函数了,此时:
- 把函数执行 -> DemoOne()
- 把virtualDOM中的props,作为实参传递给函数 -> DemoOne(props)
- 接收函数执行的返回结果「也就是当前组件的virtualDOM对象」
- 最后基于render把组件返回的虚拟DOM变为真实DOM,插入到#root容器中!!
函数组件最后返回的结果是 virtualDOM
组件插槽
React组件了解及使用_react 容器组件内部也有创建创建组件
这个就是一个最简单的函数组件
import React from 'react'
export default function Demo(props) {
console.log("props", props)
return (
<div>
Demo
{props.children}
</div>
)
}
组件调用:
import Demo from './views/Demo';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<div>
<Demo title="标题" x={10} data={[100, 200]} className="box" style={{ fontSize: '20px' }} >
123
<span>4444</span>
</Demo>
</div>
);
给组件传值:
{/* 传递属性: title 、x、data... */}
<Demo title="标题" x={10} data={[100, 200]} className="box" style={{ fontSize: '20px' }} >
{/* 插槽传值 */}
123
<span>4444</span>
</Demo>
运行结果:
这里没有添加传递的属性值渲染,渲染内插槽内传递过来的内容
但是可以看到组件内props打印出来的值如下:
内部相关的属性,都可以通过 props.xxx获取到
插槽:父组件内传递过来的内容都在props.children内可以获取到,然后对其进行渲染。
特性(静态组件)
主要用在在数据加载后,只渲染一次,无数据变动的组件,也称为傻瓜组件,无数据实时更新的组件即静态组件。
在第一次渲染完就不再变化的,可以使用函数组件
第一次渲染组件,把函数执行
- 产生一个私有的上下文:EC(V)
- 把解析出来的props「含children」传递进来「但是被冻结了」
- 对函数返回的JSX元素「virtualDOM」进行渲染
当我们点击按钮的时候,会把绑定的小函数执行:
- 修改上级上下文EC(V)中的变量
- 私有变量值发生了改变
- 但是“视图不会更新”
=>也就是,函数组件第一次渲染完毕后,组件中的内容,不会根据组件内的某些操作,再进行更新,所以称它为静态组件
=>除非在父组件中,重新调用这个函数组件「可以传递不同的属性信息」
真实项目中,有这样的需求:第一次渲染就不会再变化的,可以使用函数组件!!
但是大部分需求,都需要在第一次渲染完毕后,基于组件内部的某些操作,让组件可以更新,以此呈现出不同的效果!!==> 动态组件「方法:类组件、Hooks组件(在函数组件中,使用Hooks函数)」
例子:
import React from 'react'
export default function Vote(props) {
const { title } = props
let num = 1
return (
<div>
<h3>{title}</h3>
<p>{num}</p>
<button onClick={() => {
num++
console.log("num", num)
}}>click</button>
</div>
)
}
这里打印的num变化,但是页面不会加载变化的数据
函数组件props
官网。GitHub - facebook/prop-types: Runtime type checking for React props and similar objects
props是组件在使用后,获取到传递到属性及插槽内等相关的内容,上面的组件调用内有传递的例子,可以看到传值及使用等操作 简单用法在调用内可以看到
props传递进来的属性是’只读‘的 【原理:props被冻结了】
看下方样例代码:
可以看到,props是被冻结的状态
Object.isFrozen(props) -> true
获取:props.xxx
修改:props.xxx=xxx =>报错
组件传值的作用:父组件调用子组件的时候,可以基于属性,把不同的信息传递给子组件;子组件接收相应的属性值,呈现出不同的效果,让组件的复用性更强!!
props校验
对于传递进来的属性,我们不能直接修改,但是可以做一些规则校验 设置默认值:
函数组件.defaultProps = {
x: 0,
......
};
设置默认值生效只是存在于该值未传值,当有传值的情况,则defaultProps 不生效!
设置其它规则,例如:数据值格式、是否必传... 「依赖于官方的一个插件:prop-types」
import PropTypes from 'prop-types';
函数组件.propTypes = {
// 类型是字符串、必传
title: PropTypes.string.isRequired,
// 类型是数字
x: PropTypes.number,
// 多种校验规则中的一个
y: PropTypes.oneOfType([
PropTypes.number,
PropTypes.bool,
])
};
传递进来的属性,首先会经历规则的校验,不管校验成功还是失败,最后都会把属性给形参props,只不过如果不符合设定的规则,控制台会抛出警告错误{不影响属性值的获取}!!
修改props内传递的值:
- 把props中的某个属性赋值给其他内容「例如:变量、状态...」
- 我们不直接操作props.xxx=xxx,但是我们可以修改变量/状态值!!
例子:
运行结果: