React 能把数据变成 DOM 显示出来,处理在 DOM 上触发的事件,把 DOM 事件再返回给数据
只需要定义数据和 DOM 的对应关系,这样在数据变化的时候,DOM 会自动变化
所谓数据驱动的 DOM就是数据发生了变化,DOM 会跟着变化,再用事件反馈给数据
在日常开发中,推荐以组件的方式组织我们的项目结构;
另外 React 提供了 state 和 props 两个主要的属性
1)state 就是组件里面的 React 的数据我们也可以称之为状态
2)props 是用于父子组件通信的子组件可以通过 props 拿到父组件的数据或方法; React 组件间的数据传递方式是由上向下的;因此需要父组件在 props里把 *** 作的方法传递给子组件,通过一种类似回调的方式让父组件的状态发生改变
除此之外, React 的组件是有生命周期的,一个组件从建立、到存在、最后到销毁的整个过程就是组件的生命周期;React 也为整个生命周期提供了灵活的节点,方便我们处理各种业务情节
我们可以通过 JSX 表达式处理数据和 DOM 之间的关系,而不需要 *** 作 DOM
可以从以下三个方面理解 JSX:
1)JSX 是一种 JS 扩展的表达式
2)JSX 是带有逻辑的标记语法,有别于 HTML 模版
3)并且支持样式、逻辑表达式和事件
React 中采用虚拟 DOM 的机制;即在只有在必要的时候才会去 *** 作 DOM,通过减少低效的 *** 作,来提升性能
1)在 React 中,我们会把定义好的JSX 标记最终转换为一个虚拟的 DOM,存在内存里,这是因为内存的整体速度要比 *** 作 DOM 快的多
2) 如果用户做了对DOM 可能产生影响的 *** 作的时候,虚拟DOM 会把 *** 作前后的数据进行对比,如果 *** 作前后的数据有变化,就会把所有的变动然后统一 *** 作一次 DOM ,如果发现 *** 作前后的数据没有差异的话,就不会再去 *** 作 DOM了; 虚拟DOM 的思路就是不到万不得已就不去做低效的 DOM *** 作
3)虚拟DOM 有两大优势
1一个是 *** 作 DOM 前对数据进行对比,只有数据变化的时候才去 *** 作DOM
2它会整合 DOM *** 作,可以减少 DOM *** 作,提升性能
在当业务流程复杂的时候,我们就会发现单向数据流和组件化的组合方式会很大程度上降低问题的复杂度
在 React 里,我们可以把一切理解为 JS,这样 *** 作起来就少了很多束缚;另外组件提供的多种嵌套方式,数据驱动、生命周期等让开发变得更加顺畅
这离不开我们刚才的虚拟 DOM,它通过减少和优化 对DOM 的 *** 作,能在 React 在浏览器里有更好的性能表现
1 React 只是 视图层的一个框架,如果需要做其他事情,需要依赖它的生态系统;如处理单页面路由使用 Router,处理数据使用 Redux
2 变动频繁,经常不向前兼容
1 为什么需要虚拟DOM
先介绍浏览器加载一个网页需要经历那些过程;我们只讨论页面解析流程,不考虑网络请求过程。
浏览器内核拿到html文件后,大致分为一下5个步骤:
1 解析html元素,构建dom 树
2 解析CSS,生成页面css规则树(Style Rules)
3 将dom树 和 css规则树关联起来,生成render树
4 布局(layout/ reflow),浏览器会为Render树上的每个节点确定在屏幕上的尺寸、位置
5 绘制Render树,绘制页面像素信息到屏幕上,这个过程叫paint
当你用原生js 或jquery等库去 *** 作DOM时,浏览器会从构建DOM树开始讲整个流程执行一遍,所以频繁 *** 作DOM会引起不需要的计算,导致页面卡顿,影响用户体验。而Virtual DOM能很好的解决这个问题。它用javascript对象表示virtual node(VNode),根据VNode 计算出真实DOM需要做的最小变动,然后再 *** 作真实DOM节点,提高渲染效率。
2 Virtual DOM
虚拟DOM用javascript对象来表示VNode,VNode的结构如下:
虚拟节点(vNode)结构
下面是虚拟DOM的算法流程图:
虚拟DOM算法流程图
React Diff算法
高效的diff算法能够保证进行对实际的DOM进行最小的变动。但是 标准的的 Diff 算法 复杂度需要 O(n^3),这显然无法满足性能要求。要达到每次界面都可以整体刷新界面的目的,势必需要对算法进行优化。React里结合 Web 界面的特点做出了两个简单的假设,使得 Diff 算法复杂度直接降低到 O(n)。
1 两个相同组件产生类似的 DOM 结构,不同的组件产生不同的 DOM 结构;
2 对于同一层次的一组子节点,它们可以通过唯一的 id 进行区分。
算法上的优化是 React 整个界面 Render 的基础,保证了整体界面渲染的性能。
不同节点类型的比较
为了在树之间进行比较,我们首先要能够比较两个节点,在 React 中即比较两个虚拟 DOM 节点,当两个节点不同时,应该如何处理。这分为两种情况:(1)节点类型不同 ,(2)节点类型相同,但是属性不同。
节点类型不同:直接删除原节点, 插入新节点。
React 的 DOM Diff 算法实际上只会对树进行逐层比较,两棵树只会对同一层次的节点进行比较如下所述。
dom树
React 只会对相同颜色方框内的 DOM 节点进行比较,即同一个父节点下的所有子节点。当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个 DOM 树的比较。
相同类型节点的比较
React 会对属性进行重设从而实现节点的转换。
首先我们是用第一个表中竖列的信息填第二个表,所以要用函数vlookup来完成。现在工程B下面输入等于号“=”和函数“vlookup”,再输入括号“()”,再点击“工程B”,再输入“,”,这个逗号一定要是输入法在英文状态下的。
2,点击进入第一张有完整数据的表,选中A2:B10。
3,返回第二张表,我们可以看到,红笔画的地方名字不对,我们第一张表的名字是叫“表单一”而这里显示的是第二张表的名字,我们需要把这里的名字改成第一张表的名字。
4,这是我们把表的名字改后的情况,注意名字后面有感叹号“!”,这个感叹句也必须是英文的感叹号。
5,我们再输入“,2”,这里“2”的意思是我们在“表单一”的第二列取数据。
6,再输入“,0”。然后我们对函数进行固定,在“A”,“2”,“B”,“10”前面分别加上“$”
7,点击“enter”键后我们发现出现的不是几年几月几日,这是因为我们单元格的格式不对,先不要管格式。
8,使箭头变成十字架的形状再向右拉,进行填充。
9,点击鼠标右键,在出现的列表里选择“设置单元格格式”。
10,将单元格格式改为日期格式。
11,这就是我们改完格式之后的结果
为了获取真实的dom节点,文本输入框必须有一个 ref 属性,然后 thisrefs[refName] 就会返回这个真实的 DOM 节点。
var MyComponent = ReactcreateClass({
handleClick: function() {
thisrefsmyTextInputfocus();
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" />
<input type="button" value="Focus the text input" onClick={thishandleClick} />
</div>
);
}
});
ReactDOMrender(
<MyComponent />,
documentgetElementById('example')
);
存在一个数据量大的列表,如select、list,可能存在1000个数据项
传统做法是直接将1000个item渲染到页面上,即渲染1000个dom
从上图可以看出,可视区域范围内,仅有7个item,也就是说,不可见的993个元素的存在是浪费资源,从而导致渲染时占满内存、造成页面卡顿。
从这里我们可以入手优化这个list。
既然可视区域只有至多7个元素可见,那么就仅仅渲染这7个dom,不渲染剩下的993个dom。
我们需要计算出如下的数据:
我们可以用到 offsetHeight 属性,元素的可视高度
在此之前需要先定义一个滚动容器,并使用ref将其管理起来,并且设置容器高度 height ,和 overflow-y , 让它滚动起来。 这里为了组件的通用性,我们将 height 和 width 都通过父组件传入的方式获取。
这里我们需要先将第一个子元素渲染出来才能动态地获取它的高度, 同样,我们将列表通过props传入
用可视化区域的高度 除以 单个元素的高度,再向上取整,因为存在一些可见,但是不完全可见的元素,所以这里向上取整。
滚动容器中有个属性叫 scrollTop ,表示当前已滚动的高度,也就是上方不可见的区域。
我们用 scrollTop 除以单个元素的高度 domHeight ,再向下取整,即可得到当前可见区域第一个元素的下标。
至此我们可以将上边需要动态计算的几个变量存入state
下面开始渲染。
这里需要截取 listDom , 使用 slice() 方法。截取起始位置为 currentIndex ,结束为止为 currentIndex + domCount
在这里我们会发现,列表上只有7个元素,且无法滚动
我们还没有给元素绑定滚动事件。
这里我们将上述计算逻辑提到一个函数中进行,并将其绑定scroll
绑定事件后发现还是无法滚动
原来我们还需要给列表设置上下空白区域,撑开列表,不然无法模拟出来真是列表的高度。
我们继续计算上下空白的高度。
上空白的高度,我们已经计算出了当前可视区域第一个元素的下标了,也计算出了单个元素的站位高度那么
下空白的高度
整个列表的高度 - 可视区域的元素高度 - 上空白的高度
将这两个变量也存入state
然后再将上下两个空白元素添加到dom结构中占位,此时需要将计算 domHeight 的取值下标改为[1]
这里可以看到dom结构中只有几个可见的dom, 并且滑动时动态变化。
渲染效率也大大提高。
可以明显感觉到变快了
在这里我们渲染的时候事在对 listDom 在不断地增删、也就是在不断的增加、删除dom。
那么可不可以不增加删除、或者尽量少地增加删除dom实现我们的功能呢?
element_diff (相同层级的虚拟dom发生变化)
如滚动前 A B C D
滚动后 B C D E
如果不加key,则无法判断 B C D是否还是原来的B C D
对比时 发现A!=B,则会删除A,添加B,以此类推 删除B C D、插入 C D E
添加key后,发现B C D没有变化 ,仅仅需要删除A 插入E即可
索引:
当组件的state或者props发生改变的时候,自己的render函数就会重新执行。
注意:当父组件的render被执行的时候,子组件的也render会被重新执行一次(因为在父组件的render里面)。
也就是说当绑定的事件改变了state或者props,render函数就会重新执行解析页面,这个时候解析的时候就会使用新的数据了,所以页面就会变化。
刚才提到只要state、props改变就会重新render,可以想象要不断的重新渲染页面对性能要求非常高,实际上render的性能是非常高的,这是归功于虚拟DOM。
首先明确DOM的相关 *** 作需要调用web application对性能损耗是比较高的。
先看看常规的思路
改良思路(仍然使用DOM)
React的思路
Vue和react的虚拟DOM的原理和步骤是完全一致的。
React中真实DOM的生成步骤:JSX -> createElement方法 -> JS对象(虚拟DOM) -> 真实的DOM
因此可见,JSX中的div等标签仅仅是JSX的语法,并不是DOM,仅用于生成JS对象
其实在React中创建虚拟DOM(js对象)使用的是(没有JSX语法也可以用下面的方式生成)
虚拟DOM的优点:
React中ref的使用
组件挂载的过程
组件更新
组件去除的过程
以上就是关于React 初识—— 四大特点全部的内容,包括:React 初识—— 四大特点、虚拟DOM(Virtual DOM)的工作原理、React 如何合并2个虚拟 Dom求示例代码等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)