【深入React】之理解 JSX

简介: 【深入React】之理解 JSX

开篇

学习 React 一定离不开 JSX ,当其他人问起 “你是怎么理解 JSX 的 ?”

大概率我们都会说:“通过 babel 转换,将 jsx 转化为 React.createElement .... ”

那我先抛出几个问题,希望能帮助你更好的理解 JSX

  • 什么是 JSX ?
  • 为什么是它
  • 浏览器是如何 “认识” JSX 的

什么是 JSX(JavaScriptXml)

JSX 是一种 JavaScript 的语法扩展,首先在 React 中被进入,其格式比较像是模版语言,但事实上完全是在 JavaScript 内部实现的。元素是构成 React 应用的最小单位,JSX 就是用来声明 React 当中的元素。React 主要使用 JSX 来描述用户界面,但 React 并不强制要求使用 JSX  [1] ,而 JSX 也在 React 之外的框架得到了广泛的支持,包括 Vue.js ,Solid 等。

简单理解:

  • JSX 就是 React 用来描述用户界面的一个模版,在这个模版里面既可以写 JS 又可以写 HTML 标签。
  • 又或者说 JSX 其实是 React.createElement 的语法糖。

简单理解一下转化,后面我们会详细介绍部分内容。

<MyButton color="blue" shadowSize={2}>
  Click Me 
</MyButton>

会编译为:

React.createElement(   MyButton,   {color: 'blue', shadowSize: 2},   'Click Me' )

为什么是 JSX

这个问题其实可以换个角度思考,想想 React 需要什么 ?

  • 一个声明式的编程方式(声明式编程不用告诉计算机问题领域,从而避免随之而来的副作用)
  • 代码结构尽可能的简洁
  • 样式、结构和事件尽可能的可以实现高内聚,实际上 Vue3 options Api 的转化也是学习了 React 中的设计思想
  • 不想引入新的概念,在原生 JS 的基础上进行扩展即可。(Vue 中的单文件组件 就是一个新的概念,需要学习很多指令)

知道了上面 React 需要啥,那为什么选择 JSX 呢 ?这个问题就引刃而解了,很显然,我们谈到 React 设计需要的这些特点都是指向了 JSX

浏览器是如何认识 JSX 的 ?

我们知道浏览器是无法直接识别 JSX 的,那我们只能通过一些特殊的手段来将其转化,让其变为一个一个的 dom 节点,然后再在这个节点上添加一些样式,事件。

通过官网的 babejs 在线转换 ,我们可以看到一个一个标签都转化为了 React.createElement 方法。

image.png

React.createElement 简称 h 函数,在 Vue 中也是类似的叫法。h是指 hyperscript,一种可以通过 js 来创建 html 的库。设计思想以及作用都是和 hyperscript 是一样的,所以简称为 h 函数没啥问题。

想知道 React.createElement 做了什么,我们还是得来看看其内部的实现, 此处代码转于 【深入理解 jsx

export function createElement(type, config, children) {
  // propName 变量用于储存后面需要用到的元素属性
  let propName; 
  // props 变量用于储存元素属性的键值对集合
  const props = {}; 
  // key、ref、self、source 均为 React 元素的属性,此处不必深究
  let key = null;
  let ref = null; 
  let self = null; 
  let source = null; 
  // config 对象中存储的是元素的属性
  if (config != null) { 
    // 进来之后做的第一件事,是依次对 ref、key、self 和 source 属性赋值
    if (hasValidRef(config)) {
      ref = config.ref;
    }
    // 此处将 key 值字符串化
    if (hasValidKey(config)) {
      key = '' + config.key; 
    }
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // 接着就是要把 config 里面的属性都一个一个挪到 props 这个之前声明好的对象里面
    for (propName in config) {
      if (
        // 筛选出可以提进 props 对象里的属性
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName) 
      ) {
        props[propName] = config[propName]; 
      }
    }
  }
  // childrenLength 指的是当前元素的子元素的个数,减去的 2 是 type 和 config 两个参数占用的长度
  const childrenLength = arguments.length - 2; 
  // 如果抛去type和config,就只剩下一个参数,一般意味着文本节点出现了
  if (childrenLength === 1) { 
    // 直接把这个参数的值赋给props.children
    props.children = children; 
    // 处理嵌套多个子元素的情况
  } else if (childrenLength > 1) { 
    // 声明一个子元素数组
    const childArray = Array(childrenLength); 
    // 把子元素推进数组里
    for (let i = 0; i < childrenLength; i++) { 
      childArray[i] = arguments[i + 2];
    }
    // 最后把这个数组赋值给props.children
    props.children = childArray; 
  } 
  // 处理 defaultProps
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) { 
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

通过 React.createElement 创建出来的节点其实浏览器还是不认识的,回想一下我们平常使用 React 的过程,还缺少了一个 render方法。

const element = <div> hello 邵小白 </div>
const container = document.getElementById('root')
ReactDOM.render(element, container)

ReactDom 指的是渲染库,因为我们已经通过React.createElment 创建出一颗树(fiber 树)来了,后面想让哪个平台认识,就做一些平台内部的处理就好了,比如 ReactDOM 就是想让浏览器认识我们 fiber 树的一个工具库。

我们简单实现一下 React.DOM.render 方法,帮助大家理解,其实本质上还是通过 dom 上的 createElement 以及 appendChild 去做的这样一件事情。

render(element,container){
  // 判断元素类型
  const dom = element.type === 'TEXT_ELEMENT' ?
    document.createTextNode('') :
    document.createElement(element.type)
  // 将元素中 除了children 属性之外的其他 props 添加在需要创建的节点身上
  Object.keys(element.props).
    filter(key => key !== 'children')
    .forEach(name => {
      dom[name] = element.props[name]
    })
  // 递归调用
   element.props.children.forEach(child => {
    render(child, dom)
  })
  // 将最后生成的 dom-tree 添加到 容器中
  container.appendChild(dom)
}

当然实际上 render 方法不会这么简单,还需要考虑线程阻塞的问题,这里就不过多介绍了。但是通过实现这个 render 方法相信你一定有了新的理解。


相关文章
|
19天前
|
前端开发 JavaScript 开发者
React:JSX语法入门
React:JSX语法入门
31 0
|
18天前
|
JavaScript 前端开发
vue3中使用jsx报错React is not defined和h is not defined
vue3中使用jsx报错React is not defined和h is not defined
|
19天前
|
XML 前端开发 JavaScript
react中JSX的详解
react中JSX的详解
19 2
|
19天前
|
前端开发 JavaScript 安全
React中的JSX:语法与原理深入解析
【4月更文挑战第25天】本文深入解析React中的JSX,一种JavaScript语法扩展,使代码更直观。JSX让开发者以HTML样式描述组件UI,但最终编译成JavaScript。通过Babel转换,JSX标签转为React.createElement()调用,创建虚拟DOM。JSX的优势在于直观性、类型安全、代码复用和工具支持,助力高效开发React组件,适应不断发展的Web应用需求。
|
19天前
|
XML 前端开发 JavaScript
【前端】深入了解React JSX语法及实例应用
【前端】深入了解React JSX语法及实例应用
18 0
|
19天前
|
前端开发 JavaScript 安全
react为什么要使用JSX
react为什么要使用JSX
32 1
|
19天前
|
XML JavaScript 前端开发
说说React jsx转换成真实DOM的过程?
说说React jsx转换成真实DOM的过程
20 0
|
19天前
|
JSON 前端开发 JavaScript
React源码解析-JSX
React源码解析-JSX
66 1
|
19天前
|
前端开发 JavaScript 开发者
React中JSX语法入门
React中JSX语法入门
|
19天前
|
前端开发 JavaScript
react JSX是什么,作用是什么
react JSX是什么,作用是什么
46 0