【React】函数组件底层渲染机制
目录
- 函数组件
- 创建
- 调用&命名
- 组件传参
- 渲染机制
- 对属性props的处理
- 关于对象的规则设置
- 冻结
- 密封
- 扩展
函数组件
创建
创建一个函数,让函数返回JSX视图「或者JSX元素、virtualDOM虚拟DOM对象」,就是创建了一个“函数组件”。
const DemoOne = function DemoOne(props) {
return <div className="demo-box">
<h2 className="title">Hello,world</h2>
</div>;
};
调用&命名
基于ES6Module规范,导入创建的组件「可以忽略.jsx后缀名」,然后像写标签一样调用这个组件即可。组件的名字,一般都采用PascalCase「大驼峰命名法」这种方式命名。
<Component/> 单闭合调用
<Component> ... </Component> 双闭合调用
import DemoOne from './views/DemoOne';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<DemoOne />
)
组件传参
调用组件的时候,我们可以给调用的组件设置(传递)各种各样的属性
<DemoOne title="我是标题" x={10} data={[100, 200]} className="box" style={{ fontSize: '20px' }} />
- 如果设置的属性值不是字符串格式,需要基于
{}
进行嵌套 - 调用组件的时候,我们可以把一些数据/信息基于属性props的方式,传递给组件
打印子组件接收到的参数:
const DemoOne = function DemoOne(props) {
console.log(props,'props')
const {title,x,data,style,className} = props;
return <div className="demo-box">
<h2 className="title">Hello,world</h2>
</div>;
};
渲染机制
1、基于babel-preset-react-app
把调用的组件转换成createElement
格式
<DemoOne title="我是标题" x={10} data={[100, 200]} className="box" style={{ fontSize: '20px' }} />
上面JSX语法经过babel-preset-react-app
编译为如下:
import { jsx as _jsx } from "react/jsx-runtime";
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
容器
对属性props的处理
调用组件,传进来的props
是可读的,不可修改,可以通过Object.isFrozen(props)
进行判断:
const DemoOne = function DemoOne(props) {
console.log(Object.isFrozen(props)) //结果为true,说明props传进来被冻结了;
const {title,x,data,style,className} = props;
return <div className="demo-box">
<h2 className="title">Hello,world</h2>
</div>;
如果修改props
中的值就会报错,如下:
const DemoOne = function DemoOne(props) {
props.title = "你好";
return <div className="demo-box">
<h2 className="title">{title}</h2>
</div>;
};
虽然对于传递进来的属性,我们不能直接修改,但是可以做一些规则校验
- 设置默认值
DemoOne.defaultProps = {
x: 0
};
- 设置参数类型,需要用到
prop-types
插件,https://github.com/facebook/prop-types
,传递进来的属性,首先会经历规则的校验,不管校验成功还是失败,最后都会把属性给形参props,只不过如果不符合设定的规则,控制台会抛出警告错误(不影响属性值的获取
)
import PropTypes from 'prop-types';
函数组件.propTypes = {
// 类型是字符串、必传
title: PropTypes.string.isRequired,
// 类型是数字
x: PropTypes.number,
// 多种校验规则中的一个
y: PropTypes.oneOfType([
PropTypes.number,
PropTypes.bool,
])
};
如果想把传递的属性值进行修改,可以:
- 把props中的某个属性赋值给其他内容「例如:变量、状态…」
- 我们不直接操作
props.xxx=xxx
,但是我们可以修改变量/状态值
const DemoOne = function DemoOne(props) {
console.log(props, 'props')
let { title, x, data, style, className } = props;
return <div className="demo-box">
<h2 className="title">{title}</h2>
</div>;
};
关于对象的规则设置
冻结
- 冻结对象:
Object.freeze(obj)
- 检测是否被冻结:
Object.isFrozen(obj) =>true/false
- 被冻结的对象:不能修改成员值、不能新增成员、不能删除现有成员、不能给成员做劫持「
Object.defineProperty
」
密封
- 密封对象:Object.seal(obj)
- 检测是否被密封:Object.isSealed(obj)
- 被密封的对象:可以修改成员的值,但也不能删、不能新增、不能劫持!!
扩展
- 把对象设置为不可扩展:Object.preventExtensions(obj)
- 检测是否可扩展:Object.isExtensible(obj)
- 被设置不可扩展的对象:除了不能新增成员、其余的操作都可以处理!!
- 被冻结的对象,即是不可扩展的,也是密封的!!同理,被密封的对象,也是不可扩展的!!