回敬尾巴是阿里的某资深:手撸一个基于html字符串模板的Virtual DOM

广告位招租
扫码页面底部二维码联系

写完《【转载请注明来源】HTMLStringParser:自己撸著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。一个Virtual DOM之前》之后,我第一时间整理代码,把文章转载到未经授权,禁止复制转载。【原创内容,转载请注明出处】掘金上,满足作为宅逼程序猿的虚荣感。但写转载请注明出处:www.tangshuang.net原创内容,盗版必究。完HTMLStringPareser之后本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。,我并不满足,既然都已经实现了把html本文作者:唐霜,转载请注明出处。【未经授权禁止转载】转换为对应的js对象结构了,而且连cre【转载请注明来源】【版权所有】唐霜 www.tangshuang.netateElement都写了,为何不更进一【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】步,把整个Virtual DOM也给实现【本文首发于唐霜的博客】【原创不易,请尊重版权】了呢?于是开始手撸。这一下,把自己给摔进本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。坑里,在实现diff的时候,几乎陷入了绝【关注微信公众号:wwwtangshuangnet】【版权所有】唐霜 www.tangshuang.net境。最后,在无法实现的前提下,做了妥协,本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net做了不严谨处理,最后才终于撸完了整个Vi著作权归作者所有,禁止商业用途转载。【作者:唐霜】rtual DOM,代码在这里著作权归作者所有,禁止商业用途转载。,你可以自己慢慢拍砖。【未经授权禁止转载】

【作者:唐霜】【本文受版权保护】【版权所有】唐霜 www.tangshuang.net【未经授权禁止转载】【版权所有】唐霜 www.tangshuang.net

在文章发表在掘金之后,我就开始去写代码了未经授权,禁止复制转载。【原创内容,转载请注明出处】,但是过了两天回去看掘金的时候,有几条评【本文首发于唐霜的博客】【转载请注明来源】论,还挺兴奋,一看,一位带着“@阿里巴巴著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。”尾巴的资深x师竟然嘲讽到说“从HTML【本文受版权保护】【版权所有】唐霜 www.tangshuang.net转vdom这个实践不行,自己写写玩玩吧”未经授权,禁止复制转载。【关注微信公众号:wwwtangshuangnet】,我就怒了,这口气,太拽了吧,也不知道是【未经授权禁止转载】【本文受版权保护】不是随便说的。我写HTMLStringP【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.netarser就是要解决自己抓取网页,获取某【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。个节点数据的实际问题,写完了就解决了,怎【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。么是玩玩呢?再说了,vue用的不也是ht【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】ml字符串模板么?所以,我打算写一篇文章原创内容,盗版必究。【未经授权禁止转载】,把自己完整撸完Virtual DOM的著作权归作者所有,禁止商业用途转载。本文版权归作者所有,未经授权不得转载。这个过程写下来,实现的肯定不如React本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】,但是从整个功能而已,是完整的,说明上面转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net这位资深的说法不对,不仅可行,而且说不定本文版权归作者所有,未经授权不得转载。【转载请注明来源】以后我还会用到自己的开发中,不是玩玩。

本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】

尾巴是@阿里巴巴的某资深的嘲笑转载请注明出处:www.tangshuang.net

原创内容,盗版必究。【版权所有,侵权必究】转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net

整体思路【本文受版权保护】

未经授权,禁止复制转载。【转载请注明来源】【未经授权禁止转载】【未经授权禁止转载】本文版权归作者所有,未经授权不得转载。

我已经写关于Virtual DOM的文章【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。好几篇了,虽然不一定对所有细节都非常专业【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】,但是自认为整个逻辑是搞清楚了的。实现一【未经授权禁止转载】原创内容,盗版必究。个Virtual DOM不是把真实DOM【原创不易,请尊重版权】【作者:唐霜】结构转换为一个js对象就完事了,一个Vi著作权归作者所有,禁止商业用途转载。【原创不易,请尊重版权】rtual DOM还要包括把这个js对象本文作者:唐霜,转载请注明出处。【作者:唐霜】渲染回真实DOM,接收数据变化,数据变化【版权所有,侵权必究】著作权归作者所有,禁止商业用途转载。时做diff,知道哪些节点会发生变化,做【本文受版权保护】【原创内容,转载请注明出处】patch去更新真实DOM。所以,我的整【原创不易,请尊重版权】未经授权,禁止复制转载。体思路,就是写一个VirtualDOM的转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。Class,这个Class包含:

【本文首发于唐霜的博客】【未经授权禁止转载】【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。
  • createVritualDOM:从ht【版权所有,侵权必究】【原创不易,请尊重版权】ml字符串模板抽象出js对象即Virtu转载请注明出处:www.tangshuang.net【原创内容,转载请注明出处】al DOM
  • 【原创不易,请尊重版权】【版权所有,侵权必究】未经授权,禁止复制转载。
  • createDOM:用Virtual D【作者:唐霜】【关注微信公众号:wwwtangshuangnet】OM渲染出真正的DOM
  • 转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。【作者:唐霜】【转载请注明来源】【本文受版权保护】
  • render:将createDOMD得到本文版权归作者所有,未经授权不得转载。【转载请注明来源】的节点挂载到真实的文档中,使文档发生变化
  • 转载请注明出处:www.tangshuang.net【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】【原创内容,转载请注明出处】【版权所有,侵权必究】
  • update:传入新的数据,新数据会引起本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。界面变化
    • diff:传入数据之后使用createV【本文首发于唐霜的博客】【本文首发于唐霜的博客】irtualDOM得到一个新的Virtu未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.netal DOM,和老的进行比较,得到哪些节原创内容,盗版必究。【本文首发于唐霜的博客】点是变化了的
    • 本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】【本文受版权保护】转载请注明出处:www.tangshuang.net【本文受版权保护】
    • patch:利用diff的结果,更新真实【原创内容,转载请注明出处】原创内容,盗版必究。的DOM,使界面发生变化
    • 【作者:唐霜】未经授权,禁止复制转载。【本文受版权保护】本文作者:唐霜,转载请注明出处。
  • 本文作者:唐霜,转载请注明出处。【本文受版权保护】原创内容,盗版必究。
  • destroy:销毁对应的DOM节点,界【未经授权禁止转载】【未经授权禁止转载】面发生变化
  • 本文版权归作者所有,未经授权不得转载。未经授权,禁止复制转载。【访问 www.tangshuang.net 获取更多精彩内容】

就这么几个方就够了,这就是一个Virtu【原创内容,转载请注明出处】【本文受版权保护】al DOM的全部内容了。我们又不是在写【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】component,所以React的那套【转载请注明来源】原创内容,盗版必究。生命周期我没考虑进来。因此,我们要实现的本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。,就是上面这些内容,一个一个实现就好了。

原创内容,盗版必究。本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】

HTML字符串模板转Virtual DO【未经授权禁止转载】本文作者:唐霜,转载请注明出处。M

原创内容,盗版必究。本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】【访问 www.tangshuang.net 获取更多精彩内容】

在写HTMLStringParser时,著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】利用htmlparser2去解析html原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。的原理已经说过了,如果不懂,可以在文章开【原创不易,请尊重版权】【本文受版权保护】头的链接中找到答案。但是,我们还要解决三【关注微信公众号:wwwtangshuangnet】转载请注明出处:www.tangshuang.net个问题:1. 插值;2. 事件绑定;3.【未经授权禁止转载】【本文受版权保护】 for和if。

本文作者:唐霜,转载请注明出处。【未经授权禁止转载】原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】

插值【本文受版权保护】

转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】未经授权,禁止复制转载。

插值其实比较好解决,无非是字符串匹配替换【原创内容,转载请注明出处】转载请注明出处:www.tangshuang.net。我采用了双大括号作为插值标记,在字符串著作权归作者所有,禁止商业用途转载。【未经授权禁止转载】模板的任何位置都可以使用,但是应该注意几【本文首发于唐霜的博客】著作权归作者所有,禁止商业用途转载。个点:

本文版权归作者所有,未经授权不得转载。【本文受版权保护】著作权归作者所有,禁止商业用途转载。
  • 插值代表变量,因此最好只用在属性值(除i【原创内容,转载请注明出处】本文版权归作者所有,未经授权不得转载。d和key两个属性外)和文本中
  • 【未经授权禁止转载】未经授权,禁止复制转载。【作者:唐霜】
  • 插值只能是字符串或数字,不能是array著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】或object
  • 【转载请注明来源】原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。【作者:唐霜】著作权归作者所有,禁止商业用途转载。
  • 插值结果是在html字符串转换为VNod【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.nete之前就已经完成了,VNode中不存在插原创内容,盗版必究。【本文首发于唐霜的博客】
  • 本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】【作者:唐霜】

我们来看一段实例:【原创不易,请尊重版权】

【原创内容,转载请注明出处】原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。
<div id="my-test" class="{{myClass}}">{{myText}}</div>

上面的【原创内容,转载请注明出处】{{myClass}}本文版权归作者所有,未经授权不得转载。{{myText}}在转换完的VNode结果中,是真实的值,【访问 www.tangshuang.net 获取更多精彩内容】未经授权,禁止复制转载。而不是插值字符串。

本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】【版权所有】唐霜 www.tangshuang.net

插值从哪里来呢?所以,我们要在实例化Cl【未经授权禁止转载】本文作者:唐霜,转载请注明出处。ass的时候,把插值对应的真实值都传进来【转载请注明来源】【访问 www.tangshuang.net 获取更多精彩内容】,所以,我规定了一个data选项,实例化【访问 www.tangshuang.net 获取更多精彩内容】未经授权,禁止复制转载。的时候应该:

【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。【作者:唐霜】【本文受版权保护】
new VirtualDOM({
  template: `<div id="my-test" class="{{myClass}}">{{myText}}</div>`,
  data: {
    myClass: 'class1 class2',
    myText: 'this is a text',
  },
})

这是初始化信息,传进来之后,后面还可以通著作权归作者所有,禁止商业用途转载。【作者:唐霜】过update方法去改数据来修改界面。而未经授权,禁止复制转载。【原创内容,转载请注明出处】这个效果,跟Vue的感觉有点像,但是Vu著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】e是把整个component实现了,我这未经授权,禁止复制转载。未经授权,禁止复制转载。里只实现Virtual DOM。

未经授权,禁止复制转载。【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。【本文首发于唐霜的博客】

那么从代码层面怎么实现插值呢?下面来看代【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】码。

【未经授权禁止转载】【版权所有,侵权必究】转载请注明出处:www.tangshuang.net

先定义一个函数,用来替换插值:【访问 www.tangshuang.net 获取更多精彩内容】

原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】【版权所有】唐霜 www.tangshuang.net
let interpose = (str, key, value) => {
  if (typeof str !== 'string') {
    return str
  }
  if (str.indexOf('{{') > -1 && str.indexOf('}}')) {
    let reg = new RegExp('\{\{' + key + '\}\}', 'g')
    str = str.replace(reg, value)
  }
  return str
}

这个函数的使用方法非常简单,str就是字【访问 www.tangshuang.net 获取更多精彩内容】【关注微信公众号:wwwtangshuangnet】符串模板,key就是插值字符串,valu转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。e就是插值字符串对应的值。有了这个函数之【转载请注明来源】【版权所有】唐霜 www.tangshuang.net后,我们只需要遍历我们所有的插值预设项,本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。也就是data,如果data中有对应的k未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。ey,那么key就会被替换为value。

本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.net【原创不易,请尊重版权】
// data是我们前面提到的,传入的data数据对象,template就是字符串模板
let dataKeys = Object.keys(data)
if (dataKeys.length) {
  dataKeys.forEach(key => {
    let value = data[key]
    template = interpose(template, key, value)
  })
}

这个处理在使用htmlparser2进行【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】解析前就可以先完成。

【本文受版权保护】【本文首发于唐霜的博客】【访问 www.tangshuang.net 获取更多精彩内容】本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。

事件绑定【访问 www.tangshuang.net 获取更多精彩内容】

本文版权归作者所有,未经授权不得转载。未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。

插值是最好解决的,字符串替换就可以了,但本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。是事件绑定比较难,因为事件的回调函数数据著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。类型是函数,不能和字符串直接连接,必须采【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】用绕一点的方案来解决这个问题。

原创内容,盗版必究。未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net

首先是如何实现事件机制,请再翻看一下著作权归作者所有,禁止商业用途转载。这里【原创内容,转载请注明出处】,把事件绑定回dom元素的时候,我们从V本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】Node的events属性中取出事件的回【版权所有,侵权必究】【作者:唐霜】调函数。现在的问题是,我们希望在html著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。字符串模板中把事件记录进去,怎么办?我的【版权所有,侵权必究】【原创不易,请尊重版权】解决方案是,使用特殊的{{:eventHandler}}插值表达式。用一个冒号区分和普通插值的不【未经授权禁止转载】未经授权,禁止复制转载。同,有冒号的,表示事件插值,对应的是传入【版权所有】唐霜 www.tangshuang.net本文作者:唐霜,转载请注明出处。的events的property名:

【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。【版权所有,侵权必究】
new VirtualDOM({
  template: `<a href="javascript:" onclick="{{:clickHandler}}">click</a>`,
  events: {
    clickHandler(e) {
      e.preventDefault()
    },
  },
})

当这样的规则建立之后,在createVi本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】rtualDOM阶段,这个VNode的e【访问 www.tangshuang.net 获取更多精彩内容】未经授权,禁止复制转载。vents中就会把click事件的值设置未经授权,禁止复制转载。【原创不易,请尊重版权】为clickHandler,而click【访问 www.tangshuang.net 获取更多精彩内容】【本文首发于唐霜的博客】Handler是一个函数,可以在crea【本文受版权保护】未经授权,禁止复制转载。teElement中用来作为被绑定的回调【未经授权禁止转载】【本文首发于唐霜的博客】函数。

【关注微信公众号:wwwtangshuangnet】【作者:唐霜】【版权所有】唐霜 www.tangshuang.net【本文首发于唐霜的博客】【访问 www.tangshuang.net 获取更多精彩内容】

代码层面,怎么解决【原创不易,请尊重版权】{{:的识别呢?其实很简单,我不是通过【关注微信公众号:wwwtangshuangnet】{{:进行识别的,而是通过属性名前缀是否为【关注微信公众号:wwwtangshuangnet】on来识别。如果是on前缀的属性,说明这是一转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。个绑定,所以代码就如下操作:

【未经授权禁止转载】本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】转载请注明出处:www.tangshuang.net【转载请注明来源】
let attrs = obj.attrs // obj实际上就是vnode
let attrKeys = Object.keys(attrs)
attrKeys.forEach(key => {
  let value = attrs[key]
  if (key.indexOf('on') === 0 && value.substring(0, 3) == '{{:' && value.substring(value.length - 2) == '}}') {
    let eventName = key.substring(2).toLowerCase()
    let eventCallbackName = value.substring(3, value.length - 2)
    obj.events[eventName] = this.events[eventCallbackName].bind(this)
    delete attrs[key]
  }
})
  
obj.attrs = attrs

这段代码是在创建VNode的函数crea未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。teVNode中的,这样的函数的结果,会【本文首发于唐霜的博客】本文作者:唐霜,转载请注明出处。正好把事件相关的回调函数都保存起来,也就【本文受版权保护】原创内容,盗版必究。是我们想象中的VNode结构。

原创内容,盗版必究。【本文受版权保护】本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。

@foreach循环原创内容,盗版必究。

【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net【本文受版权保护】【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。

在很多模板引擎中,都有for循环,但是我原创内容,盗版必究。【作者:唐霜】觉得,作为和DOM结合紧密的Virtua著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。l DOM,更多的是从数据中去循环,也就【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。是根据数据不同,渲染不同结果。所以,我没【作者:唐霜】未经授权,禁止复制转载。有实现for,而是实现了foreach。【本文首发于唐霜的博客】未经授权,禁止复制转载。foreach的目标是object,ar【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.netray也是一种特殊的object,所以是转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】通用的。foreach的实现模型如下:

转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】未经授权,禁止复制转载。
foreach(object, key, value) {
  // ...
}

传入一个object,它会把每一个key【本文首发于唐霜的博客】【版权所有,侵权必究】=>value在遍历中给出来。

本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net本文版权归作者所有,未经授权不得转载。

但是要在html模板中实现也有些让人苦恼转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。,vue的实现方式是在标签的属性里面加了【本文受版权保护】【未经授权禁止转载】一个for,但我是直接增加了一个<【转载请注明来源】转载请注明出处:www.tangshuang.net@foreach>标签,标签内部的【未经授权禁止转载】【原创不易,请尊重版权】东西会被循环加载到结果中。

【转载请注明来源】【本文受版权保护】【本文受版权保护】本文版权归作者所有,未经授权不得转载。
<@foreach target="items" key="i" value="v">
  <li>{{i}} {{v}}</li>
</@foreach>

target对应的是data的某个属性的【未经授权禁止转载】【转载请注明来源】property name,用来获取数据【未经授权禁止转载】【未经授权禁止转载】,比如上面的items,实际上对应的是d【本文受版权保护】未经授权,禁止复制转载。ata.items。

未经授权,禁止复制转载。原创内容,盗版必究。【未经授权禁止转载】【原创不易,请尊重版权】

代码层面怎么去实现呢?这个确实有点复杂,原创内容,盗版必究。【本文受版权保护】它涉及到克隆的问题,也就是一个节点要反复【版权所有】唐霜 www.tangshuang.net【作者:唐霜】多次使用。而且,有一个难点,是htmlp【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。arser2解析的结果,会把<@f原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。oreach>作为一个正常的节点,【转载请注明来源】转载请注明出处:www.tangshuang.net那么我们期望的父子关系就会被这个节点打乱著作权归作者所有,禁止商业用途转载。【版权所有,侵权必究】,为了解决这个问题,我们还要处理还原为正原创内容,盗版必究。未经授权,禁止复制转载。常的节点关系,这个也比较难。

【版权所有,侵权必究】【转载请注明来源】【作者:唐霜】

最终我的方案,是在全部节点已经处理完之后本文版权归作者所有,未经授权不得转载。【关注微信公众号:wwwtangshuangnet】,再重新遍历一遍所有节点,这个时候再来处本文作者:唐霜,转载请注明出处。【访问 www.tangshuang.net 获取更多精彩内容】理@foreach的问题:

本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。
elements.forEach(vnode => {
  if (vnode.name === '@foreach') {
    let attrs = vnode.attrs
    let items = data[attrs.target]
    let key = attrs.key
    let value = attrs.value
    let children = vnode.children
    let childNodes = []
  
    if (items) {
      foreach(items, (i, item) => {
        children.forEach(child => {
          let node = {}
          foreach(child, (prop, value) => {
            node[prop] = value
          })
          node.text = interpose(node.text, key, i)
          node.text = interpose(node.text, value, item)
          foreach(node.attrs, (k, v) => {
            node.attrs[k] = interpose(v, key, i)
            node.attrs[k] = interpose(v, value, item)
          })
          node.id = node.attrs.id
          node.class = node.attrs.class ? node.attrs.class.split(' ') : []
          childNodes.push(node)
        })
      })
    }
  
    if (childNodes.length) {
      let parentChildren = vnode.parent ? vnode.parent.children : elements
      let i = parentChildren.indexOf(vnode)
      parentChildren.splice(i, 1, ...childNodes)
    }
  }
})

读过之前文章的同学知道,elements【本文受版权保护】【作者:唐霜】是包含所有html节点,扁平的数组,因此【本文首发于唐霜的博客】【访问 www.tangshuang.net 获取更多精彩内容】遍历它,就可以实现全部节点的处理了。
本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】 通过修改parent属性,实现了节点提原创内容,盗版必究。【作者:唐霜】升,这样就不用担心@foreach打乱原【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。本设想的节点父子关系了。

本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。

上面有一个foreach函数,其实实现起【原创内容,转载请注明出处】转载请注明出处:www.tangshuang.net来也超级简单,用法跟jquery的$.e【版权所有,侵权必究】【版权所有,侵权必究】ach一样。

【转载请注明来源】著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net【关注微信公众号:wwwtangshuangnet】

@if的实现未经授权,禁止复制转载。

未经授权,禁止复制转载。【转载请注明来源】【访问 www.tangshuang.net 获取更多精彩内容】【本文受版权保护】

和foreach一样,@if也是一个特殊【原创不易,请尊重版权】【作者:唐霜】标签,标签内部的内容根据标签condit【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。ion属性值的运算结果来确定是否加入到V【原创内容,转载请注明出处】转载请注明出处:www.tangshuang.netNode中。当然,也存在节点父子关系的问转载请注明出处:www.tangshuang.net【作者:唐霜】题,但是实现起来就简单很多,因为不用考虑转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。循环克隆的问题。

转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net
else if (vnode.name === '@if') {
  let attrs = vnode.attrs
  let condition = attrs.condition
  let children = vnode.children
  let parentChildren = vnode.parent ? vnode.parent.children : elements
  let i = parentChildren.indexOf(vnode)
  if (eval(condition)) {
    parentChildren.splice(i, 1, ...children)
  }
  else {
    parentChildren.splice(i, 1)
  }
}

红色的eval是不太安全的一种操作,但是【版权所有】唐霜 www.tangshuang.net本文作者:唐霜,转载请注明出处。目前因为没有仔细思考如何自己实现一个运算原创内容,盗版必究。本文作者:唐霜,转载请注明出处。器,所以暂且这样做,后期可以自己实现一个本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】condition运算器来替代这里。

【未经授权禁止转载】【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。

小结原创内容,盗版必究。

【作者:唐霜】本文作者:唐霜,转载请注明出处。原创内容,盗版必究。

HTMLStringParser的实现帮本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。助我实现了基本的Virtual DOM的【作者:唐霜】原创内容,盗版必究。创建问题,上面这四个小节则在前文的基础上【原创不易,请尊重版权】【未经授权禁止转载】,更加深入的实现了模板引擎的新功能。作为原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】开发者,你想要完全搞定这块,读源码吧,所本文作者:唐霜,转载请注明出处。原创内容,盗版必究。有的源码都在createVirtualDOM【转载请注明来源】方法中。【本文受版权保护】

转载请注明出处:www.tangshuang.net【作者:唐霜】原创内容,盗版必究。原创内容,盗版必究。

真实DOM元素的构建和渲染【版权所有】唐霜 www.tangshuang.net

【版权所有,侵权必究】【作者:唐霜】【作者:唐霜】本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】

这个知识在【版权所有】唐霜 www.tangshuang.net这里著作权归作者所有,禁止商业用途转载。已经讲过了,也是非常容易实现的一个环节。【原创内容,转载请注明出处】转载请注明出处:www.tangshuang.net无非是document.createEl著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.netement和appendChild之类的【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】操作。最后得到一个真实的DOM元素,而且【版权所有,侵权必究】【原创不易,请尊重版权】这个DOM元素还被绑定了事件(如果有的话【原创不易,请尊重版权】转载请注明出处:www.tangshuang.net)。

著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。

因为有了VNode这一层,从HTML模板【本文受版权保护】著作权归作者所有,禁止商业用途转载。到真实DOM之间就有了一层抽象,真实DO未经授权,禁止复制转载。【作者:唐霜】M依赖的是VNode的name、attr转载请注明出处:www.tangshuang.net本文作者:唐霜,转载请注明出处。s、events这几个属性,所以完全脱离原创内容,盗版必究。【转载请注明来源】原始信息。

【本文受版权保护】【未经授权禁止转载】转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。【本文首发于唐霜的博客】

除了我在createElement原来的转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。内容基础上,我还增加了一个工作,就是把一本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】个VNode对应的element挂载到V原创内容,盗版必究。未经授权,禁止复制转载。Node的$element属性上,这样当本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】你想操作一个VNode对应的DOM元素时【版权所有】唐霜 www.tangshuang.net【作者:唐霜】,就非常简单了,因为直接通过这个$ele【本文首发于唐霜的博客】【原创不易,请尊重版权】ment属性,就可以找到它对应的真实DO【原创不易,请尊重版权】著作权归作者所有,禁止商业用途转载。M元素。当然,这也增加了风险,就是当你删未经授权,禁止复制转载。【转载请注明来源】除一个DOM元素时,应该把$elemen未经授权,禁止复制转载。未经授权,禁止复制转载。t的引用也删除,不然会造成内存泄露问题。

【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。

总之,还原真实DOM的这部分内容相对来说本文作者:唐霜,转载请注明出处。【作者:唐霜】还是比较简单的。具体的源码可以在这里【本文受版权保护】阅读。而渲染到文档界面中就更简单,直接使【本文受版权保护】【版权所有】唐霜 www.tangshuang.net用DOM元素的操作方法即可实现。但是在渲原创内容,盗版必究。【作者:唐霜】染之前,应该用innerHTML = ''将内部文档置空。【版权所有,侵权必究】

本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。

用数据更新界面【原创不易,请尊重版权】

【作者:唐霜】【关注微信公众号:wwwtangshuangnet】【转载请注明来源】

这可是Virtual DOM的重头戏,在【版权所有】唐霜 www.tangshuang.net【本文首发于唐霜的博客】大部分文献中只会讲diff和patch,【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。不会告诉你数据进入Virtual DOM【版权所有,侵权必究】【版权所有,侵权必究】到界面输出的过程是怎么做到的。diff和【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。patch我会单独拿出来讲,因此,这里主【关注微信公众号:wwwtangshuangnet】本文版权归作者所有,未经授权不得转载。要是梳理数据进入到界面输出的逻辑过程。

本文版权归作者所有,未经授权不得转载。【本文受版权保护】【访问 www.tangshuang.net 获取更多精彩内容】未经授权,禁止复制转载。

其实这个逻辑过程也超级简单:著作权归作者所有,禁止商业用途转载。

本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】原创内容,盗版必究。
  1. 传入新数据著作权归作者所有,禁止商业用途转载。
  2. 【本文首发于唐霜的博客】本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。【版权所有,侵权必究】未经授权,禁止复制转载。
  3. 重新获得一份新的Virtual DOM,【关注微信公众号:wwwtangshuangnet】【访问 www.tangshuang.net 获取更多精彩内容】而新的Virtual DOM是根据传入的著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。新数据和老数据merge之后,利用插值机【版权所有】唐霜 www.tangshuang.net【未经授权禁止转载】制得到的,因此插值不同,得到的Virtu【本文首发于唐霜的博客】【版权所有】唐霜 www.tangshuang.netal DOM可能就不同
  4. 本文作者:唐霜,转载请注明出处。原创内容,盗版必究。未经授权,禁止复制转载。【版权所有,侵权必究】
  5. 既然可能不同,那么就要找出不同的地方,利未经授权,禁止复制转载。【版权所有,侵权必究】用一个diff算法,把所有不同的地方记录【关注微信公众号:wwwtangshuangnet】著作权归作者所有,禁止商业用途转载。到一个patches的数组中
  6. 转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net【转载请注明来源】本文作者:唐霜,转载请注明出处。
  7. 遍历这个patches数组,每一个元素就【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】是我要进行的真实DOM的操作,操作完DO【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。M之后,界面就发生了改变
  8. 【作者:唐霜】【作者:唐霜】原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。【访问 www.tangshuang.net 获取更多精彩内容】

在这整个逻辑过程中,一定要注意js这门语著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。言的一些特性,特别是object是引用型本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.net数据类型的这一特性,如果不处理好,就会存【转载请注明来源】【转载请注明来源】在很多bug。

【本文受版权保护】【关注微信公众号:wwwtangshuangnet】著作权归作者所有,禁止商业用途转载。【未经授权禁止转载】

你可以看我写的转载请注明出处:www.tangshuang.netudpate方法【原创内容,转载请注明出处】,超级简单的几行代码。原创内容,盗版必究。

【未经授权禁止转载】原创内容,盗版必究。【转载请注明来源】【本文首发于唐霜的博客】【本文首发于唐霜的博客】

diff算法未经授权,禁止复制转载。

本文作者:唐霜,转载请注明出处。【原创内容,转载请注明出处】【转载请注明来源】

重头戏中的重头戏,diff算法消耗了我整【转载请注明来源】【本文首发于唐霜的博客】个写作最多的时间,我查阅了网上很多资料,【转载请注明来源】【未经授权禁止转载】还看了github上几个知名的Virtu【访问 www.tangshuang.net 获取更多精彩内容】本文作者:唐霜,转载请注明出处。al DOM库的源代码,发现他们都是将d【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。iff和patch紧密的融合在一起,我希【原创不易,请尊重版权】【转载请注明来源】望写一个独立的diff算法的函数,这个函【作者:唐霜】【版权所有】唐霜 www.tangshuang.net数得到patches,但不执行patch【本文首发于唐霜的博客】转载请注明出处:www.tangshuang.net的任何操作。最终的代码在这里未经授权,禁止复制转载。【未经授权禁止转载】

原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。

关于React标准的diff算法,这里就本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.net不去深究了,毕竟React的diff算法转载请注明出处:www.tangshuang.net【版权所有,侵权必究】还要考虑component内嵌compo原创内容,盗版必究。未经授权,禁止复制转载。nent的情况,而我们现在不用考虑这种情【版权所有,侵权必究】未经授权,禁止复制转载。况,因为我们是html字符串模板,所以,转载请注明出处:www.tangshuang.net【转载请注明来源】我们只需要考虑React标准diff中三著作权归作者所有,禁止商业用途转载。【原创不易,请尊重版权】种情况中的一种即可,Text组件和Com【版权所有,侵权必究】【作者:唐霜】ponent组件我们都不考虑(虽然我们会本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net考虑如果一个元素只包含文本的情况)。

本文版权归作者所有,未经授权不得转载。未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。

diff原则【版权所有,侵权必究】

著作权归作者所有,禁止商业用途转载。【转载请注明来源】转载请注明出处:www.tangshuang.net【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net

我们只会涉及到React标准diff中有【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。关元素对比的部分。即主要遵循的规则如下:

【转载请注明来源】【原创不易,请尊重版权】【未经授权禁止转载】【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。
  • 逐层对比未经授权,禁止复制转载。
  • 【版权所有,侵权必究】【转载请注明来源】【未经授权禁止转载】【未经授权禁止转载】本文版权归作者所有,未经授权不得转载。
  • 同层的元素间对比【关注微信公众号:wwwtangshuangnet】
  • 【原创内容,转载请注明出处】本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。【未经授权禁止转载】

什么意思呢?就是在老的VNode和新的V【关注微信公众号:wwwtangshuangnet】著作权归作者所有,禁止商业用途转载。Node中,先比较顶层的元素,顶层的元素【本文受版权保护】【版权所有】唐霜 www.tangshuang.net相同,再对比下层,如果顶层的不同,直接不未经授权,禁止复制转载。【本文首发于唐霜的博客】用比下层了。而在同一层中,会存在多个元素转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】,怎么确定每一个位置的元素是否是相同的,【关注微信公众号:wwwtangshuangnet】【版权所有,侵权必究】或者位置发生了变化,这是一个极度困难的问【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。题。

原创内容,盗版必究。【转载请注明来源】【转载请注明来源】【本文首发于唐霜的博客】

确定元素是否是同一个元素【未经授权禁止转载】

【作者:唐霜】原创内容,盗版必究。【未经授权禁止转载】著作权归作者所有,禁止商业用途转载。

在老的VNode和新的VNode中,有这【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。么一层存在一些节点,如图:
本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。
本文版权归作者所有,未经授权不得转载。【作者:唐霜】 现在我们假定要比较第二层的元素,那么我转载请注明出处:www.tangshuang.net【未经授权禁止转载】们能否确定A1和B1代表的是同一个元素呢转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net?我们来举一个实际代码的例子:

转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net【转载请注明来源】著作权归作者所有,禁止商业用途转载。
A:<div class="class1">...</div><div class="class2">...</div><div class="class3">...</div>
B:<div class="class1">...</div><div class="class2">...</div><div class="class3">...</div>

我们能确定A1和B1是同一个元素吗?其实【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】是不能的。但是下面这种情况我们就能确定:

本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。【作者:唐霜】【作者:唐霜】
A:<div id="my-test" class="class1">...</div><div class="class2">...</div><div class="class3">...</div>
B:<div id="my-test" class="class1">...</div><div class="class2">...</div><div class="class3">...</div>

因为在我们的文档中,我们一般会保证一个i【版权所有,侵权必究】【本文首发于唐霜的博客】d只存在一个元素,所以两个VNode中,本文版权归作者所有,未经授权不得转载。【原创内容,转载请注明出处】如果存在id相等的情况,我们基本可以确定【原创内容,转载请注明出处】【关注微信公众号:wwwtangshuangnet】他们是同一个元素。当然,如果把id属性用本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net插值来表示,那就坑了。

【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。转载请注明出处:www.tangshuang.net

因此,我们根本无法做到完全判断节点是否是未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。同一个,我们只能通过比较一些基本特征来确本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net定。当然,我们学习React中的做法,声【未经授权禁止转载】【本文受版权保护】明一个key属性,如果key属性相等,那本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。么这两个节点肯定是指同一个元素。

未经授权,禁止复制转载。【版权所有,侵权必究】转载请注明出处:www.tangshuang.net本文作者:唐霜,转载请注明出处。

在这种模糊状态下,我们采用比较理想化的解原创内容,盗版必究。【本文首发于唐霜的博客】决方法:当一个VNode和另外一个VNo【原创内容,转载请注明出处】【本文受版权保护】de的name、attrs的属性名都相等转载请注明出处:www.tangshuang.net【未经授权禁止转载】时,我们认为它们是指同一个元素。

【本文受版权保护】本文作者:唐霜,转载请注明出处。【版权所有,侵权必究】【未经授权禁止转载】

虽然从理论上讲,这是非常不严谨的,但是从转载请注明出处:www.tangshuang.net【作者:唐霜】实践的角度看,这样做把问题简单化。因为如本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。果一个html的属性列表比较复杂的话,这【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】种方法可以非常容易区分不同的元素。而如果【本文受版权保护】【版权所有】唐霜 www.tangshuang.net最坏的情况出现,也就是标签没有任何属性,本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。那么我也认为它们是同一个元素,只不过他们本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】的children可能变了而已。

著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。未经授权,禁止复制转载。本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。

还要考虑到,如果一个元素直接包含文本,那【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。么它和另外一个包含子节点的元素肯定是不同本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】的元素。
【未经授权禁止转载】【本文受版权保护】 因此,我写了一个函数来决定这个元素的身【未经授权禁止转载】本文版权归作者所有,未经授权不得转载。份:

【未经授权禁止转载】【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。【未经授权禁止转载】
function identify(vnode) {
  if (vnode.attrs.key) {
    return vnode.name + ':' + vnode.attrs.key
  }
  return vnode.name + ':' + Object.keys(vnode.attrs).join(',') + '|' + !!vnode.text
}

当来自新老Virtual DOM的两个V【未经授权禁止转载】【版权所有】唐霜 www.tangshuang.netNode经过计算得到相同的identit著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】y之后,就认为他们是相同的元素。

原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。

同级元素的diff未经授权,禁止复制转载。

【作者:唐霜】【原创不易,请尊重版权】原创内容,盗版必究。【关注微信公众号:wwwtangshuangnet】【版权所有】唐霜 www.tangshuang.net

接下来,我们就要开始写同级元素的diff【未经授权禁止转载】转载请注明出处:www.tangshuang.net算法。在我们思考的过程中,应该由简入难,未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net先把简单的情况处理掉,然后再处理复杂的情【转载请注明来源】【关注微信公众号:wwwtangshuangnet】况。因此,我的做法如下:

【关注微信公众号:wwwtangshuangnet】【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。【访问 www.tangshuang.net 获取更多精彩内容】本文版权归作者所有,未经授权不得转载。

先把新老节点列表的identity算出来原创内容,盗版必究。【未经授权禁止转载】

【转载请注明来源】【原创内容,转载请注明出处】【原创内容,转载请注明出处】原创内容,盗版必究。【原创不易,请尊重版权】
let oldIdentifies = oldNodes.map(vnode => identify(vnode)) 
let newIdentifies = newNodes.map(vnode => identify(vnode))

这样可以知道,在新的DOM中,哪些老节点【原创不易,请尊重版权】【版权所有,侵权必究】被删除了:

本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net【关注微信公众号:wwwtangshuangnet】
oldIdentifies.forEach((id, i) => {
  let oldNode = oldNodes[i]
  if (newIdentifies.indexOf(id) === -1) {
    patches.push(makePatch('remove', oldNode))
  }
  else {
    ...
  }
})

被删除的节点立即记录到了patches这原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。个变量里面。

【转载请注明来源】原创内容,盗版必究。【未经授权禁止转载】

接下来,我们去遍历新节点列表,每遍历到一【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】个,就用它去和老节点中(被删除后剩余的节【关注微信公众号:wwwtangshuangnet】著作权归作者所有,禁止商业用途转载。点列表)这个位置上的节点是不是同一个节点未经授权,禁止复制转载。【未经授权禁止转载】,如果是,那么就保持不动,进入child原创内容,盗版必究。【本文首发于唐霜的博客】ren层级的比较:

转载请注明出处:www.tangshuang.net本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】未经授权,禁止复制转载。【原创不易,请尊重版权】
if (id === targetIndentity) {
  patches = patches.concat(diffSameNodes(targetNode, newNode))
}

diffSameNodes函数就是去对比转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。相同节点,这个函数内会去对比文本、att【访问 www.tangshuang.net 获取更多精彩内容】【本文首发于唐霜的博客】ributes属性、以及children【关注微信公众号:wwwtangshuangnet】【版权所有,侵权必究】,当进入到children之后,其实又是【转载请注明来源】原创内容,盗版必究。同一层的对比,所以是一个递归。后文会讲文著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net本和attrs的对比。

本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。【转载请注明来源】

接下来,我们要考虑一种情况,那就是遍历到转载请注明出处:www.tangshuang.net本文版权归作者所有,未经授权不得转载。的新节点的identity在老节点列表里本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。找到了,但是不在当前对比的这个位置上,那【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】么说明这个节点位置发生了改变。

本文版权归作者所有,未经授权不得转载。【关注微信公众号:wwwtangshuangnet】本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net
let foundPosition = findIndentityIndex(id, finalIdentities, cursor)
...
else if (foundPosition !== -1) {
  let oldNode = finalNodes[foundPosition]
  let oldIndentity = finalIdentities[foundPosition]
  patches.push(makePatch('move', targetNode, oldNode))
  ...
}

findIndentityIndex是一【版权所有,侵权必究】原创内容,盗版必究。个用来找到老节点所在位置的函数,它内部要【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。考虑一种情况,就是排除以前已经对比过的节【本文受版权保护】本文作者:唐霜,转载请注明出处。点,所以第三个参数cursor就是用来记本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。录要从哪个位置开始往后找。

【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】未经授权,禁止复制转载。本文版权归作者所有,未经授权不得转载。

而当新节点不存在与老节点列表中时,有两种【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。情况,一种是在中间插入,一种是插入到最后【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。

本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。【转载请注明来源】本文版权归作者所有,未经授权不得转载。
  // not exists, insert
  else if (i < finalIdentities.length) {
    patches.push(makePatch('insert', targetNode, newNode))
    ...
  }
  // not exists, append
  else {
    patches.push(makePatch('append', parentNode, newNode))
    ...
  }

这样就解决了几乎所有情况。剩下的就是实现【访问 www.tangshuang.net 获取更多精彩内容】【版权所有】唐霜 www.tangshuang.netdiffSameNodes。

【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】【未经授权禁止转载】【未经授权禁止转载】【版权所有,侵权必究】

两个相同的元素,也可能存在变化,一种是如转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net果它只包含文本的话,那么文本可能变,另一著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net种是它的attrs可能变,还有一种是它的【关注微信公众号:wwwtangshuangnet】【版权所有】唐霜 www.tangshuang.net子节点可能变。所以,都要处理。文本变很容原创内容,盗版必究。【作者:唐霜】易:

【本文首发于唐霜的博客】【版权所有,侵权必究】【本文受版权保护】【作者:唐霜】【作者:唐霜】
if (oldNode.text !== newNode.text) {
  patches.push(makePatch('changeText', oldNode, newNode.text))
}

attrs变的话,要相对复杂一点,要做一【版权所有,侵权必究】【原创内容,转载请注明出处】个遍历:

【本文首发于唐霜的博客】【关注微信公众号:wwwtangshuangnet】本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】【作者:唐霜】
function diffAttributes(oldNode, newNode) {
  let patches = []
  let oldAttrs = oldNode.attrs
  let newAttrs = newNode.attrs
  let keys = Object.keys(newAttrs)
  if (keys.length) {
    keys.forEach(key => {
      let oldValue = oldAttrs[key]
      let newVaule = newAttrs[key]
      if (oldValue !== newVaule) {
        patches.push({
          key,
          value: newVaule,
        })
      }
    })
  }
  return patches
}
let attrsPatches = diffAttributes(oldNode, newNode)
if (attrsPatches.length) {
  patches = patches.push(makePatch('changeAttribute', oldNode, attrsPatches))
}

最后是children,实际上,就是进入【原创内容,转载请注明出处】本文作者:唐霜,转载请注明出处。到下一层的diff算法。直接使用diff【本文首发于唐霜的博客】【版权所有,侵权必究】函数去比较就可以了,就是一个递归罢了。

【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】未经授权,禁止复制转载。原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。

小结【本文首发于唐霜的博客】

本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】【版权所有】唐霜 www.tangshuang.net【作者:唐霜】

整体的逻辑就这些,和React标准dif著作权归作者所有,禁止商业用途转载。【本文受版权保护】f存在差异,性能上存在不足,在一些特殊情原创内容,盗版必究。【关注微信公众号:wwwtangshuangnet】况下,可能一次性更新大片的DOM。但是,本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.net自己手撸能够做出来,我已经很欣慰了。当然【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.net,这里的只是思路,具体的全部代码,还是要未经授权,禁止复制转载。【本文首发于唐霜的博客】去看GitHub,看着点列出来的代码完全【转载请注明来源】【本文受版权保护】没意义。

【版权所有】唐霜 www.tangshuang.net【本文受版权保护】【作者:唐霜】【未经授权禁止转载】

patch算法转载请注明出处:www.tangshuang.net

【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net【原创内容,转载请注明出处】【转载请注明来源】原创内容,盗版必究。

说算法根本谈不上,patch操作其实就是著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。从diff中拿到patches变量,然后未经授权,禁止复制转载。【作者:唐霜】根据变量中的元素进行遍历,对dom进行操【原创不易,请尊重版权】本文作者:唐霜,转载请注明出处。作,仅此而已。代码在这里【原创内容,转载请注明出处】,你可以自己看下,真的很少,就是一个sw【未经授权禁止转载】【原创内容,转载请注明出处】itch。

【未经授权禁止转载】【作者:唐霜】著作权归作者所有,禁止商业用途转载。

但是有一个点需要注意,就是在remove【转载请注明来源】【版权所有,侵权必究】的时候,一定要将$element和$vn【原创内容,转载请注明出处】未经授权,禁止复制转载。ode的引用清除,否则会造成内存问题。

本文版权归作者所有,未经授权不得转载。未经授权,禁止复制转载。原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net

总结【原创不易,请尊重版权】

【版权所有】唐霜 www.tangshuang.net【转载请注明来源】【作者:唐霜】【本文受版权保护】

整个Virtual DOM的手撸过程大致原创内容,盗版必究。【本文受版权保护】就是这样,可能有很多细节你还是不懂,你可【本文受版权保护】【本文首发于唐霜的博客】以在本文下方留言,或者在GitHub上提【作者:唐霜】【转载请注明来源】issue。实际上,讲这么多,重要的还是【转载请注明来源】未经授权,禁止复制转载。在html字符串转VNode和diff这【未经授权禁止转载】【原创内容,转载请注明出处】两部分,而且目前来看,都有可以改进,真的【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】是短板在哪里哪里被谈的最多。

【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】【转载请注明来源】【原创不易,请尊重版权】

另外需要强调的是,这真的是一个Virtu【原创内容,转载请注明出处】【本文首发于唐霜的博客】al DOM的独立库,它不包含生命周期函【未经授权禁止转载】【转载请注明来源】数,但是已经具备了应有的功能。作为开发者【本文受版权保护】【作者:唐霜】,可以用它去进行封装,实现类似React【转载请注明来源】【作者:唐霜】 Component的部分功能,但是如何原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net实现在template中加入compon【作者:唐霜】【原创内容,转载请注明出处】ent,还需要再深入思考。无论如何,自己【未经授权禁止转载】【关注微信公众号:wwwtangshuangnet】手撸一个总能学到东西,我是实践派,不轻易【关注微信公众号:wwwtangshuangnet】【本文首发于唐霜的博客】在没有自己撸过的情况下说“这个实践不行”本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.net这样的话。

原创内容,盗版必究。转载请注明出处:www.tangshuang.net本文作者:唐霜,转载请注明出处。

2017-09-17 6618 ,

为价值买单,打赏一杯咖啡

本文价值66.18RMB