为了获得更好的阅读体验,请访问原地址:传送门

一、React 简介


React 是什么

React 是一个起源于 Facebook 的内部项目,因为当时 Facebook 对于市场上所有的 JavaScript MVC 框架都不太满意,所以索性就自己写了一套,用来架设 Instagram。做出来之后,发现这套东西还蛮好用的,于是就在 2013 年 5 月开源了

在这里我们需要稍微注意一下 库(Library)框架(Framework) 的区别,React 本身是一个用于构建用户界面的 JavaScript 库,而我们平时所说的 React 框架其实是指的是 React/ React-router 和 React-redux 的结合体,库和框架的本质区别体现在于控制权:

  • 「库」是一个封装好的特定的集合,提供给开发者使用,而且是特定于某一方面的集合(方法和函数),库没有控制权,控制权完全在于使用者本身;
  • 「框架」顾名思义是一套架构,会基于自身的特点向用户提供一套比较完整的解决方案,如果使用者选定了一套框架,那么就需要根据框架本身做出一定的适应。

为什么使用 React?

这是一个非常有趣的问题,也让我困惑和苦恼。在笔者还在学校的时候尝试用 Vue 搭建了一套简单的博客系统,学习曲线平滑,让只会一些基础 HTML/ CSS 代码的我通过一段时间学习就能够上手了,但是学习 React 以来,进展变得相对缓慢.. 一部分原因是因为 React 创新性的开发模式以及让我感到无所适从的 JSX 语法(菜才是原罪)。

Vue 作者尤雨溪在知乎上回答「Vue 和 React 的优点分别是什么?」这个问题的时候提到 :

这里我可以大方地承认,如果多年以后要论历史地位,React 肯定是高于 Vue 的。事实上,我作为一个开发者,也是由衷地佩服 Jordan Walke, Sebastian Markbage 这样的,能从开发模式层面上提出突破性的新方向的人。

React 从一开始的定位就是提出 UI 开发的新思路。当年 Pete Hunt 最开始推广 React 的时候的一句口号就叫 "Rethinking Best Practices",这样的定位使得 React 打开了一些全新的思路,吸引了一群喜欢折腾的早期核心用户,并在这个基础上通过社区迭代孵化出了许多今天被 React 开发者当作常识的 pattern。这是 React 伟大的地方,Vue 里面也有很多地方是直接受到了 React 的启发。React 敢做这样的尝试,是因为它是 Facebook。这样的体量的公司,在 infrastructure 层面获得质的提升,收益是巨大的,而且 Facebook 的工程师们足够聪明又要靠工资吃饭,改变他/她们的习惯并不是什么问题。而对外推广,则是一种大公司才有的 “改变业界” 的底气。

相比「为什么使用 React?」的理由,称赞 React 的倒是明显更多一些(React 确实是突破性的开发模式)。

是因为 React 组件化的思想吗?不是。我觉得这跟多少跟微服务化之类的概念有点儿类似,这是属于一个时代对于计算机工程的思想进步,是对于团队协作提出的新一种成熟的解决方案,也是必然的一种趋势。当前流行的不管是 Angular/ Vue 还是 React,都天然的支持着组件化的概念。

那是因为 React 性能出众吗?我想也不是。或许 React 刚出世时因为其独特高效的虚拟 DOM 设计,能够在前端江湖中平步青云,但是现在前端技术都主键地趋于成熟(我也不懂,我乱说的..),从很多地方的对比数据中,都能够看得到其实 React 与其他框架的性能差异并不是特别大。并且体现在平时的开发中,这样对比不明显的速度差异,根本没有多大的用处。

还看到一种观点,说 React 适用于构建大型的项目。从我并不多的了解中,我知道 React 体系中天然有着许多的约束,以及一些不成文的约定,这就好像是 SpringBoot 中默认提供给使用者的一些姿势,天然就有很强的工程性,加上一些约定俗成的代码风格 or 归约,这就使得 Java 很适合一些大型的团队项目。但能不能开发大型的项目从来都是取决于人,而不是采用了哪种框架。

所以比较令我信服的理由是(我乱猜的):像 Java 一样,React 体系足够成熟,社区也非常活跃,你遇到的问题很容易在网络上找到答案,并且也有一些成熟的实践 or 轮子用以解决各种各样的问题。而且 React 还有一个比较特别的特性是:你能够比较无痛地使用 React Native 开发原生移动应用。

二、React 核心概念


虚拟 DOM(Vitural Document Object Model)

要理解这个「虚拟 DOM」的概念,首先我们就需要知道什么是「DOM」。我们先暂时忘掉什么网页之类的,我们想象现在我们需要编写程序来对下列的 Markdown 文档进行改变应该怎么做:

# Title
## subtitle - 1
content - 1
## subtitle - 2
content - 2

比如我现在就想要 content - 2 的内容进行改变,那么我就需要一行一行的不断遍历直到最后遍历到它才能进行操作,对内容改变的操作都差不多,所以如果我想对这个查找的操作进行优化,最简单的想法就是把它树化以减少高度,增加效率。

DOM 的概念

DOM 是英文 Document Object Model 的缩写,即文档对象模型。它是一种跨平台的、独立于编程语言的 API,它把 HTML、XHTML 或 XML 文档都当做一个树结构,而每个节点视为一个对象,这些对象可以被编程语言操作,进而改变文档的结构,映射到文档的显示。DOM 最开始的时候是和 JavaScript 交织在一起的,只是后来它们最终演变成了两个独立的实体。DOM 被设计成与特定编程语言相独立,尽管绝大部分时候我们都是使用 JavaScript 来操作,但其实其他的语言一样可以(如 Python)。

假如有这么一段 HTML 代码:

<html>
<head>
<title>文档标题</title>
</head> <body>
<a href="">链接</a>
<h1>标题</h1>
</body>
</html>

那么它最终就应该会是下面这棵树一样的结构:

这里不对 DOM 节点的类型啊方法之类的进行讨论,我们只需要对 DOM 有一个大致的概念就好了。

浏览器渲染 DOM 的流程

我们可以简单了解一下浏览器渲染 DOM 的流程:

  1. 解析 HTML 建立 DOM 树;
  2. 解析 CSS,并结合 DOM 树形成 Reander 树;
  3. 布局 Render 树(Layout/ reflow),确定各节点的尺寸、位置等信息;
  4. 绘制 Render 树(Paint),绘制页面像素信息;
  5. 浏览器将各层信息发给 GPU,GPU 会将各层合成(Composite),显示在屏幕上;

操作 DOM 为什么慢

其实严格来说,单纯的操作 DOM 并不慢,说它慢是带有一定条件的。

想象在一次事件循环中多次操作 DOM 时,有时希望 JS 代码中能立刻获取最新的 DOM 节点信息,这时浏览器不得不挂起 JS 引擎,转而调用 DOM 引擎,计算渲染出最新的 DOM,以此来获取最新的 DOM 节点信息,接着再重新激活 JS 引擎 继续后续的操作。

可以预见,上述操作不仅需要多次进行引擎的切换,还需要多次计算布局,重新绘制 DOM。事实上paint是一个耗时的过程,然而layout是一个更耗时的过程,我们无法确定layout一定是自上而下或是自下而上进行的,甚至一次layout会牵涉到整个文档布局的重新计算。

但是layout是肯定无法避免的,所以我们主要是要最小化layout的次数。

所以,降低引擎切换频率、减小 DOM 变更规模才是 DOM 性能优化方案的关键!

Virtual DOM 算法步骤

虚拟 DOM 正是解决了上述问题,它的本质就是用 JS 对象来模拟出我们真实的 DOM 树,它的算法大致如下:

  1. 用 JavaScript 对象映射形成 DOM 树的结构,然后用这个树构建一个真正的 DOM 树,插到文档当中;
  2. 当状态变更的时候,重新构造一棵新的对象树,然后用新的树和旧的树进行比较(Diff 算法),记录两棵树差异;
  3. 把第二步中所记录的差异应用到步骤一所构建的真正的 DOM 树上,视图就更新。

虚拟 DOM 和真实 DOM 的区别

我们由此可以对比出两者的不同:

  1. 改变多个状态,影响多个节点布局时,只是频繁的修改了内存中的 JS 对象,然后一次性比较并修改真实 DOM 中需要改的部分,最后在真实 DOM 中进行排版与重绘,减少过多 DOM 节点排版与重绘损耗;
  2. 真实 DOM 频繁排版与重绘的效率是相当低的;
  3. 虚拟 DOM 有效降低大面积(真实 DOM 节点)的重绘与排版,因为最终与真实 DOM 比较差异,可以只渲染局部(同2);

使用虚拟DOM的损耗计算:

总损耗 = 虚拟DOM增删改 + (与Diff算法效率有关)真实DOM差异增删改 + (较少的节点)排版与重绘

直接使用真实DOM的损耗计算:

总损耗 = 真实DOM完全增删改 + (可能较多的节点)排版与重绘

Diff 算法

虚拟 DOM 的核心在于 Diff,它自动帮你计算那些应该调整的,然后只修改应该被调整的区域,省下的不是运行速度这种 "小速度",而是开发速度/ 维护速度/ 逻辑简练程度等 "总体速度"。

但虚拟 DOM 快也是在相对条件下的,这里引用 @尤雨溪大大在知乎问题《网上都说操作真实 DOM 慢,但测试结果却比 React 更快,为什么?》上回答的一句话吧:

不要天真地以为 Virtual DOM 就是快,diff 不是免费的,batching 么 MVVM 也能做,而且最终 patch 的时候还不是要用原生 API。在我看来 Virtual DOM 真正的价值从来都不是性能,而是它 1) 为函数式的 UI 编程方式打开了大门;2) 可以渲染到 DOM 以外的 backend,比如 ReactNative。

Diff 大致可以分为三种类型:

  • Tree Diff: 新旧两棵 DOM 树,逐层对比的过程,就是 Tree Diff,当整颗DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到;
  • Component Diff: 在进行 Tree Diff 的时候,每一层中,组件级别的对比,叫做 Component Diff:
    • 如果对比前后,组件的类型相同,则暂时认为此组件不需要被更新;
    • 如果对比前后,组件类型不同,则需要移除旧组件,创建新组件,并追加到页面上;
  • Element Diff: 在进行组件对比的时候,如果两个组件类型相同,则需要进行元素级别的对比,这叫做 Element Diff;

三、Hello World


使用 React 的网页源码,结构大致如下(可以直接运行):

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body> <div id="example"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
</script> </body>
</html>

上面代码有两个地方需要注意。首先,最后一个 <script> 标签的 type 属性为 text/babel 。这是因为 React 独有的 JSX 语法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/babel"

其次,上面代码一共用了三个库: react.jsreact-dom.jsBrowser.js ,它们必须首先加载。其中,react.js是 React 的核心库,react-dom.js 是提供与 DOM 相关的功能,Browser.js 的作用是将 JSX 语法转为 JavaScript 语法,这一步很消耗时间,实际上线的时候,应该将它放到服务器完成。

$ babel src --out-dir build

上面命令可以将 src 子目录的 js 文件进行语法转换,转码后的文件全部放在 build 子目录。

ReactDOM.render()

ReactDOM.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。

ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);

上面代码将一个 h1 标题,插入 example 节点,运行结果如下:

JSX 语法

上一节的代码, HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX 的语法,它允许 HTML 与 JavaScript 的混写。我们先来看以下一段代码:

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

与浏览器的 DOM 元素不同,React 当中的元素事实上是普通的对象,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致。要将 React 元素渲染到根 DOM 节点中,我们通过把它们都传递给 ReactDOM.render() 的方法来将其渲染到页面上:

var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));

JSX 看起来类似 HTML ,你也可以在上面代码中嵌套多个 HTML 标签,但是需要使用一个 div 元素包裹它。

JavaScript 表达式

我们可以在 JSX 中使用 JavaScript 表达式。表达式写在花括号 {} 中。实例如下:

ReactDOM.render(
<div>
<h1>{1+1}</h1>
</div>
,
document.getElementById('example')
);

在 JSX 中不能使用 if else 语句,但可以使用 conditional (三元运算) 表达式来替代。以下实例中如果变量 i 等于 1 浏览器将输出 true, 如果修改 i 的值,则会输出 false.

ReactDOM.render(
<div>
<h1>{i == 1 ? 'True!' : 'False'}</h1>
</div>
,
document.getElementById('example')
);

样式

React 推荐使用内联样式。我们可以使用 camelCase 语法来设置内联样式. React 会在指定元素数字后自动添加 px 。以下实例演示了为 h1 元素添加 myStyle 内联样式:

var myStyle = {
fontSize: 100,
color: '#FF0000'
};
ReactDOM.render(
<h1 style = {myStyle}>菜鸟教程</h1>,
document.getElementById('example')
);

注释

注释需要写在花括号中,实例如下:

ReactDOM.render(
<div>
<h1>菜鸟教程</h1>
{/*注释...*/}
</div>,
document.getElementById('example')
);

数组

JSX 允许在模板中插入数组,数组会自动展开所有成员:

var arr = [
<h1>菜鸟教程</h1>,
<h2>学的不仅是技术,更是梦想!</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);

参考资料


  1. http://www.ruanyifeng.com/blog/2015/03/react.html - React 入门实例教程 - 阮一峰
  2. https://www.jianshu.com/p/60100985dd7f - 前端框架与库的区别
  3. https://www.zhihu.com/question/301860721/answer/545031906 - Vue 和 React 的优点分别是什么?
  4. https://zhuanlan.zhihu.com/p/22184194 - 你真的理解 DOM 了吗?
  5. https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model/Introduction - DOM 概述
  6. https://blog.huteming.site/posts/e0c41c5f/ - 为什么说虚拟DOM更快

按照惯例黏一个尾巴:

欢迎转载,转载请注明出处!

独立域名博客:wmyskxz.com

简书ID:@我没有三颗心脏

github:wmyskxz

欢迎关注公众微信号:wmyskxz

分享自己的学习 & 学习资料 & 生活

想要交流的朋友也可以加qq群:3382693

React入门学习的更多相关文章

  1. React 入门学习笔记整理目录

    React 入门学习笔记整理(一)--搭建环境 React 入门学习笔记整理(二)-- JSX简介与语法 React 入门学习笔记整理(三)-- 组件 React 入门学习笔记整理(四)-- 事件 R ...

  2. react入门学习及总结

    前言 不知不觉一年又过去了,新的一年又到来,2019应该要好好思考,好好学点有用的东西,规划下自己今后的学习方向,不要再像以前那样感觉很迷茫. react简单介绍 官网及中文文档 https://re ...

  3. [Todo] Nodejs学习及Spider实验(包括php入门学习、React入门学习)

    /Users/baidu/Documents/Data/Interview/Web-Server开发 深入浅出Node.js-f46c http://blog.csdn.net/u012273376/ ...

  4. React 入门学习笔记2

    摘自阮一峰:React入门实例教程,转载请注明出处. 一.获取真实的DOM节点 组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM).只有当它 ...

  5. React 入门学习笔记1

    摘自阮一峰:React入门实例教程,转载请注明出处. 一. 使用React的html模板 使用React, 我们需要加载3个库,react.js, react-dom.js, 和browser.js. ...

  6. 【JAVASCRIPT】React入门学习-文本渲染

    摘要 react 学习包括几个部分: 文本渲染 JSX 语法 组件化思想 数据流 文本渲染 1. 纯文本渲染 <!DOCTYPE html> <html> <head&g ...

  7. React 入门学习笔记整理(一)——搭建环境

    使用create-react-app脚手架搭建环境 1.安装node .软件下载地址:https://nodejs.org/en/,我下的推荐的版本. 安装之后测试是否安装成功.windows系统下, ...

  8. React 入门学习笔记整理(二)—— JSX简介与语法

    先看下这段代码: import React from 'react'; //最终渲染需要调用ReactDOM库,将jsx渲染都页面中 import ReactDOM from 'react-dom'; ...

  9. React 入门学习笔记整理(三)—— 组件

    1.定义组件 1)函数组件 function GreateH(props){ return <div> <h2>hello,{props.name}</h2> &l ...

随机推荐

  1. 【Offer】[59-2] 【队列的最大值】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 请定义一个队列并实现函数max得到队列里的最大值,要求函数max.push_back和 pop_front 的时间复杂度都是0(1). ...

  2. 《即时消息技术剖析与实战》学习笔记5——IM系统如何保证消息的一致性

    一.什么是消息一致性 消息一致性指的是消息的时序一致性,即消息收发的一致性.如果不能保证时序一致性,就会造成聊天语义不连贯,引起误会. 对于点对点的聊天场景,时序一致性保证接收方的接收顺序和发送方的发 ...

  3. 【LeetCode】240-搜索二维矩阵 II

    题目描述 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target.该矩阵具有以下特性: 每行的元素从左到右升序排列. 每列的元素从上到下升序排列. 示例: 现有矩阵 m ...

  4. Go语言标准库之strconv

    Go语言中strconv包实现了基本数据类型和其字符串表示的相互转换. strconv包 strconv包实现了基本数据类型与其字符串表示的转换,主要有以下常用函数: Atoi().Itia().pa ...

  5. Python函数编程——闭包和装饰器

    Python函数编程--闭包和装饰器 一.闭包 关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数).而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量.参数.当其中一个 ...

  6. 超级密码(BFS)

    Problem Description Ignatius花了一个星期的时间终于找到了传说中的宝藏,宝藏被放在一个房间里,房间的门用密码锁起来了,在门旁边的墙上有一些关于密码的提示信息: 密码是一个C进 ...

  7. Java 内存溢出分析

    原文地址:Java 内存溢出分析 博客地址:http://www.moonxy.com 一.前言 Java 的 JVM 的内存一般可分为 3 个区:堆(heap).栈(stack)和方法区(metho ...

  8. Windows下更换MAC地址

    使用TMAC软件是最佳方案.官网地址:www.technitium.com

  9. [Leetcode] 第309题 最佳买卖股票时机含冷冻期

    一.题目描述 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 .​ 设计一个算法计算出最大利润.在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 你不能同时参与 ...

  10. 波士顿房价预测 - 最简单入门机器学习 - Jupyter

    机器学习入门项目分享 - 波士顿房价预测 该分享源于Udacity机器学习进阶中的一个mini作业项目,用于入门非常合适,刨除了繁琐的部分,保留了最关键.基本的步骤,能够对机器学习基本流程有一个最清晰 ...