手写现代前端框架diff算法-前端面试进阶
前言
在前端工程上,日益复杂的今天,性能优化已经成为必不可少的环境。前端需要从每一个细节的问题去优化。那么如何更优,当然与他的如何怎么实现的有关。比如key为什么不能使用index呢?为什么不使用随机数呢?答案当然是影响性能,那为什么?相信你看完本文的diff算法就能略懂一些。
diff算法的概念
diff算法, 是 Virtual DOM 产生的一个概念, 作用是用来计算出 Virtual DOM 中被改变的部分,然后根据算法算出dom的结果进行原生DOM操作,而不用重新渲染整个页面,从而提高了页面渲染效率,已经成为当今框架(vue,react)必不可少的部分。
手写diff算法的过程
背景:dom对性能的消耗特别高,因此前辈们提出用js对象模拟dom的操作,计算出最后需要更新的部分。而dom本身的算法的时间复杂度是O(n ^ 3)。这时react团队,提出了diff算法!(本案例提供核心代码,以及完整案例)
简单理解版本的思路的核心,可分为三个步骤:
1.模拟"dom树",将dom转换为js数组。
定义js构造函数,可同步dom对象。通常对象可由下边组成:tag('string'):标签的名称props('object'):属性与属性的值{ class: 'a', type: 'hidden'}children('array'):子属性key('string'):表示元素的唯一标识 'nowKeys'
2.获取两个dom数之间的差异(diff算法)
对比两个dom对应的实体,获取他们的不同点,根据顺序数组。当前demo处理了以下方法:
Change: 'Change',//表示元素有变化Move: 'Move',//表示移动了位置Add: 'Add',//表示元素是新增的Del: 'Del',//表示元素给删除了DiffPropsList: 'DiffPropsList',//表示元素对应的属性列表有变动DelProps: 'DelProps',//表示该属性给删除ChangeProps: 'ChangeProps',//表示该属性有变化AddProps: 'AddProps',//表示该属性是新增的
3.将“差异”进行“渲染”
根据步骤2),将差异进行对应的处理
实例方法如下:
var a1 =new WzElement('div', { class: 'a1Class' }, ['a1'], "a1");
var a2 =new WzElement('div', { class: 'a2Class' }, ['a2'], "a2")
let root = a1.render();//js模拟dom生成
步骤2)
let pathchs = diff(a1, a2); //获取当前的两个dom的差异
步骤3)
reloadDom(root, pathchs);//根据差异重新渲染
核心的代码(步骤1):
_createDom( tag, props, children, key ){let dom = document.createElement( tag );for( let propKey in props ){dom.setAttribute( propKey, props[propKey] );}if( !key ){dom.setAttribute( "key", key );}children.forEach( item => {if( item instanceof WzElement ){//var root = this._createDom(item.tag,item.props,item.children,item.key)dom.appendChild( root ); }else{var childNode = document.createTextNode( item );dom.appendChild( childNode ); }});return dom;}
核心的代码(步骤2):参考 前端进阶面试题详细解答
//判断当前对象
function dfs(oldElement, newElement, index, patches) {//如果新的对象为空,无需要对比//如果新的对象,key跟tag都不同,说明元素变了,直接替换。//如果新的对象,key跟tag都相同,则遍历子集,观察子集是否不同,观察元素属性是否不同var curPatches = [];if (!newElement) {} else if (oldElement.key != newElement.key || oldElement.tag != newElement.tag) {curPatches.push({type: stateType.Change,node: newElement});} else {var propsDiff = diffProps(oldElement.props, newElement.props);if (propsDiff.length > 0) {curPatches.push({type: stateType.DiffPropsList,node: newElement});}diffChildren(oldElement.children, newElement.children,index, patches);//对比子集是否不同}if (curPatches.length > 0) {if (!patches[index]) {patches[index] = []}patches[index] = patches[index].concat(curPatches);}return patches;
}//对比子集是否不同
function diffChildren(oldChild, newChild, index, patches) {let { changeList, resultList } = listDiff(oldChild, newChild, index, patches);if (changeList.length > 0) {if (!patches[index]) {patches[index] = []}patches[index] = patches[index].concat(changeList);}let last = null;oldChild && oldChild.forEach((item, i) => {let child = item && item.children;if (child) {if ( last && last.children != null) {//有子节点index = index + last.children.length + 1;} else {index += 1;}let keyIndex = resultList.indexOf( item.key ) ;let node = newChild[keyIndex]//只遍历新旧中都存在的节点,其他新增或者删除的没必要遍历if ( node ) {dfs(item, node, index, patches)}} else {index += 1;}last = item});
}
核心的代码(步骤3):
var num = 0;
//根据virtDom的结果渲染页面
function reloadDom( node, patchs ){var changes = patchs[ num ];let childNodes = node && node.childNodes;if (!childNodes) num += 1if( changes != null ){changeDom( node, changes );}//保持更diff算法的num一直var last = nullchildNodes && childNodes.forEach(( item, i ) => {if( childNodes ){if ( last && last.children != null) {//有子节点num = num + last.children.length + 1;} else {num += 1;}}reloadDom( item, patchs );last = item;})
}//执行每个dom的变化
function changeDom( node, changes ){changes && changes.forEach( change => {let {type} = change;switch( type ){case stateType.Change:node.parentNode && node.parentNode.replaceChild( change.node.create() , node );break;case stateType.Move:let fromNode = node.childNodes[change.from];let toNode = node.childNodes[change.to];let formClone = fromNode.cloneNode(true);let toClone = toNode.cloneNode(true);node.replaceChild( fromNode, toClone ) ;node.replaceChild( toNode, formClone ) ;break;case stateType.Add:node.insertBefore( change.node.create(), node.childNodes[ change.index ] )break;case stateType.Del:node.childNodes[change.index ].remove();break;case stateType.DiffPropsList:let {props} = change.node;for( let key in props ){if( key == stateType.DelProps ){node.removeAttribute( );} else {node.setAttribute( key, props[key] );}}break;default:break;}});
}
相关文章:
手写现代前端框架diff算法-前端面试进阶
前言 在前端工程上,日益复杂的今天,性能优化已经成为必不可少的环境。前端需要从每一个细节的问题去优化。那么如何更优,当然与他的如何怎么实现的有关。比如key为什么不能使用index呢?为什么不使用随机数呢?答案当然…...
【半监督医学图像分割 2022 MICCAI】CLLE 论文翻译
文章目录【半监督医学图像分割 2022 MICCAI】CLLE 论文翻译摘要1. 简介2. 方法2.1 半监督框架概述2.2 监督局部对比学习2.3 下采样和块划分3. 实验4. 结论【半监督医学图像分割 2022 MICCAI】CLLE 论文翻译 论文题目:Semi-supervised Contrastive Learning for Labe…...
vivo官网App模块化开发方案-ModularDevTool
作者:vivo 互联网客户端团队- Wang Zhenyu 本文主要讲述了Android客户端模块化开发的痛点及解决方案,详细讲解了方案的实现思路和具体实现方法。 说明:本工具基于vivo互联网客户端团队内部开源的编译管理工具开发。 一、背景 现在客户端的业…...
Python基础-数据类型之数字类型
变量中的变量值是用来存储事物状态的,事物的状态分成不同的种类(例如:人的姓名、年龄,身高、职位、工资等),因此变量值有多种不同的数据类型。 age 18 # 用整型记录年龄 salary 3.1 # 用浮点型记录…...
基于Web的6个完美3D图形WebGL库
现代前端、游戏和Web开发正是WebGL可以转化为数字杰作的东西。使用GPU绘制在浏览器屏幕上生成的矢量元素,WebGL创建交互式Web图形,从而获得用户体验。视觉元素的质量和复杂性使该工具在HTML或CSS等其他方法中脱颖而出。WebGL基础WebGL不是一个图形套件。…...
界面组件DevExpress Reporting v22.2 - 增强的Web报表组件UI
DevExpress Reporting是.NET Framework下功能完善的报表平台,它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集,包括数据透视表、图表,因此您可以构建无与伦比、信息清晰的报表。DevExpress Reporting v22.2版本已正式发布&…...
初学vector
目录 string的收尾 拷贝构造的现代写法: 浅拷贝: 拷贝构造的现代写法: swap函数: 内置类型有拷贝构造和赋值重载吗? 完善拷贝构造的现代写法: 赋值重载的现代写法: 更精简的现代写法&…...
Windows10 安装wsl2、Ubuntu相关操作
Windows10 安装wsl2、Ubuntu相关操作 安装wsl2 查看本机windows版本: 键盘上按下winr,输入winver,查看系统版本。必须运行 windows 10 版本 2004 及更高版本(内部版本 19041 及更高版本)或 windows 11。满足版本要求后…...
SpringBoot简单使用MongoDB
MongoDB介绍 SpringBoot简单使用MongoDB 一、配置步骤 1、application.yml 2、pom 3、entity 4、mapper 二、案例代码使用 1、库 前期准备上一篇安装MongoDB地址http://t.csdn.cn/G4oYJ 跟关系型数据库概念对比 Mysql MongoDB Database(数据库) Datab…...
Oracle Data Guard 角色转换(Role Transitions)
查询视图V$DATABASE的DATABASE_ROLE列可以看到数据库当前的角色。 1.角色转换介绍 Oracle Data Guard让你可以使用SQL语句或者通过Oracle Data Guard broker界面来动态更改数据库的角色,Oracle Data Guard支持以下的角色转换: 1࿰…...
opencv的TrackBar控件
大家好,我是csdn的博主:lqj_本人 这是我的个人博客主页: lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…...
关于基线长度对双天线GNSS测姿精度的影响
文章目录一、GNSS测姿原理1. 载波相位双差求解基线向量2. GNSS姿态角表示二、基线长度对GNSS测姿精度的影响三、GNSS定向产品精度描述实例四、参考文献在GNSS定向模块或者板卡的指标参数中,我们一般会看到航向的测量精度和基线的长度相关。在实际使用,用…...
口交换机睿易 RG-NBS1826GC 24 口
接口形态不将就,标配光纤接口传输性能不将就,标配千兆上联口和大缓存设计端口数量不将就,8/16/24 三种选择楼宇对讲交换机不将就,保证开锁指令品质服务不将就,监控专用交换机接口形态不将就,标配光纤接口非…...
如何在Excel中向下拉列表中添加条件
在Excel中向下拉列表中添加条件 创建矩阵型数据集创建下拉列表创建第一个下拉列表创建第二个下拉列表你可以使用Microsoft Excel下拉列表来显示一个简单的列表,尽管有时需要更多的控制。假设你的人员分散在四个地区:北部、南部、东部和西部。你希望按地区与人员合作,而不是与…...
自定义bean 加载到spring IOC容器中
自定义bean加载到spring容器中的两种方式: 1.在类上添加注解Controller、RestController(本质是Controller)、Service、Repository、Component2.使用Configuration和Bean 这篇文章主要介绍第二种方式原理(因为在实际使用中&#…...
[python入门㊻] - python装饰器和类的装饰器
目录 ❤ python装饰器介绍 ❤ 什么是装饰器 ❤ 装饰器的流程 ❤ 定义装饰器时通常会涉及以下3个函数 无参装饰器 有参装饰器 多重装饰器 ❤ 装饰器的用法(闭包) ❤ 装饰器语法糖 ❤ 时间计时器 ❤ 装饰器中wraps作用 不使用wraps装饰器 使用wraps装饰器解…...
企业级信息系统开发学习1.1 初识Spring——采用Spring配置文件管理Bean
文章目录一、Spring容器演示——采用Spring配置文件管理Bean(一)创建Maven项目(二)添加Spring依赖(三)创建杀龙任务类(四)创建勇敢骑士类(五)采用传统方式让勇…...
CSS盒子模型
盒子模型 CSS三大特性 继承性、层叠性、优先级 优先级比较 继承 < 通配符选择器 < 标签选择器 < 类选择器 < id选择器 < 行内样式 < !important 注意:!important不能提升继承的优先级,只要是继承优先级最低 复合选择器权重叠加计…...
Python基础学习笔记 —— 数据结构与算法
数据结构与算法1 数据结构基础1.1 数组1.2 链表1.3 队列1.4 栈1.5 二叉树2 排序算法2.1 冒泡排序2.2 快速排序2.3 (简单)选择排序2.4 堆排序2.5 (直接)插入排序3 查找3.1 二分查找1 数据结构基础 本章所需相关基础知识:…...
笔记本连接wifi,浏览器访问页面,显示访问被拒绝
打开chrome、edge浏览器访问第1个第2个页面正常,后面再打开页面显示异常。 但手机连接正常,笔记本连接异常,起初完全没有怀疑是wifi问题 以为用了vpn软件问题,认为中了病毒。杀毒,并没有中毒。 1、关闭vpn代理&#…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
