facebook研发react的初衷是简化前端开发,防止xss攻击,通过虚拟dom规避风险(因为vdom内部默认配置的xss防御的选项)
其次:那就是在进行更新的时候,需要去操作 DOM,传统 DOM API 细节太多,操作复杂,所以就很容易出现 Bug,而且代码难以维护。
**vdom的工作原理:**是JavaScript按照DOM的结构来创建的虚拟树型结构对象,是对DOM的抽象,比DOM更加轻量型。使用虚拟DOM改变了当前的状态不需要立即的去更新DOM 而且更新的内容进行更新,对于没有改变的内容不做任何操作,通过前后两次差异进行比较。
Vue 里面的 虚拟DOM 当初是借鉴了 snabbdom:
在 snabbdom 使用了多次的 h 函数,主要作用是创建 虚拟节点,他说起来也不是递归,像是一种嵌套,生成了虚拟DOM** 树。
h函数的实现?
大家都知道js 函数执行,当然是先执行最里面的 函数
1.h('li', {}, '我是一个li')第一个执行 返回的 {sel,data,children,text,elm} 连续三个 li 都是这个
2.接着就是 h('ul', {}, []) 进入到了第二个判断是否为数组,然后 把每一项 进行判断是否对象 和 有sel 属性,然后添加到 children 里面又返回了出去 {sel,data,children,text,elm}
3.第三就是执行 h('div', {},h()) 了, 第三个参数 直接是 h()函数 = {sel,data,children,text,elm} ,他的 children 把他用 [ ] 包起来
再返回给 vnode
/* index.js */
import h from './my-snabbdom/h'
let vnode = h('div', {},
h('ul', {}, [
h('li', {}, '我是一个li'),
h('li', {}, '我是一个li'),
h('li', {}, '我是一个li'),
),
])
console.log(vnode)
优势:1.性能优越
2.规避xss
3.可跨平台
以上为错误示范。
操作虚拟dom一定比真实的dom更高效吗?
不一定,在首次渲染时,或者只进行一处节点的更改,远不如真实dom更快。
优势:
大量的直接操作DOM容易引起网页性能下降这时React基于虚拟DOM的diff 处理与批处理操作
可降低DOM的操作范围与频次,提升页面性能。
规避xss,较低的成本进行可跨平台开发。
虚拟DOM一定可以规避XSS吗?
虚拟DOM内部确保字符转义,确实可做到这点,但 React存在风险,因为React 留有dangerouslySetlnnerHTML API绕过转义。
没有虚拟 DOM不能实现跨平台吗?
比如 NativeScript没有虚拟DOM层,它是通过提供兼容原生API的JS API实现跨平台开发
虚拟DOM的跨平台优势在哪里?
跨平台的成本更低
在React Native后,前端社区从虚拟DOM中体会到跨平台的无限前景,所以在后续发展中,都借鉴虚拟DOM。(比如社区流行的小程序同构方案,会提供类似vdom的表述对象来支撑多端转换)
虚拟dom有什么缺陷?
1.内存占用较高,它包含当前网页真实dom的全部完整信息。(空间上))
2.无法进行极致优化,虽然能应对绝大多数要求,但在一些性能高的应用中,vdom无法进行针对性的极致优化。比如实现类似ghrome
3.首次渲染大量DOM时,由于多了一层DOM计算,会比innerHTML插入慢。(速度上)
除渲染页面,虚拟DOM还有哪些应用场景?
比如,只要记录真实DOM变更,它甚至可应用于埋点统计与数据记录等 ,比如:(回放录制库 rrweb)
基于 rrweb 可以方便地帮助我们实现录屏回放功能:
vdom在vue和react中有什么区别
一个diff 树,一个diff 数据
在 Vue 里面就是叫做 patch ,它的核心就是参考 Snabbdom,通过新旧虚拟 DOM 对比(即 patch 过程),找出最小变化的地方转为进行 DOM 操作。
它是在什么时候执行的呢?
在页面首次渲染的时候会调用一次 patch 并创建新的 vnode,不会进行更深层次的比较。
然后是在组件中数据发生变化时,会触发 setter 然后通过 Notify 通知 Watcher,对应的 Watcher 会通知更新并执行更新函数,它会执行 render 函数获取新的虚拟 DOM,然后执行 patch 对比上次渲染结果的老的虚拟 DOM,并计算出最小的变化,然后再去根据这个最小的变化去更新真实的 DOM,也就是视图
它是怎么计算的?
这会有什么问题呢?
可是这样会有很大问题,假如有1000个节点,就需要计算 1000³ 次,也就是10亿次,这样是无法让人接受的,所以 Vue 或者 React 里使用 Diff 算法的时候都遵循深度优先,同层比较的策略做了一些优化,来计算出最小变化
Diff 算法的优化? 从n3 =》 n
1. 只比较同一层级,不跨级比较
2. 比较标签名 如果同一层级的比较标签名不同,就直接移除老的虚拟 DOM 对应的节点
3. 比较 key 如果标签名相同,key 也相同,就会认为是相同节点,也不继续按这个树状结构做深度比较,比如我们写 v-for 的时候会比较 key,不写 key 就会报错,这也就是因为 Diff 算法需要比较 key。
进行新旧节点比较
主要流程是这样的:
用来判断是不是同一节点的函数
这个是在新的 vnode 和 oldVnode 是同一节点的情况下,才会执行的函数,主要是对比节点文本变化或子节点变化
这个是新的 vnode 和 oldVnode 都有子节点,且子节点不一样的时候进行对比子节点的函数
循环遍历两个列表,循环停止条件是:其中一个列表的开始指针 startIdx 和 结束指针 endIdx 重合
循环内容是:{
为什么会有头对尾,尾对头的操作?
因为可以快速检测出 reverse 操作,加快 Diff 效率
Vue3 的优化?
尤大公布的数据就是 update 性能提升了 1.3~2 倍,ssr 性能提升了 2~3 倍,
最长递增的理解?
在 Vue3 里 patchKeyedChildren 为
看个例子,比如
[ a, b, c, d, e, f, g ][ a, b, f, c, d, e, h, g ][ a, b ][ g ][ f, c, d, e, h ],并通过 newIndexToOldIndexMap 拿到在数组里对应的下标,生成数组 [ 5, 2, 3, 4, -1 ],-1 是老数组里没有的就说明是新增[ 2, 3, 4 ] 对应的节点 [ c, d, e ][ c, d, e ] 的位置进行移动/新增/删除就可以了使用最长递增子序列可以最大程度的减少 DOM 的移动,达到最少的 DOM 操作
为什么不采用广度有效遍历算法?
广度优先遍历会导致组件的声明周期失序错乱,深度可以解决。
reactdiff过程?
总个来说,React Diff 算法分以下几个步骤:
如何根据reactdiff算法的原理优化代码?
vue和react的diff有什么差异?
fiber是什么?
在 React15 以前 React 的组件更新创建虚拟 DOM 和 Diff 的过程是不可中断,如果需要更新组件树层级非常深的话,在 Diff 的过程会非常占用浏览器的线程,如果 JavaScript 的代码运行时间过长则会造成页面卡顿。
基于以上原因 React 团队在 React16 之后就改写了整个架构,将原来数组结构的虚拟DOM,改成叫 Fiber 的一种数据结构。可以实现由原来不可中断的更新过程变成异步的可中断的更新。
fiber对diff做了那种优化策略?
具体:
组件在经过 JSX 的编译之后,初始化的时候会生成成一个类似于 React 15 或者 Vue 那种虚拟 DOM 的数据结构。然后创建一个叫 fiberRoot 的 Fiber 节点,然后开始从 fiberRoot 这个根 Fiber 开始进行协调,生成一棵 Fiber 树,这个棵树被称为:workInProgress Fiber 树 ,意思是正在工作的 Fiber 树。
在组件状态数据发生变更的时候,会根据最新的状态数据先会生成新的虚拟DOM,再去构建一棵新的 workInProgress Fiber 树 ,而在重新协调构建新的 Fiber 树的过程也就是 React Diff 发生的地方。
为什么 Vue 中不需要使用 Fiber?
对于这个问题其实尤雨溪也在英文社区里回答过,也有前端大牛翻译发布在公众号上,那么下面我也进行一下总结。
第一,首先时间分片是为了解决 CPU 进行大量计算的问题,因为 React 本身架构的问题,在默认的情况下更新会进行过多的计算,就算使用 React 提供的性能优化 API,进行设置,也会因为开发者本身的问题,依然可能存在过多计算的问题。
第二,而 Vue 通过响应式依赖跟踪,在默认的情况下可以做到只进行组件树级别的更新计算,而默认下 React 是做不到的(据说 React 已经在进行这方面的优化工作了),再者 Vue 是通过 template 进行编译的,可以在编译的时候进行非常好的性能优化,比如对静态节点进行静态节点提升的优化处理,而通过 JSX 进行编译的 React 是做不到的。
第三,React 为了解决更新的时候进行过多计算的问题引入了时间分片,但同时又带来了额外的计算开销,就是任务协调的计算,虽然 React 也使用最小堆等的算法进行优化,但相对 Vue 还是多了额外的性能开销,因为 Vue 没有时间分片,所以没有这方面的性能担忧。
第四,根据研究表明,人类的肉眼对 100 毫秒以内的时间并不敏感,所以时间分片只对于处理超过 100 毫秒以上的计算才有很好的收益,而 Vue 的更新计算是很少出现 100 毫秒以上的计算的,所以 Vue 引入时间分片的收益并不划算。