细读 React | 元素、组件、实例

简介: 细读 React | 元素、组件、实例

前言


元素是构成 React 应用最小的砖块,它描述了你在页面上想看到的内容。


const element = <h1>Hello World</h1>


与浏览器的 DOM 元素不同,React 元素是创建开销极小的普通对象。React DOM 会负责更新 DOM 来与 React 元素保持一致。

以上 JSX 语法编写的代码,最终会被 Babel (在线 Babel 编译器)转换为:

const element = React.createElement('h1', null, 'Hello World')

React.createElement() 最后会返回一个普通的 JavaScript 对象(该对象就是对 React 元素的描述):

const element = {
  type: 'h1',
  props: {
    children: 'Hello World'
  },
  // ...
}


可以在控制台打印一下 element 元素:

12.webp.jpg


需要注意的是,React 元素描述的是 Virtual DOM 的结构,而非真实 DOM。真实 DOM 由 React DOM 根据 Virtual DOM 负责更新。


正文


React 元素


上面提到,React 元素本身上就是一个 JavaScript 对象,而且是不可变对象(Immutable Object)。


由于 React 元素是不可变对象,若对其 props 等属性进行修改操作,是会抛出错误的。


React 元素表示了某个时刻的 UI,而更新 UI 的唯一方式是创建一个全新的元素。React DOM 会根据 Diff 算法只更新它需要更新的部分。


React 组件


刚开始接触 React 时,人们很容易将元素组件的概念混淆。元素是 React 应用最小的单元,而组件则是由一个或多个元素构成的。

组件是 React 中很重要的思想。一个复杂庞大的 React 应用,是由许多结构简单、清晰的组件组合而成的。


组件,从概念上类似于 JavaScript 函数,它接受任意的入参(即 props),并返回 React 元素。


在 React 中,组件分为函数组件class 组件。下面声明了两种不同类型的组件:

// 函数组件
function Comp1() {
  return <h1>Functional Component.</h1>
}
// class 组件
class Comp2 extends React.Component {
  // render 是类组件唯一必需实现的方法
  render() {
    return <h1>Class Component.</h1>
  }
}


我们知道 ReactDOM.render(element, container[, callback]) 方法,第一个参数 element 接收的是 React 元素。而我们声明的组件本质上是一个 JavaScript 函数,因此,我们应该要这样使用自定义组件:

ReactDOM.render(<Comp1 />, document.getElementById('root'))


React 元素分类


从以上可知,我们遇到的 React 元素,可以是 DOM 标签:

const element = <h1>Hello World</h1>


也可以是用户自定义的组件:

const element = <Comp1 />


因此,我们可以将 React 元素分为两类:DOM 类型元素、组件类型元素。

前者是指使用类似 divh1pspan 等 DOM 标签创建的 React 元素,而后者是指使用 React 组件(如 Class Components、Functional Components)创建的 React 元素。


对于 DOM 标签,React 可以分辨出来,而自定义的 React 组件呢?若不约定规则,那么它无法辨认啊。所以 React 要求以 JSX 语法编写 React 组件时,其组件名称必须以大写字母开头


例如,MyComponent 是“合法”的 React 组件,而 myComponent 是“不合法”的。否则 React 会发出如下警告 ⚠️:


Warning: The tag <myComponent> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.

上面打了双引号,其实在声明一个 React 组件时,是可以以小写字母开头的,但在使用时必须以大写字母开头。即我们可以将 React 组件赋值给一个以大写字母开头的变量,然后正常使用。但这种“非主流”的写法,不被推荐。再者,声明一个构造函数,它的名称应以大写字母开头,这是一种约定俗成的写法。


同样的,DOM 类型元素只能以小写字母的形式,例如 <div />。如果使用 <Div /> 会被 React 认为是自定义组件,但由于我们又没有声明,因此可能会抛出引用错误:ReferenceError: Div is not defined


想了解更多关于此规范的原因,请看深入 JSX


React 元素、组件使用误区


一些容易混淆、出错的写法:

const Element = <h1>React Element.</h1>
const rootEl = document.getElementById('root')
// 正确示例 ✅
ReactDOM.render(Element, rootEl)
ReactDOM.render(<Comp1 />, rootEl)
// 错误示例 ❌
ReactDOM.render(<Element />, rootEl) // 1️⃣
ReactDOM.render(Comp1, rootEl) // 2️⃣


错误示例 1️⃣ 会抛出以下警告 ⚠️:


Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: <h1 />. Did you accidentally export a JSX literal instead of a component?


错误示例 2️⃣ 会抛出以下警告 ⚠️:


Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.


我们都知道,ReactDOM.render(element, container[, callback]) 第一个参数接收 React 元素。而 React 元素都是由 React.createElement() 返回的。我们从 React.createElement() 的语法上解释以上错误示例:

React.createElement(
  type,
  [props],
  [...children]
)


  • type 可以是标签名字符串(如 divspanh1 等),也可以是 React 组件类型(class 组件或函数组件),或者是 React Fragment 类型。
  • props 可选,组件属性
  • children 可选,子元素。含有多个子元素时,最终 React Element 的 props.children 会返回一个数组。


原因分析:

  1. ReactDOM.render(Element, rootEl) 相当于 ReactDOM.render(<h1>React Element.</h1>, rootEl)( 这里 Element 只是一个变量)。以小写字母开头的元素代表一个 HTML 内置组件,在编译时 <h1> 会生成响应的字符串 'h1' 传递给 React.createElement,是符合参数要求的。所以没问题。
  2. ReactDOM.render(<Comp1 />, rootEl)以大写字母开头的元素,对应着自定义组件,<Comp1 /> 会被编译为 React.createElement(Comp1)。而且 Comp1 正是 React 组件(本质上就是 JavaScript 函数),也符合参数要求,所以没问题。
  3. 按上面的规则,<Element /> 以大写字母开头,会被认为是 React 组件,但 Element 的类型是 "object",而不是 "function"(class 本质也是函数),不符合 React.createElement 参数要求,因此会被报错或警告。
  4. ReactDOM.render(Comp1, rootEl) 中的 Comp1 是一个 JavaScript 函数,而非 React 元素,因此也会报错。


实例


React 组件是一个函数或者类,而实际发挥作用的是 React 组件的实例对象。只有在组件实例化之后,每个组件实例才有自身的 props、state 或对 DOM 节点的引用。

function Child() {
  return <div>Child Component</div>
}
class Parent extends React.Component {
  // 需要注意的是,在组件将要被销毁的时候会触发此生命周期函数
  // 当组件从页面中“移除”,并不意味着组件实例被回收掉了
  // 仅在组件实例不再被任何地方引用,它才会被垃圾回收。
  componentWillUnmount() {
    // ...
  }
  render() {
    return (
      <div>
         <div>Parent Component</div>
         {/* 当父组件触发 render 之后,子组件就会被实例化 */}
         <Child />
      </div>
    )
  }
}


节点


很多情况下,我们会使用 PropTypes 来限制组件属性类型。这里我们提一下与本文相关的两种类型:PropTypes.nodePropTypes.element

import PropTypes from 'prop-types'
function MyComponent(props) {
  return (
    <div>
      <div>node: { props.node }</div>
      <div>element: { props.element }</div>
    </div>
  )
}
MyComponent.propTypes = {
  node: PropTypes.node,
  element: PropTypes.element
}


  • PropTypes.element:可以是 nullundefined 或 React 元素。
  • PropTypes.node: 可以是 nullundefined、字符串、React 元素或包含这些类型的数组。


The end.


目录
相关文章
|
27天前
|
前端开发 UED 索引
React 图片灯箱组件 Image Lightbox
图片灯箱组件是一种常见的Web交互模式,用户点击缩略图后弹出全屏窗口展示大图,并提供导航功能。本文介绍了基于React框架的图片灯箱组件开发,涵盖初始化状态管理、图片加载与预加载、键盘和鼠标事件处理等常见问题及解决方案。通过`useState`和`useEffect`钩子管理状态,使用懒加载和预加载优化性能,确保流畅的用户体验。代码案例展示了组件的基本功能实现,包括打开/关闭灯箱、切换图片及键盘操作。
127 80
|
11天前
|
移动开发 前端开发 JavaScript
React 视频播放控制组件 Video Controls
本文介绍了如何使用 React 构建视频播放控制组件(Video Controls),涵盖基本概念、创建步骤和常见问题解决。首先,通过 HTML5 `&lt;video&gt;` 标签和 React 组件化思想,实现播放/暂停按钮和进度条等基础功能。接着,详细讲解了初始化项目、构建 `VideoControls` 组件及与主应用的集成方法。最后,针对视频无法播放、控制器状态不同步、进度条卡顿和音量控制失效等问题提供了具体解决方案,并介绍了全屏播放和自定义样式等进阶功能。希望这些内容能帮助你在实际项目中更好地实现和优化视频播放功能。
83 40
|
26天前
|
移动开发 前端开发 JavaScript
React 视频播放器组件:Video Player
本文介绍了如何使用 React 和 HTML5 `&lt;video&gt;` 标签构建自定义视频播放器组件。首先,通过创建基础的 React 项目和 VideoPlayer 组件,实现了基本的播放、暂停功能。接着,探讨了常见问题如视频加载失败、控制条样式不一致、性能优化不足及状态管理混乱,并提供了相应的解决方案。最后,总结了构建高效视频播放器的关键要点,帮助开发者应对实际开发中的挑战。
110 27
|
29天前
|
前端开发 JavaScript API
React 图片放大组件 Image Zoom
本文介绍如何使用React创建图片放大组件(Image Zoom),提升用户体验。组件通过鼠标悬停或点击触发放大效果,利用`useState`管理状态,CSS实现视觉效果。常见问题包括图片失真、性能下降和移动端支持,分别可通过高质量图片源、优化事件处理和添加触摸事件解决。易错点涉及状态管理混乱、样式冲突和过多事件绑定,建议使用上下文API、CSS模块及优化事件绑定逻辑。高级功能扩展如多张图片支持和自定义放大区域进一步丰富了组件的实用性。
58 25
|
25天前
|
存储 编解码 前端开发
React 视频上传组件 Video Upload
随着互联网的发展,视频内容在网站和应用中愈发重要。本文探讨如何使用React构建高效、可靠的视频上传组件,涵盖基础概念、常见问题及解决方案。通过React的虚拟DOM和组件化开发模式,实现文件选择、进度显示、格式验证等功能,并解决跨域请求、并发上传等易错点。提供完整代码案例,确保用户能顺畅上传视频。
128 92
|
6天前
|
移动开发 前端开发 UED
React 音频预览组件:Audio Preview
本文介绍如何使用 React 构建音频预览组件,涵盖基础实现、常见问题及解决方案。通过 HTML5 `&lt;audio&gt;` 标签和 React 状态管理,实现播放控制。解决文件路径、浏览器兼容性等问题,并优化性能,避免状态不同步和内存泄漏,提升用户体验。
54 22
|
1天前
|
移动开发 前端开发 JavaScript
React 视频播放控制组件 Video Controls
本文深入探讨了如何使用React创建功能丰富的视频播放控制组件(Video Controls)。首先介绍了React与HTML5 `&lt;video&gt;` 标签的基础知识,展示了如何通过状态管理和事件处理实现自定义控件。接着分析了常见问题如视频加载失败、控件样式不一致、状态管理混乱和性能问题,并提供了相应的解决方案。最后通过完整代码案例详细解释了播放、暂停、进度条和音量控制的实现方法,帮助开发者在React中构建高质量的视频播放组件。
36 17
|
8天前
|
移动开发 前端开发 UED
React 音频播放器组件 Audio Player
本文介绍如何使用React创建功能丰富的音频播放器组件。基于HTML5 `&lt;audio&gt;` 标签,结合React的状态管理和事件处理,实现播放、暂停、进度和音量控制等功能。通过代码示例展示基本实现,并探讨常见问题如自动播放限制、进度条不更新、文件加载失败及多实例冲突的解决方案。同时,避免易错点如忽略生命周期管理、错误处理和性能优化,确保高效开发与良好用户体验。
62 23
|
10天前
|
缓存 前端开发 JavaScript
React 视频弹幕组件 Video Danmaku
本文介绍了如何在React中构建视频弹幕组件,提升用户观看体验和互动性。首先通过Create React App初始化项目,并集成`react-player`作为视频播放器。接着实现基本弹幕功能,包括评论的接收与显示,使用CSS动画实现滚动效果。针对常见问题如弹幕重叠、性能下降及同步问题,提供了随机化位置、分批加载和监听播放进度等解决方案。最后探讨了弹幕分类和特效等高级技巧,确保弹幕系统的高性能和良好用户体验。
52 23
|
28天前
|
存储 前端开发 索引
React 图片轮播组件 Image Carousel
本文介绍了如何使用React创建图片轮播组件。首先,解释了图片轮播的基本概念和组件结构,包括图片容器、导航按钮、指示器和自动播放功能。接着,通过代码示例详细说明了创建基本组件、添加自动播放、处理边界情况的步骤。还探讨了常见问题如状态更新不及时、内存泄漏和样式问题,并提供了解决方案。最后,介绍了进阶优化,如添加过渡效果、支持触摸事件和动态加载图片,帮助读者构建更完善的轮播组件。
52 16