Open youngwind opened 7 years ago
new Vue()
更新
- items发生改变,新的值为items=[b,c,e,f]
- 实例复用检查。在旧的实例数组OldVms里面发现Vma和Vmb之前已经有了,给它们打上reused的标签。发现Vme和Vmf之前没有,新建这两个实例,最后构建的新的vm实例数组为Vms=[Vmb,Vmc,Vme,Vmf]。
应该是Vmb和Vmc吧。Correct me if I am wrong.
@LiuMengzhou 是的,此处为笔误,多谢指出,已修正。
diff算法那有一点死活想不明白,实例数组[b,c]生成后,为什么要先插入f再插入e,不能先插入e再插入f
@Geocld 我也疑惑,关于diff算法,这篇文章写得很详细:https://github.com/aooy/blog/issues/2
前言
继上一篇 #90 实现了条件渲染之后,本篇我们来看看如何实现vue列表渲染功能。
问题具象
考虑以下的情况
我们的目标是:根据数组items循环渲染li标签,并且当items改变的时候能够最小化操作DOM完成重新渲染。
思路
我们先来思考一下,总共有几个难点:
父子实例作用域
我们先来解决上面提到的第二个难点:作用域。有没有发现这样一个有趣的现象:父子实例的作用域极度类似于js对象的继承?子实例继承于父实例。比如说,当我们在子实例要找user.name的时候,由于在子实例中找不到user,所以就会沿着原型链跑到父实例上去找,在父实例中找到了user,然后终止查找。当我们在子实例中要找item的时候,因为item是子实例自己的属性,所以在子实例就可以找到,不会跑到父实例上面去找。然而当父实例想访问子实例的item的时候,是无法访问得到的。 综上:通过js的继承,可以解决父子实例的作用域嵌套问题。 具体的实现基本上就是下面这一行代码。
由此导致的另一个相关的问题: 如果我们用继承来实现作用域嵌套,那么,当父实例的user.name改变的时候,如何将变化传导到各个子实例呢? 答案:把子实例存储到父实例的_children数组中,当父实例的data发生变动,触发父实例的updateBindingAt方法的时候,循环遍历_children,分别触发每个子实例的updateBindingAt方法。
那么,具体如何实现呢?
diff算法
第三个难点。vue确保列表渲染性能的核心是:diff算法,整个算法分成两部分:初始化和更新,如下图所示。![diff算法](https://cloud.githubusercontent.com/assets/8401872/18739956/a6769e70-80d9-11e6-879e-0a7affd15ae3.png)
初始化
更新
说的比较抽象,更为直接的方式是直接去debug源码,我参考的版本vue版本是这个,自己实现的版本在这里,核心的代码在这里,实现的效果如下图所示。![demo](https://cloud.githubusercontent.com/assets/8401872/18740039/1f6ff07e-80da-11e6-91ae-e30f9e16e814.gif)
后话
关于列表渲染的性能优化,本篇实现的只是最简单的版本,我感觉还有很多其他的东西没有实现,有待研究,比如track
参考资料