v-mode是封装了动态属性value和事件input的语法糖来实现的。
双向绑定的实现需要3个核心点
1.响应式
2.虚拟dom
vue2中的响应式式通过es5 提供的api object.definProperty来实现的,在vue实力化的时候通过这个api 我们给data对象的所有属性添加了getter/setter 方法,当数据发生改变的时候,相对应的setter会被触发,从而触发页面渲染
vue3中的响应式式通过es6提供的api Proxy代理来实现的,他拦截的是整个对象的所有属性变化,当数据发生改变的时候,Proxy的set捕获器会被触发,从而触发页面渲染
虚拟dom
虚拟DOM本质上是一个js对象,通过对象来表示真实的DOM结构。tag用来描述标签,props用来描述属性,children用来表示嵌套的层级关系
const vnode = {
tag: 'div',
props: {
id: 'container',
},
children: [
{
tag: 'div',
props: {
className: 'content',
},
text: 'This is a container'
}
]
}
//对应的真实DOM结构<div id="container"> <div class="content">
// This is a container </div></div>
虚拟dom 更新不会立即操dom而是通过diff算法对比出新旧dom的差异,然后在将差异渲染到真实dom中 从而也形成了异步渲染
当页面发生改变后虚拟dom 感知到了变换,将数据绑定到dom层的
diff算法:diff算法是前端常见的一种优化技术,它可在数据发生改变的时候计算出新旧虚拟dom的差异,从而达到最小的更新真实dom
diff 算法必须与key结合要不然性能不好
diff 算法是同层进行比较的不能跨层比较
diff算法是用虚拟dom 通过patch进行比较的
下面是实现diff的基本流程(1遍历新旧dom树,2 如果节点不同直接替换3.如何节点相同再比较节点属性是否有变化,如果有变化就更新节点属性 4如果节点有新的子节的递归执行1-3 5.如果旧的dom有子节,新dom 没有子节点 则需要删除子节点,6 如果旧的dom没有子几点,新的dom有字节的旧需要点击子节点
建立diff算法的关键是建立虚拟dom树,虚拟dom树可以js来表示真实的dom树,通过比对新旧dom树判断子节点是否需要删除或者添加节点以上是一个简单的diff算法流程,具体实现还需要考虑特殊情况处理,如key,组件化,异步跟新等)
patch(oldvnode,newvnode) 通过isSameVnode 判断是否是同一个标签是的话直接替换,不是的话调用patchVnode方法判断oldvnode和newvnode是否相等,相同直接返回,不同分情况处理
第一种情况:oldvnode和newvnode都有文本节点,直接用新的文本节点换掉旧的文本节点
第二种情况: oldvnode没有子节点 newvnode 有子节点,添加新的节点
第三种情况:oldvnode有子节点newvnode没有子节点,删除旧的节点
第四种情况:oldvnode和newvnode 都有子节点,直接调用updatechildren
updatechildren中的比较
双端交叉指针 新老dom都有两个指针 分别是队头与队头,队尾和队尾,队头与队尾, 队尾与队头
就跟x一样对比4次,如果寻找4次 找到与我元素的key相同,就会去移动元素的位置。如何4中情况都没有匹配,就会在老vdom查找元素如果找到了进行复用,没找到就进行创建
在移动结束后如过新dom有剩余就需要批量添加,如果旧dom有剩余就进行批量删除
v3.0 双端快速diff 分别是队头与队头 队尾与队尾 如果匹配上,跟vue2一样如果没有匹配上就会对新的vdom 进行最长递增子序列 计算,说白就是在新的vdom中寻找依次递增的元素有这些元素的顺序就是固定的然后去寻找不在这些列表里面的元素和老的vdom 进行对比,
diff 算法的前提是同层级和同类 型的节点 那核心就是循环中的diff算法