同步DOM结构

除了简单的单向绑定和双向绑定,MVVM还有一个重要的用途,就是让Model和DOM的结构保持同步。

我们用一个TODO的列表作为示例,从用户角度看,一个TODO列表在DOM结构的表现形式就是一组<li>节点:

  1. <ol>
  2. <li>
  3. <dl>
  4. <dt>产品评审</dt>
  5. <dd>新款iPhone上市前评审</dd>
  6. </dl>
  7. </li>
  8. <li>
  9. <dl>
  10. <dt>开发计划</dt>
  11. <dd>与PM确定下一版Android开发计划</dd>
  12. </dl>
  13. </li>
  14. <li>
  15. <dl>
  16. <dt>VC会议</dt>
  17. <dd>敲定C轮5000万美元融资</dd>
  18. </dl>
  19. </li>
  20. </ol>

而对应的Model可以用JavaScript数组表示:

  1. todos: [
  2. {
  3. name: '产品评审',
  4. description: '新款iPhone上市前评审'
  5. },
  6. {
  7. name: '开发计划',
  8. description: '与PM确定下一版Android开发计划'
  9. },
  10. {
  11. name: 'VC会议',
  12. description: '敲定C轮5000万美元融资'
  13. }
  14. ]

使用MVVM时,当我们更新Model时,DOM结构会随着Model的变化而自动更新。当todos数组增加或删除元素时,相应的DOM节点会增加<li>或者删除<li>节点。

在Vue中,可以使用v-for指令来实现:

  1. <ol>
  2. <li v-for="t in todos">
  3. <dl>
  4. <dt>{{ t.name }}</dt>
  5. <dd>{{ t.description }}</dd>
  6. </dl>
  7. </li>
  8. </ol>

v-for指令把数组和一组<li>元素绑定了。在<li>元素内部,用循环变量t引用某个属性,例如,{{ t.name }}。这样,我们只关心如何更新Model,不关心如何增删DOM节点,大大简化了整个页面的逻辑。

我们可以在浏览器console中用window.vm.todos[0].name='计划有变'查看View的变化,或者通过window.vm.todos.push({name:'新计划',description:'blabla...'})来增加一个数组元素,从而自动添加一个<li>元素。

需要注意的是,Vue之所以能够监听Model状态的变化,是因为JavaScript语言本身提供了Proxy或者Object.observe()机制来监听对象状态的变化。但是,对于数组元素的赋值,却没有办法直接监听,因此,如果我们直接对数组元素赋值:

  1. vm.todos[0] = {
  2. name: 'New name',
  3. description: 'New description'
  4. };

会导致Vue无法更新View。

正确的方法是不要对数组元素赋值,而是更新:

  1. vm.todos[0].name = 'New name';
  2. vm.todos[0].description = 'New description';

或者,通过splice()方法,删除某个元素后,再添加一个元素,达到“赋值”的效果:

  1. var index = 0;
  2. var newElement = {...};
  3. vm.todos.splice(index, 1, newElement);

Vue可以监听数组的splicepushunshift等方法调用,所以,上述代码可以正确更新View。

用CSS修饰后的页面效果如下:

todo-mvvm

参考源码

vue-todo

读后有收获可以支付宝请作者喝咖啡,读后有疑问请加微信群讨论:

同步DOM结构 - 图2 同步DOM结构 - 图3