1.react的虚拟DOM diff
react主要通过使用高效diff算法、进行batch操作、摒弃脏检测更新方式来保证虚拟DOM diff算法和更新都能高效。
任何一个组件使用setState方法时,会被认为变‘脏’了,进而触发组件本身的重新渲染。因为react始终维护两套虚拟DOM,一套是更新后的,另一套是前一个状态的,可以通过对两套执行diff算法,找到需要的最小单元集,并应用在真实的DOM中。
react的大胆假设:
- 对DOM节点跨层级移动的情况忽略不计。
- 拥有相同类型的两个组件生成相似的树形结构,拥有不同类型的两个组件生成不同的树形结构。
根据这些假设,采取策略:
- 对组件树进行分层比较,两棵树只会对同一层级的节点进行比较,可以用key属性来声明同一层级节点的更新方式。
- 不同的组件类型,直接将整个组件替换为更新后的组件。
- 相同的组件类型,如果组件的state或props发生变化,则直接重新渲染组件本身。
setState方法会引发“蝴蝶效应”,通过diff算法找到更新的最小单元集,但是这些变更并不会立即同步生效,会执行setState的合并操作,积攒归并变化后再统一进行更新。
规避不必要的重新渲染:
- 使用shouldComponentUpdate函数对比前后状态是否出现了变更来决定是否重新渲染。
- 对于无状态组件返回相同的element实例,react会认为组件并没有发生变化,相当于使用lodash的memoize函数。
- pureComponent相比于一般组件会先进行浅比较前后的state和props。
设计亮点:
- 事件:将所有事件挂载到document节点上,利用事件代理实现优化;采用合成事件,在原生事件基础上包装,并结合池化思路实现内存保护。
- setState:异步合并设计优化了渲染性能。
- fiber:将耗时高、易阻塞的长任务进行切片,分成子任务,并异步执行。
2.Vue动静结合DOM diff
之所以能预编译优化,是因为vue可以静态解析模板:利用正则表达式顺序解析模板,当解析到开始标签、闭合标签和文本时就会分别执行对应的回调,来达到构造AST的目的。
在进行预编译优化时,vue需要做数据双向绑定,进行数据拦截或代理,所以需要在预编译阶段静态分析模板,分析出视图依赖了哪些数据进行响应式处理;而react只需要进行局部重新渲染,它所负责的就是一堆递归React.createElement的执行调用,无法从模板层面进行静态分析。但在react框架之外,可以通过工程化手段达到类似的目的,开发babel插件以便将JSX编译。