细读 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.


目录
相关文章
|
20天前
|
前端开发 JavaScript
除了使用Route组件,React Router还有其他方式处理404错误页面吗
除了使用Route组件,React Router还有其他方式处理404错误页面吗
81 58
|
20天前
|
前端开发
React 中高阶组件的原理是什么?
React 中高阶组件的原理是什么?
89 57
|
20天前
|
前端开发 开发者
除了函数组件和类组件,React 还有其他创建组件的方式吗?
除了函数组件和类组件,React 还有其他创建组件的方式吗?
80 57
|
20天前
|
前端开发
如何在React Router中定义404错误页面组件?
如何在React Router中定义404错误页面组件?
76 57
|
20天前
|
前端开发
在 React 中使用高阶组件时,如何避免命名冲突?
在 React 中使用高阶组件时,如何避免命名冲突?
81 56
|
13天前
|
移动开发 前端开发 JavaScript
React音频播放列表组件:常见问题、易错点与解决方案
本文介绍了在React中实现音频播放列表时常见的挑战及解决方案。通过基础实现、常见问题分析和最佳实践,帮助开发者避免状态管理、生命周期控制和事件处理中的陷阱。关键点包括使用`useRef`操作音频元素、`useState`同步播放状态、全局状态管理防止多音频同时播放、以及通过`useEffect`清理资源。还提供了代码示例和跨浏览器兼容性处理方法,确保高效实现功能并减少调试时间。
77 30
|
10天前
|
编解码 前端开发 开发者
React 图片组件样式自定义:常见问题与解决方案
在 React 开发中,图片组件的样式自定义常因细节问题导致布局错乱、性能损耗或交互异常。本文系统梳理常见问题及解决方案,涵盖基础样式应用、响应式设计、加载状态与性能优化等,结合代码案例帮助开发者高效实现图片组件的样式控制。重点解决图片尺寸不匹配、边框阴影不一致、移动端显示模糊、加载失败处理及懒加载等问题,并总结易错点和最佳实践,助力开发者提升开发效率和用户体验。
41 22
|
15天前
|
移动开发 前端开发 UED
React 音频音量控制组件 Audio Volume Control
在现代Web应用中,音频播放功能不可或缺。React以其声明式编程和组件化开发模式,非常适合构建复杂的音频音量控制组件。本文介绍了如何使用HTML5 `&lt;audio&gt;`元素与React结合,实现直观的音量控制系统,并解决了常见问题如音量范围不合理、初始音量设置及性能优化等,帮助开发者打造优秀的音频播放器。
63 27
|
17天前
|
移动开发 前端开发 开发者
React 音频播放控制组件 Audio Controls
本文介绍了如何使用React构建音频播放控制组件,涵盖HTML5 `&lt;audio&gt;`标签和React组件化思想的基础知识。针对常见问题如播放状态管理、进度条更新不准确及跨浏览器兼容性,提供了详细的解决方案和代码示例。同时,还总结了易错点及避免方法,如确保音频加载完成再操作、处理音频错误等,帮助开发者实现稳定且功能强大的音频播放器。
48 11
|
16天前
|
移动开发 前端开发 UED
React 音频进度条组件 Audio Progress Bar
在现代Web开发中,音频播放功能不可或缺。使用React构建音频进度条组件,不仅能实现播放控制和拖动跳转,还能确保代码的可维护性和复用性。本文介绍了如何利用HTML5 `&lt;audio&gt;`标签的基础功能、解决获取音频时长和当前时间的问题、动态更新进度条样式,并避免常见错误如忘记移除事件监听器和忽略跨浏览器兼容性。通过这些方法,开发者可以打造高质量的音频播放器,提升用户体验。
46 6