1. JSX基础知识
大小驼峰:PasalCase、CamelCase、kabab-case、xxx_xxx
JSX(javascript and xml\html): 把JS与HTML标签混合在了一起(不是字符串拼接,❌element = “
123
“)
 🎈Tips: vscode如何支持JSX语法(格式化、快捷提示…)
- 后缀名设置为jsx
- webpack打包的规则中,也是会对.jsx这种文件,按照JS的方式进行处理的
1.1.语法特性
JSX语法具备过滤效果(过滤非法内容),有效防止XSS攻击(扩展思考:总结常见的XSS攻击和预防方案?)
1.1.1.一个根容器
最外层只能一个根容器(一个app有一个容器)
在ReactDOM.createRoot()的时候,不能直接把HTML/BODY做为根容器,需要指定一个额外的盒子「例如:#root」
1.1.2.一个根元素节点
容器可以有多个视图、每一个视图,只有一个“根节点”。
- 出现多个根节点则报错 Adjacent JSX elements must be wrapped in an enclosing tag.
- React.Fragment 空文档标记标签:<></>(既保证只有一个根节点,又不新增一个HTML层级结构!!)
1.1.3 动态绑定数据{}
把JS表达式嵌入到HTML中,需要使用{} (❗大括号中存放的是JS表达式)
JS表达式:执行有结果的表达式,如:数值、数学运算、三元运算符、循环(借助数组迭代的办法map,foreach没有返回值所以不行)
 if(){}不行,JSX中进行的判断一般都要基于三元运算符来完成
命令式循环for,for/in,for/of、while不行
1.1.4 JS表达式要求
{}语法中嵌入不同的值,所呈现出来的特点
- number/string:值是啥,就渲染出来啥 
- boolean/null/undefined/Symbol/BigInt:渲染的内容是空 
- 除数组对象外,其余对象一般都不支持在{}中进行渲染,但是也有特殊情况: - 
- JSX虚拟DOM对象
- 给元素设置style行内样式,要求必须写成一个对象格式
 
- 数组对象:把数组的每一项都分别拿出来渲染「并不是变为字符串渲染,中间没有逗号」 - 
- JSX中遍历数组中的每一项,动态绑定多个JSX元素,一般都是基于数组中的map来实现的 - 和vue一样,循环绑定的元素要设置key值(作用:用于DOM-DIFF差异化对比) 
 
- 函数对象:不支持在{}中渲染,但是可以作为函数组件,用方式渲染。 
1.1.5 给元素设置样式
- 行内样式:需要基于对象的格式处理,直接写样式字符串会报错;
| 12
 3
 4
 
 | <h2 style={{color: 'red',
 fontSize: '18px'
 }}>
 
 | 
- 设置样式类名:需要把class替换为className;
1.2 核心语法初识
为了可以看懂代码于是这里先讲讲一些语法核心。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | import React from 'react'; import ReactDOM from 'react-dom/client';
 
 
 const root = ReactDOM.createRoot(document.getElementById('root'));
 
 
 root.render(
 ....
 );
 
 | 
这样就能看懂练手了。😊
1.2 练手
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 
 | 
 import React from 'react';
 import ReactDOM from 'react-dom/client';
 
 const root = ReactDOM.createRoot(document.getElementById('root'));
 
 
 let flag = false,isRun = false;
 
 
 root.render(
 <>
 {/!* 控制元素的display样式:不论显示还是隐藏,元素本身都渲染出来了 *!/}
 <button style={{
 display: flag ? 'block' : 'none'
 }}>按钮1</button>
 
 <br />
 
 {/!* 控制元素渲染或者不渲染 *!/}
 {flag ? <button>按钮2</button> : null}
 
 <br />
 
 <button>{isRun ? '正在处理中...' : '立即提交注册'}</button>
 </>
 );
 
 
 
 let data = [{
 id: 1,
 title: 'zono1'
 }, {
 id: 2,
 title: 'zono2'
 }, {
 id: 3,
 title: 'zono3'
 }];
 
 root.render(
 <>
 <h2 className="title">今日新闻</h2>
 <ul className="news-box">
 {data.map((item, index) => {
 /* 循环创建的元素一定设置key属性,属性值是本次循环中的“唯一值”「优化DOM-DIFF」 */
 return <li key={item.id}>
 <em>{index + 1}</em>
   
 <span>{item.title}</span>
 </li>;
 })}
 </ul>
 
 <br />
 
 {/* 扩展需求:没有数组,就是想单独循环五次 */}
 {new Array(5).fill(null).map((_, index) => {
 return <button key={index}>
 按钮{index + 1}
 </button>;
 })}
 </>
 );
 
 | 
2. JSX底层渲染机制
使用babel转换工具,可以直观看见。
第一步:JSX→virtualDOM
运行时会把我们编写的JSX语法编译为虚拟DOM对象(virtualDOM)。
虚拟DOM对象:框架自己内部构建的一套对象体系(对象的相关成员都是React内部规定的),基于这些属性描述出我们所构建视图中的DOM节点的相关特征。
具体流程
https://babeljs.io/repl可以在线观看jsx转换为对象的效果
下面是我编写的例子
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 
 | <>
 <h2 className="title" style={styObj}>123</h2>
 <div className ="box">
 123
 <span>321</span>
 <span>zono</span>
 </div>
 </>
 
 
 
 
 
 React.createElement(
 React.Fragment,
 null,
 React.createElement(
 "h2",
 {className: "title",
 style: styObj,},
 "123"
 ),
 React.createElement(
 "div",
 {className: "box",},
 "123",
 React.createElement("span", null, "321"),
 React.createElement("span", null, "zono")
 )
 );
 
 
 import { jsx as _jsx } from "react/jsx-runtime";
 import { jsxs as _jsxs } from "react/jsx-runtime";
 import { Fragment as _Fragment } from "react/jsx-runtime";
 _jsxs(_Fragment, {
 children: [
 _jsx("h2", {
 className: "title",
 style: styObj,
 children: "123",
 }) ,
 _jsxs("div", {
 className: "box",
 children: [
 "123" ,
 _jsx("span", {
 children: "321",
 }),
 _jsx("span", {
 children: "zono",
 }),
 ],
 }),
 ],
 });
 
 | 
让我们来解读一下:
- 基于babel-preset-react-app(一个专门对react的babel) 把JSX编译为React.createElement(...)格式。元素节点都会基于createElement进行处理!
出现的函数的解释:
createElement – React 中文文档 (docschina.org)
React.createElement(ele,props,...children)
- 感觉Automatic模式就是表示的更清楚。
- 注意该方法开发时并不常用,有jsx就够了
- 再把 createElement 方法执行,创建出virtualDOM虚拟DOM对象(JSX元素、JSX对象、ReactChild对象…)!! - 我们可以把- React.createElement打印出来看看效果。
 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | virtualDOM = {
 $$typeof: Symbol(react.element),
 ref: null,
 key: null,
 type: 标签名「或组件」,
 
 props: {
 元素的相关属性,如:className、style,
 children:子节点信息(没有子节点则没有这个属性、属性值可能是一个值、也可能是一个数组)
 }
 }
 
 | 
实操(非必要)
 这里将简单编写一个creatElement函数
 感觉太硬了,所以分个p,见04.jsx底层实操
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | const createElement = function createElement(type, props, ...children) {
 
 let len = children.length;
 let virtualDOM = {
 type,
 props: {}
 };
 if (props !== null) virtualDOM.props = { ...props };
 if (len === 1) virtualDOM.props.children = children[0];
 if (len > 1) virtualDOM.props.children = children;
 return virtualDOM;
 };
 
 | 
第二步:virtualDOM→真实DOM
真实DOM:浏览器页面中,最后渲染出来,让用户看见的DOM元素(js基础内容)
    这一步是基于ReactDOM中的render方法处理的
v16(16版本)写法
| 12
 3
 4
 
 | ReactDOM.render(<>...</>,
 document.getElementById('root')
 );
 
 | 
v18
| 12
 3
 4
 
 | const root = ReactDOM.createRoot(document.getElementById('root'));root.render(
 <>...</>
 );
 
 | 
实操(非必要)
render
推荐看04
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 
 | const render = function render(virtualDOM, container) {let { type, props } = virtualDOM;
 if (typeof type === "string") {
 let element = document.createElement(type);
 for (let key in props) {
 if (!props.hasOwnProperty(key)) break;
 if (key === 'className') {
 element.setAttribute('class', props[key]);
 continue;
 }
 if (key === 'style') {
 let styOBJ = props['style'];
 for (let attr in styOBJ) {
 if (!styOBJ.hasOwnProperty(attr)) break;
 element.style[attr] = styOBJ[attr];
 }
 continue;
 }
 if (key === 'children') {
 let children = props['children'];
 if (!Array.isArray(children)) children = [children];
 children.forEach(item => {
 if (typeof item === "string") {
 element.appendChild(document.createTextNode(item));
 return;
 }
 render(item, element);
 });
 continue;
 }
 element.setAttribute(key, props[key]);
 }
 container.appendChild(element);
 }
 };
 
 | 
更新:新老virtual对比,然后更新对应内容
第一次渲染页面是直接从virtualDOM->真实DOM;
后期视图更新时,会经过一个DOM-DIFF的对比,计算出补丁包PATCH(两次视图差异的部分),把PATCH补丁包进行渲染!!