jsx底层渲染机制!!
1.第一大步创建virtualDom
首先把我们编写的JSX语法,编译为虚拟DOM对象「virtualDOM」,这一步也分为两小步
虚拟DON对象∶框架自己内部构建的一套对象体系(对象的相关成员都是React内部规定的),基于这些属性描述出,我们所构建视图中的,DOM节点的相关特征!!
第一小步基于babel-preset-react-app 把JSX编译为React.createElement(…)这种格式!!
只要是元素节点,必然会基于createElement进行处理!
React.createElement(ele,props , . …children)
ele:元素标签名「或组件」
props:元素的属性集合(对象)「如果没有设置过任何的属性,则此值是null
children:第三个及以后的参数,都是当前元素的子节点
第二小步再把createElement方法执行,创建出virtua1DCN虚拟DOM对象「也有称之为:JSX元素、JSX对象、ReactChild对象…」!
virtualDOM= {
$ $typeof : Symbol( react.element),
ref: null,
key: null,
type:标签名「或组件」,
//存储了元素的相关属性&&子节点信息
props: {
元素的相关属性,
children:子节点信息「没有子节点则没有这个属性、属性值可能是一个值、也可能是一个数组」
}
}
因为胡子语法({})无法直接创建对象,下面这样做是错误的:
所以我们可以通过React.createElement()来创建元素
总结:胡子语法中,不能之前嵌入除数组外的其他对象,但是有一个对象是可以直接嵌入的:JSX元素对象「虚拟DOM对象」
问题:胡子语法中不能直接渲染对象对不对??
不对,1.数组对象可以渲染,2.style的行内样式属性可以渲染,3.可以直接通过createElement来渲染!!
第二大步:渲染真实dom
真实DOM:浏览器页面中,最后渲染出来,让用户看见的DOM元素! !
v16
ReactDOM. render(
<>...</>,
document.getELementById( 'root')
);
v18
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>...</>
);
render方法实现!!!
首先封装一个对象迭代的方法,基于传统的for/in循环,会存在一些弊端 1.性能较差(既可以迭代私有的,也可以迭代公有的;2.只能迭代“可枚举、非Symbol类型的“属
性…
解决思路,获取对象的所有私有属性(无论是否枚举,无论是否可以枚举)
let keys = Object.getOwnPropertyNames(arr).concat(Object.getOwnPropertySymbols(arr));
console. log(keys);
或者使用es6的:
Reflect.ownKeys(arr);
封装的方法:each
说说往标签上加值:
export function render(virtualDOM, container) {
let { type, props } = virtualDOM;
if (typeof type === "string") {
//存储的是标签名:动态创建这样一个标签
let ele = document.createElement(type);
//为标签设置相关的属性&子节点
each(props, (value, key) => {
// className的处理: value存储的是样式类名
if(key === 'className') {
ele.className = value;
return;
}
// style的处理: va lue存储的是样式对象
if (key === 'style') {
each(value, (val, attr) => {
ele.style[attr] = val;
});
return;
}
if (key === 'children') {
let children =value;
if(children.length===1) children =[children];
children.forEach(child => {
//子节点是文本节点:直接插入即可
if(typeof child === "string") {
ele.appendChild(document.createTextNode(child));
return;
}
//子节点又是一个virtuaLDOM: 递归处理
render(child, ele);
});
return;
}
ele.setAttribute(key, value);
});
container.appendChild(ele);
}
};
//子节点的处理: value存储的children属性值
这段代码的作用是将一个虚拟 DOM 渲染到真实的 DOM 中。它首先会根据虚拟 DOM 中的 type 属性创建一个对应的 HTML 元素,然后根据虚拟 DOM 中的 props 属性设置元素的属性和子元素,最后将该元素插入到指定的 container 容器中。该代码也只考虑了样式类名和样式对象,而没有考虑其他可能出现的属性,例如 ID 属性、事件属性等。
补充说明:第1次渲染页面是直接从virtua LDOM- ->真实DOM;但是后期视图更新的时候,需要经过1个DOM-DIFF的对比,计算出补丁包PATCH(两次视图差异的部分),把PATCH补丁包进行渲染! !
函数组件的底层渲染机制!!
一.函数组件
1.创建:在SRC目录中,创建一个 xxx.jsx 的文件,就是要创建一个组件;我们在此文件中,创建一个函数,让函数返回JSX视图「或者JSX元素、virtualDOM虚拟DOM对象」;这就是创建了一个“函数组件”!!
2.调用:基于ES6Module规范,导入创建的组件「可以忽略.jsx后缀名」,然后像写标签一样调用这个组件即可!!
<Component/>
单闭合调用
<Component> ... </Component>
双闭合调用
命名:组件的名字,我们一般都采用PascalCase「大驼峰命名法」这种方式命名
二.调用组件的时候,我们可以给调用的组件设置(传递)各种各样的属性
<DemoOne title="我是标题" x={10} data={[100, 200]} className="box" style={{ fontSize: '20px' }} />
1.如果设置的属性值不是字符串格式,需要基于“{}胡子语法”进行嵌套
2.调用组件的时候,我们可以把一些数据/信息基于属性props的方式,传递给组件!!
这里是组件里面的代码:
这里是我们入口文件的代码:
那么这里的span是什么????
在第一张图的那个props里有一个children属性能够接收到这些东西!!!!!
单闭合调用和多闭合调用区别?
多闭合调用可以传入其他标签!!!!!!!!!单闭合不可以
渲染的机制:
渲染机制
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值不再是一个字符串,而是一个函数了,此时:
1.把函数执行 -> DemoOne()
2.把virtualDOM中的props,作为实参传递给函数 -> DemoOne(props)
3.接收函数执行的返回结果「也就是当前组件的virtualDOM对象」
4.最后基于render把组件返回的虚拟DOM变为真实DOM,插入到#root容器中!!