React之jsx底层处理机制

一、关于 JSX 底层处理机制

第一步:把我们编写的 JSX 语法,编译为虚拟 DOM 对象「virtualDOM」

虚拟 DOM 对象:框架自己内部构建的一套对象体系(对象的相关成员都是 React 内部规定的),基于这些属性描述出,我们所构建视图中的,DOM 节点的相关特征!!

@1 基于 babel-preset-react-app 把 JSX 编译为 React.createElement(...) 这种格式!!

只要是元素节点,必然会基于 createElement 进行处理!

React.createElement(ele,props,...children)

  • ele:元素标签名「或组件」
  • props:元素的属性集合(对象)「如果没有设置过任何的属性,则此值是 null」
  • children:第三个及以后的参数,都是当前元素的子节点

@2 再把 createElement 方法执行,创建出 virtualDOM 虚拟 DOM 对象

也有将 virtualDOM 称之为:JSX 元素JSX 对象ReactChild 对象...

jsx 复制代码
virtualDOM = {
  $$typeof: Symbol(react.element),
  ref: null,
  key: null,
  type: 标签名「或组件」,
  // 存储了元素的相关属性 && 子节点信息
  props: {
      // 元素的相关属性,
      // children:子节点信息「没有子节点则没有这个属性、属性值可能是一个值、也可能是一个数组」
  }
}

第二步:把构建的 virtualDOM 渲染为真实 DOM

真实 DOM:浏览器页面中,最后渲染出来,让用户看见的 DOM 元素!!

基于 ReactDOM 中的 render 方法处理的!!

v16

jsx 复制代码
ReactDOM.render(<>...</>, document.getElementById("root"));

v18

jsx 复制代码
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<>...</>);

补充说明:

  • 第一次渲染页面是直接从 virtualDOM-> 真实 DOM

  • 但是后期视图更新的时候,需要经过一个DOM-DIFF 的对比,计算出补丁包PATCH(两次视图差异的部分),把 PATCH 补丁包进行渲染!!


二、组件渲染机制

1. 函数组件

  • 创建:在 SRC 目录中,创建一个 xxx.jsx 的文件,就是要创建一个组件;我们在此文件中,创建一个函数,让函数返回 JSX 视图「或者 JSX 元素、virtualDOM 虚拟 DOM 对象」;这就是创建了一个"函数组件"!!

  • 调用:基于 ES6Module 规范,导入创建的组件「可以忽略.jsx 后缀名」,然后像写标签一样调用这个组件即可!!

  • 命名:组件的名字,我们一般都采用 PascalCase「大驼峰命名法」这种方式命名

jsx 复制代码
<Component/> // 单闭合调用
<Component> ... </Component> // 双闭合调用
  • 调用组件的时候,我们可以给调用的组件设置(传递)各种各样的属性
jsx 复制代码
<DemoOne
  title="我是标题"
  x={10}
  data={[100, 200]}
  className="box"
  style={{ fontSize: "20px" }}
/>
  • 如果设置的属性值不是字符串格式,需要基于"{}胡子语法"进行嵌套

  • 调用组件的时候,我们可以把一些数据/信息基于属性 props 的方式,传递给组件!!

  • 渲染机制

@1 基于 babel-preset-react-app 把调用的组件转换为 createElement 格式

jsx 复制代码
React.createElement(DemoOne, {
  title: "\u6211\u662F\u6807\u9898",
  x: 10,
  data: [100, 200],
  className: "box",
  style: {
    fontSize: "20px",
  },
});

@2 把 createElement 方法执行,创建出一个 virtualDOM 对象!!

jsx 复制代码
var virtualDOM = {
  $$typeof: Symbol(react.element),
  key: null,
  props: {
    title: "我是标题",
    x: 10,
    data: 数组,
    className: "box",
    style: { fontSize: "20px" },
  }, //如果有子节点「双闭合调用」,则也包含 children!!
  ref: null,
  type: DemoOne,
};

@3 基于 root.rendervirtualDOM 变为真实DOM

type 值不再是一个字符串,而是一个函数了,此时:

  • 把函数执行 -> DemoOne()
  • 把 virtualDOM 中的 props,作为实参传递给函数 -> DemoOne(props)
  • 接收函数执行的返回结果「也就是当前组件的 virtualDOM 对象」
  • 最后基于 render 把组件返回的虚拟 DOM 变为真实 DOM,插入到#root 容器中!!

2. 属性 props 的处理

  • 调用组件,传递进来的属性是"只读"的「原理:props 对象被冻结了」

    Object.isFrozen(props) -> true

    获取:props.xxx

    修改:props.xxx=xxx =>报错

  • 作用:父组件(index.jsx)调用子组件(DemoOne.jsx)的时候,可以基于属性,把不同的信息传递给子组件;子组件接收相应的属性值,呈现出不同的效果,让组件的复用性更强!!

  • 虽然对于传递进来的属性,我们不能直接修改,但是可以做一些规则校验

    • 设置默认值

      jsx 复制代码
      Component.defaultProps = {
        x: 0,
        // ......
      };
    • 设置其它规则,例如:数据值格式、是否必传... 「依赖于官方的一个插件:prop-types」

      github.com/facebook/pr...

      jsx 复制代码
      import PropTypes from "prop-types";
      FuncComponent.propTypes = {
        // 类型是字符串、必传
        title: PropTypes.string.isRequired,
        // 类型是数字
        x: PropTypes.number,
        // 多种校验规则中的一个
        y: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
      };

      传递进来的属性,首先会经历规则校验,不管校验成功还是失败,最后都会把属性给形参 props 只不过如果不符合设定的规则,控制台会抛出警告错误{不影响属性值的获取}!!

  • 如果就想把传递的属性值进行修改,我们可以:

    • 把 props 中的某个属性赋值给其他内容「例如:变量、状态...」
    • 我们不直接操作 props.xxx=xxx,但是我们可以修改变量/状态值!!

三、知识扫盲

基于props特性的一些js底层知识扩展,对象的冻结、密封、扩展问题

1.冻结

冻结对象:Object.freeze(obj)

检测是否被冻结:Object.isFrozen(obj) =>true/false

被冻结的对象:

  • 不能修改成员值
  • 不能新增成员
  • 不能删除现有成员
  • 不能给成员做劫持「Object.defineProperty」

2.密封

密封对象:Object.seal(obj)

检测是否被密封:Object.isSealed(obj)

被密封的对象:可以修改成员的值,但也不能删、不能新增、不能劫持!!

3.扩展

把对象设置为不可扩展:Object.preventExtensions(obj)

检测是否可扩展:Object.isExtensible(obj)

  • 被设置不可扩展的对象:除了不能新增成员、其余的操作都可以处理!!

  • 被冻结的对象,即是不可扩展的,也是密封的!!同理,被密封的对象,也是不可扩展的!!

相关推荐
T^T尚4 小时前
uniapp H5上传图片前压缩
前端·javascript·uni-app
出逃日志5 小时前
JS的DOM操作和事件监听综合练习 (具备三种功能的轮播图案例)
开发语言·前端·javascript
XIE3925 小时前
如何开发一个脚手架
前端·javascript·git·npm·node.js·github
GISer_Jing5 小时前
React渲染相关内容——渲染流程API、Fragment、渲染相关底层API
javascript·react.js·ecmascript
山猪打不过家猪5 小时前
React(五)——useContecxt/Reducer/useCallback/useRef/React.memo/useMemo
前端·javascript·react.js
前端青山5 小时前
React事件处理机制详解
开发语言·前端·javascript·react.js
科技D人生5 小时前
Vue.js 学习总结(14)—— Vue3 为什么推荐使用 ref 而不是 reactive
前端·vue.js·vue ref·vue ref 响应式·vue reactive
对卦卦上心5 小时前
React-useEffect的使用
前端·javascript·react.js
练习两年半的工程师5 小时前
React的基本知识:事件监听器、Props和State的区分、改变state的方法、使用回调函数改变state、使用三元运算符改变state
前端·javascript·react.js
啵咿傲5 小时前
在React中实践一些软件设计思想 ✅
前端·react.js·前端框架