深入理解React(一)JSX与虚拟DOM

初衷

使用 React 有一段时间了, 一直想找个时间写一个 React 的系列文章。忙里抽闲,完成了第一篇。写这系列文章的初衷是总结这段时间的技术学习,以及给那些想学习 React 的同学们一点帮助。我会尽量以通俗易懂的语言阐述我对 React 的理解,希望能照顾到更多的新手。相信大家应该都明白一个道理,最能够带领你进步的,并不是比你强很多很多的大牛,而是刚好比你走得快那么一步的脚印。

为什么是React

这是2018年现代前端框架的使用情况统计,图片来自 JavaScript 如日中天,2018趋势报告来啦!,数据仅供参考。可以看到这是个前端框架三足鼎立的时代,选择 React 并不是因为其他框架不够好,而是 React 本身有其独特的魅力,吸引着开发者的聚集。就好比喜欢一个人,并不是因为其他人不好才喜欢他,而是被他独特的魅力所吸引才产生了感情。所以对新手来说,大可不必太过纠结于框架的选择,框架只是个帮助我们开发的工具而已。你只需要知道,React 本身足够强大,能够让你在大前端的世界里走的更远!

Hello World

我们从最简单的 Hello World 开始,进入 React 的世界,使用的是最新的 React 16版本。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <div id="root"></div>
  
  // 引入react及react-dom库
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  // 引入babel
  <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>

  // 在babel编译的环境下执行
  <script type="text/babel">
    const vDom = <h1>Hello World</h1>
    const root = document.getElementById('root')
    ReactDOM.render(vDom, root)
  </script>
</body>

</html>
复制代码

在使用 React 之前,我们需要先引入 React 相关的 js 库。

  • react.development.js 这是 React 的核心库,用于构建页面 UI
  • react-dom.development.js 这是 React 的 DOM 相关操作库,可以将你构建的 UI 渲染到浏览器中

除此之外,还需引入 babel 进行语法解析转换。

我们来详细解析下上面的 demo,其实 script 标签中一共就写了3句代码,但其中包含了 React 的核心知识点——虚拟 DOM 和 JSX 。

<script type="text/babel">
    const vDom = <h1>Hello World</h1>
    const root = document.getElementById('root')
    ReactDOM.render(vDom, root)
</script>
复制代码

JSX

JSX 是 React 的灵魂,全称 JavaScript XML。顾名思义,它可以让你在 JS 中使用 XML 标记的方式去直接声明界面的 DOM,这是 React 独有的语法糖。

请看 HelloWorld 的例子

const vDom = <h1>Hello World</h1> // 创建h1标签,右边千万不能加引号
const root = document.getElementById('root') // 找到<div id="root"></div>节点
ReactDOM.render(vDom, root) // 把创建的h1标签渲染到root节点上
复制代码

这里我们在 JS 里直接创建了一个<h1>Hello World</h1>标签,并赋值给 vDom,这就是 JSX 语法。
如果我们将 react 库的引入注释掉

会发现浏览器中报错
原因就是原生JS语法并不支持直接声明 DOM 标签,这是 JSX 的语法,需要 React.js 库的支持。

ReactDOM.render()这个是 react-dom 库中的方法,用于将你创建好的 HTML 模板(虚拟 DOM 节点)插入到某个节点上,并渲染到页面上。第一个参数是 HTML 模板,第二个参数是指定的 DOM 节点。

在上述 JSX 语法中有几个值得注意的地方:

  • 右边的 h1 标签千万不能加引号。如果加上引号的,JS 引擎会将其解释为字符串类型。其实从本质来说,<h1>Hello World</h1>这是一个对象,叫做虚拟 DOM 对象,后面会讲到。
  • <script> 标签的 type 属性为 text/babel。由于使用了 JSX 这种特殊的语法,我们不能再像往常一样,使用<script type="text/javascript">来解析 JSX,而要借助 babel 来进行解析、转换成 JS。
  • 将 JSX 语法转为 JavaScript 语法,这一步很消耗时间。所以现在前端项目,都会使用工作流的形式来构建,不会在 html 页面中直接引入 react、写 js 代码等等。

简单来说,就是 JSX 赋予我们在 JS 中直接创建 HTML 标签的能力,因为 HTML 标签实在是太弱小了。
当然我们也可以不使用 JSX 的语法创建 DOM 节点,直接使用 React 给我们提供的普通 JS 写法,React.createElement()API 来创建。

const vDom = React.createElement(
  'h1', // 第一个参数是标签名,例如h1、span、table...
  { className: 'hClass', id: 'hId' }, // 第二个参数是个对象,里面存着标签的一些属性,例如id、class等,因为class是保留字,所以要写成className的形式
  'hello world' // 第三个参数是节点中的文本
)
const root = document.getElementById('root')
ReactDOM.render(vDom, root)
复制代码

可以看到和之前的 JSX 写法const vDom = <h1>Hello World</h1>效果是一样的

在使用 React.createElement()这种写法时,我们并不需要用 babel 进行解析,因为这本身就是 JS 的语法,JS 引擎可以解析。
其实本质上,JSX 就是为了简化直接调用 React.createElement() API 的一颗语法糖而已,他执行最终会被 babel 解析转换为 React.createElement()的形式,所以推荐使用 JSX 的语法来创建页面的 UI(也就是 HTML 的一些 DOM)。

JSX的优点

  • 类 XML 语法结构清晰
  • 增强 JS 语义
  • 屏蔽 DOM 操作
  • ···

我们以前在操作时,需要经过以下流程
创建节点 -> 找到插入位置 -> 插入节点,若是还有子节点,还需要继续添加,一切都需要手动实现。
而使用 React 的 JSX 语法只需要const vDom = xxx 然后ReactDOM.render(vDom, root)就可以。
换句话说,以前都是过程式操作,你不仅知道要做什么,还需要自己手动去实现。而现在变成了声明式操作,就好比在下命令一样,你只需要下命令创建怎样的 DOM 节点,然后下命令插入,中间的过程全部都由 React 框架帮你自动实现,让开发者可以完全屏蔽 DOM 的操作。

JSX基本语法规则

  • 遇到 HTML 标签(以 < 开头),就用 HTML 规则解析
  • 遇到代码块{}或括号(),就用 JavaScript 规则解析

我们把 Hello World 的例子提升下

<script type="text/babel">
    let title = 'Hello World'
    const vDom = (
      <div>
        <h1>{title}</h1>
      </div>
    )
    const root = document.getElementById('root')
    ReactDOM.render(vDom, root)
</script>
复制代码

上述代码在执行时,当遇到<div>的左箭头括号时,使用 HTML 的解析规则,当遇到{title}时,采用 JS 的规则解析,也就是获取变量 title 的值。值得一提的是,如果需要创建嵌套的 HTML 结构,推荐使用()括号括起来。

虚拟DOM

  • 我们在写前端页面时,手动操作 DOM,繁琐又容易出错,在大规模应用下维护起来也很困难。
  • 并且每次修改 DOM,浏览器的 DOM 树都需要重绘重排,效率十分低下。
  • 既然 DOM 手动操作太繁琐且效率低下,那就在每次状态更新时重新渲染整个页面
  • 每次都重新渲染整个页面效率十分低下,所以就加上一个虚拟 DOM
  • 每次修改时,先在虚拟 DOM 上更新,最后在批量更新整个页面,这样页面只需要一次大的更新,效率很高

虚拟 DOM 是在 DOM 的基础上建立的一个抽象层,我们对 DOM 中的数据和状态所做的任何改动,都会被自动且高效的同步到虚拟 DOM,最后再批量同步到 DOM 中。React 会在内存中维护一个虚拟 DOM 树,当我们对这个树进行读或写的时候,实际上是对虚拟 DOM 进行的。当数据变化时,React 会自动更新虚拟 DOM 树,然后拿新的虚拟 DOM 树和旧的虚拟 DOM 树进行对比(当中有 DOM diff算法,这个之后再说),把不同的虚拟节点放到一个队列里,最终在渲染时一次批量更新这些队列中的虚拟节点到真实 DOM 中,这是对 DOM 渲染效率上的一个质的提升。

一点小结

React 以 JS 为中心,以 JSX 的独特语法糖将"HTML"放到了 JS 里,而 JS 远比 HTML 要强大。因此,与其增强 HTML 让其拥有逻辑,不如增强 JS 让其支持标签化,这样一来既丰富了 JS 操控领域,又提升了页面渲染的性能。所以,你若是新手也没关系,只要你 JS 能力足够强,相信一定能在 React 的世界里策马奔腾!

最后推荐几个 React 的社区
React 官方文档:react.docschina.org/
React China:react-china.org/
React 开源中国社区:www.oschina.net/translate/t…

我的 github 地址:github.com/FightingHao
如果有什么不懂的地方,欢迎评论,大家一起探讨 React 有关的知识!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值