1 .由于历史或者标准原因,也有可能是继承的关系,即使是一个简单的div元素,他的属性都会超过1百个,所以操作dom的时候所涉及到的性能问题,其实很严重,稍有不慎,就会导致页面元素的重排。
2 .dom上的结构,常用的属性,我们都可以使用对象结构简单的表示出来,而且原生js操作对象的速度非常快,而且简单。
3 .原生dom树-js对象-构建新的dom树。
4 .主要涉及到两个转化的问题,还有一个虚拟dom的diff优化。
5 .虚拟dom是真实dom的简单印射,对于一个真实的dom,我们只需要知道他的选择器,他的子节点,以及他的数据(属性,样式,类,事件等)我们就可以利用一个对象来描述这个真实的节点。
6 .用属性描述真实dom的各个特性,当他发生变化的时候,就会修改视图。
7 .以vnode节点模拟真实dom,对这颗抽象树进行创建节点,删除节点等操作,在这个过程中是不需要操作真实dom的,修改以后经过diff算法得出一些需要修改的最小单位,再将这些自小单位的视图进行更新,缉拿少许多不必要的dom操作。
8 .
diff原理
1 .状态变更,重新渲染了js对象结构,先对比js对象结构的位置,对新渲染的和就的树进行对比,记录两棵树的差异,记录下的不同就是我们需要真正的dom操作,然后把他们应用在真正的dom树上,页面发生变更。目的:视图的结构是整个全新渲染,但是最后的操作dom的时候只是变了不同的部分。
2 .这个他比较的地方是在用js生成的虚拟树上进行比较。
3 .在js和dom之间做一个缓存,可以类比cpu和硬盘,既然硬盘这么慢,那就在他们之间加个缓存,js只是操作虚拟dom,最后时间再把变更写入硬盘DOM
1 .用js结构表示dom数的结构;然后用这个树构建一个正真的dom树,插到文档里面去。
2 .当状态变更的时候,重新构造一棵新的对象树,然后用新的树和旧的树进行比较,记录两棵树的差异
3 .把2记录的差异用用到步骤1所构建的真正的dom树上,视图就完美更新了
构建js对象的部分,基于vue
1 .如何在把html代码转成js对象的时候就提前做好布局,比较的时候不需要生成dom树,只是来比较生成的前后数据有什么不一样不就可以了么,只需要改变数据发生变化的那一部分
2 .关键就是如何把渲染的数据和实际生成的dom一一对应起来,然后改的时候直接找索引就可以了。
3 .原本vue的vnode对象
4 .canvas防爬虫,为什么会有这么丧心病狂的方法
Vnode
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
functionalContext: Component | void; // only for functional component root nodes
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
constructor (
tag?: string,
data?: VNodeData,
children?: ?Array<VNode>,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions
) {
/*当前节点的标签名*/
this.tag = tag
/*当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型,可以参考VNodeData类型中的数据信息*/
this.data = data
/*当前节点的子节点,是一个数组*/
this.children = children
/*当前节点的文本*/
this.text = text
/*当前虚拟节点对应的真实dom节点*/
this.elm = elm
/*当前节点的名字空间*/
this.ns = undefined
/*编译作用域*/
this.context = context
/*函数化组件作用域*/
this.functionalContext = undefined
/*节点的key属性,被当作节点的标志,用以优化*/
this.key = data && data.key
/*组件的option选项*/
this.componentOptions = componentOptions
/*当前节点对应的组件的实例*/
this.componentInstance = undefined
/*当前节点的父节点*/
this.parent = undefined
/*简而言之就是是否为原生HTML或只是普通文本,innerHTML的时候为true,textContent的时候为false*/
this.raw = false
/*静态节点标志*/
this.isStatic = false
/*是否作为跟节点插入*/
this.isRootInsert = true
/*是否为注释节点*/
this.isComment = false
/*是否为克隆节点*/
this.isCloned = false
/*是否有v-once指令*/
this.isOnce = false
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next https://github.com/answershuto/learnVue*/
get child (): Component | void {
return this.componentInstance
}
}
1 .以上是一个最基础的Vnode节点,可以作为其他派生类的基类
虚拟dom的diff
1 .前提:diff只发生在相同的层级,跨越层级的dom添加,删除,并不常见,一般都是兄弟之间的位置发生变化
2 .只会简单考虑同层节点的位置变换,对于不同层的节点,只有简单的创建和删除
判断sameVnode
1 .如果两个节点是sameNode,则保留旧的真实dom并对新旧虚拟节点的children进行diff,否则就删掉旧的虚拟节点对应的真是dom并替换为新虚拟节点对应的真实dom
2 .sameNode相同的条件:选择器相同,key相同,如果是input元素,还需要type类型相同,tag相同,isComment是否为注释节点,
3 .新旧虚拟节点children的diff是整个虚拟diff算法的核心,主要分为以下几种特殊情况。
1 .新虚拟节点有children,旧的虚拟dom没有children,可能有一个文字节点
1.1:清除文字节点,然后将新的虚拟节点children对应的真实节点添加到父节点
2.新虚拟dom没有children,旧的虚拟dom有chidren
2.1:直接删掉
3.旧虚拟dom text属性有定义,新虚拟节点text属性没定义而且没有children
3.1:删除旧节点的文本内容即可
4.新旧节点text属性有定义
4.1: 比较文本内容,不同则替换
5.新旧虚拟dom子节点都有children,比较chidlren
2 .各种虚拟dom算法的差别主要在于updateChildren方法实现的不同,snabbdom算法的特点是给新旧children分别提供了头尾两个指针,diff的过程中头尾指针分别向中间靠拢,当任一children的头指针超过尾指针的时候则diff结束
1.其实这个就是比较和更新观察json的方式。
2.有顺序的object怎么写
3.在遍历中,如果存在key,并且满足sameVnode,会将该dom节点进行复用,否则会创建一个新的dom节点
4.比较完之后,删除或者增加新的节点元素。
5.节点类型不同(这个单纯数据变化的时候应该不会有这个问题吧),这些操作都是出现在react中多一点,甚至是编写代码的时候多一点,实际上用户在操作这个网页的时候,是很少会出现这样的问题的,所以这一块的代码优先级好像没这么高吧。他的ui是完全基于状态来每次render整个界面而无需担心性能的问题。
6.节点类型相同,节点的属性不同。对属性进行重设而实现节点的转换。
3 .列表的增删改查这个要注意一下。