为了账号安全,请及时绑定邮箱和手机立即绑定
慕课专栏

目录

索引目录

专业技术团队出品:React深度剖析+实战

原价 ¥ 58.00

立即订阅
02 React 基础知识补充之 JSX & 元素渲染
更新时间:2020-07-21 09:31:29
才能一旦让懒惰支配,它就一无可为。——克雷洛夫

引导语

上一个小节讲解了如何快速创建一个 React 应用,本小节我们开始巩固下 React 的基础知识。相信大家对 React JSX 都有一定的了解,但 JSX 的优点不仅仅是简化了写代码的过程,还需要思考为什么要使用 JSX 以及如何将 JSX 渲染到页面里。即使不了解也不用担心,接下来我们带大家一起深入学习 JSX 和 React 中是如何进行元素渲染的。

JSX 出现的原因

React 的一大亮点就是虚拟 DOM:可以在内存中创建虚拟 DOM 元素,由虚拟 DOM 来确保只对界面上真正变化的部分进行实际的 DOM 操作。和真实 DOM 一样,虚拟 DOM 也可以通过 JavaScript 来创建:

const ele = React.createElement('div', null, 'hello, world')

虽然通过以上的方式,就可以构建成 DOM 树,但这种代码可读性比较差,于是就有了 JSX 。JSX 是 JavaScript 的语法扩展,使用 JSX ,就可以采用我们熟悉的 HTML 标签形式来创建虚拟 DOM,也可以说 JSX 是 React.createElement 的一个语法糖。

JSX 语法

JSX 标签类型

// HTML 类型标签
const ele = <div>hello, world</div>

// react 组件类型标签
const component = <HelloWrold />

在 JSX 语法中,有两种标签类型:

  • HTML 类型的标签:这种标签名需小写字母开头;
  • 组件类型的标签(在之后的小节会详细介绍组件):这种标签必须以大写字母开头。

React 通过标签首字母的大小写来区分渲染的是标签类型。

React 中的所有标签,都必须有闭合标签 />

JSX 中使用 JavaScript 表达式

在 JSX 中,也可以使用 JavaScript 表达式,只需要使用 {} 将表达式包裹起来就行。通常给标签属性传值或通过表达式定义子组件时会用到。例如下面代码:

// 属性传值
const addUser = {id: 1, name: '添加好友'}

<PanelItem item={addUser} />
// 通过表达式定义子组件
const teams = [
  {id: 1, name: '创建高级群'},
  {id: 2, name: '搜索高级群'}
]

<ul>
  {teams.map(item => 
      <PanelItem
        item={item}
        key={item.id}/>
  )}
</ul>

JSX 中使用 JavaScript 表达式时,不能使用多行 JavaScript 语句。

JSX 编译之 Babel

JSX 并不是标准的 JavaScript,它需要编译器 Babel 将 JSX 代码编译成标准的 JavaScript 语言。

Babel 会把 JSX 转译成一个名为 React.createElement() 的函数调用,可以理解为 JSX 是使用一些规则来编写 React.createElement(type, config, children) 的快捷方式,可以将 JSX 视为编写 React.createElement() 声明的更简短且更自然的方法。

上文中JSX语法小节的 HellowWorld 组件,会被 Babel 编译成如下代码:

// HTML 类型标签
var ele = React.createElement("div", null, "hello, world")

// react 组件类型标签
var component = React.createElement(HelloWrold, null)

可以使用 Babel 官网提供的链接 https://www.babeljs.cn/repl 在线转译 JSX 代码。

JSX 有何优点

  1. 使用熟悉的语法定义 HTML 元素,提供更加语义化的标签,使用 JSX 编写模板更简单快速;
  2. 更加直观:JSX 让小组件更加简单、明了、直观;
  3. 抽象了 React 元素的创建过程,使得编写组件变得更加简单;
  4. JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化;
  5. JSX 是类型安全的,在编译过程中就能发现错误;
  6. 防注入攻击,所有的内容在渲染之前都被转换成了字符串,可以有效地防止 XSS(跨站脚本) 攻击。

元素渲染

在上文介绍的 JSX 中我们了解到如何创建 React 元素,但创建的 React 元素与浏览器的 DOM 元素不同,它是一个虚拟的 DOM 元素,在浏览器中我们需要把虚拟 DOM 转换成真实可用的浏览器 DOM,此过程就是元素渲染的过程。接下来,我们先来讨论下如何创建元素。

元素创建

React 元素是 React 应用中最小的可渲染单位,用于描述屏幕上输出的内容。我们可以使用 JSX 语法创建元素。

本篇主要讲解以下 3 种创建 React 元素的方式:

1. JSX 语法方式创建

const element = <h1>Hello, world</h1>;

使用 JSX 语法时我们必须引入 React 库。

JSX 的执行更快,而且编译时能及时发现错误,推荐大家使用此方式创建 React 元素。

2. React.createElement() 方式创建元素

// [] 中括号里的是选填项
React.createElement(
    type,         // 元素类型,可以是标签名字符串,也可以是 React 组件或者 React fragment, 比如:'div' 'span' 'React.Fragment'
    [props],      // 属性,对象形式,比如:{className: 'title'}
    [...children] // 子节点,比如:react元素、dom元素或文本
)

这种方法其实在实际 React 开发中几乎不会使用,因为可以直接使用 JSX 方法(JSX 章节已详细讲解了 JSX 的优点,大家可以回顾下)。

3. React.cloneElement() 方式创建元素

// [] 中括号里的是选填项
React.cloneElement(
    element,      // 元素类型,必须是 React 元素
    [props],      // 属性,对象形式,比如:{className: 'title'}
    [...children] // 子节点,比如:react元素、dom元素或文本
)

注意:createElement 的第一个参数必须是字符串或 React 组件,而在 cloneElement 里第一个参数应该是 ReactElement。

渲染方式

为了将 React 元素呈现到浏览器 DOM 中,我们需要有一个容器或者说根 DOM 元素来承载。一般在模板文件 index.html 使用一个 id 为 root 的 DOM 元素作为根元素。

//index.html
<div id="root"></div>

如何向根元素渲染一个简单的 React 元素呢?我们使用 ReactDOM.render() 方法渲染元素:

ReactDOM.render(
    element,    // 要渲染的元素
    container,  // 元素要渲染的容器
    [callback]  // 回调函数,可选的
)

ReactDOM.render() 方法有 3 个参数,它的第三个参数是一个回调函数,可以用来做渲染后的回调。
ReactDOM.render() 方法的返回值,其实是根元素的 React 实例。

我们需要在 index.js 文件中添加以下内容:

import React from 'react'
import ReactDOM from 'react-dom'

const myElement = <div>hello, world!</div>

ReactDOM.render(myElement, document.getElementById("root"))

React 16 之前的版本 DOM 不识别除了 HTML 和 SVG 支持的以外属性,而在 React 16 版本中将会把全部的属性传递给 DOM 元素。这个新特性可以让我们摆脱可用的 React DOM 属性白名单。

更新元素

React 元素是不可变的,一旦创建了元素,就无法更新其子代或者属性。我们已经创建并渲染了第一个 React 元素,但 React 并不是为了静态页面而存在的,为此,我们需要更新元素。因此,我们必须多次使用 render 方法来更新元素。对于大多数 React 应用而言,ReactDOM.render() 通常只会调用一次。React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会更新需要更新的部分。

读到此处,你是否好奇为什么 render 通常只调用一次?

在 React 中,render 执行得到的并不是真正的 DOM 节点,得到的仅仅是轻量级的 JavaScript 对象,我们称之为虚拟 DOM。

虚拟 DOM 具有批处理特性以及高效的 diff 算法(虚拟 DOM 和 diff 算法后续章节会有详细讲解),使得我们可以随时更新整个页面而无需担心性能问题。

虚拟 DOM 确保只对界面上真正变化的部分进行实际的 DOM 操作。

import React from 'react';
import './App.scss';

class App extends React.Component {
  state = {
    standingTime: 0
  }
  timeChange () {
    setInterval(() => {
      const current = this.state.standingTime + 1
      this.setState({ standingTime: current })
    }, 1000)
  }
  convertTheTime (time) {
    if (time < 60) {
      return `${time}秒`
    } else if (time < 60 * 60) {
      return `${Math.floor(time/60)}${time%60}秒`
    } else {
      return `你停留时间超过一小时,请刷新重新计时`
    }
  }
  componentDidMount () {
    this.timeChange()
  }
  render() {
    return (
      <div className="App">
        <div className="App-content">
          <p>
            你在本页面停留时间:
            <b>
              { this.convertTheTime(this.state.standingTime) }
            </b>
          </p>
        </div>
      </div>
    )
  }
}

export default App;

图片描述

提示:
和 DOM 元素不同的是,React 元素是纯对象,创建的代价低。
ReactDOM.render() 渲染传入容器节点的元素,首次调用时,将替换其中所有的 DOM 元素,以后的调用使用 React 的 虚拟 DOM diff 算法(虚拟 DOM diff 算法后续章节会有详细解读)进行有效的更新。ReactDOM.render() 不会修改容器节点(仅修改容器内的子节点)。
此外,元素和组件的概念是不一样的,组件是由元素组成的。

小结

本节内容主要讲解了 JSX 的语法及编译方式,随后在 JSX 的优点上做了深入的探究,之所以使用 JSX,不仅因为 JSX 速度更快,而且还能够在编译时及时发现错误。本节还阐述了元素渲染的方式及原理,render 时会对更新前后的虚拟 DOM 树做对比,只更新需要更新的部分。后续章节还会带大家探索渲染原理相关更多的奥秘,敬请期待。下一小节我们会继续学习 React 的基础知识点,为后续的章节夯实基础。

demo地址

知识扩展阅读

}
立即订阅 ¥ 58.00

你正在阅读课程试读内容,订阅后解锁课程全部内容

千学不如一看,千看不如一练

手机
阅读

扫一扫 手机阅读

专业技术团队出品:React深度剖析+实战
立即订阅 ¥ 58.00

举报

0/150
提交
取消