Ractivejs 装饰器初始化不一致

Ractivejs decorator inconsistently initialized

参考:http://jsfiddle.net/ffmqd0zz/

html:

<div id='books_container'/>

<script type='ractive/template' id="books">
  {{#books}}
  <div decorator='decoration' id="{{id}}">
      <span> title: {{ title }}</span>
      <span> id: {{ id }}</span>
  </div>
  {{/books}}
</script>

javascript:

var MyDecorator = function(node) {
    console.log("init MyDecorator for id: " + $(node).attr('id'));
    return { teardown: function() {}};
};

var books = new Ractive({
    el: '#books_container',
    template: '#books',
    data: {books: [{"id": 1174,"title": "franny and zoey"},
                   {"id": 1175,"title": "moby duck"}]},
    decorators: { decoration: MyDecorator }
  });

console.log("init complete")

books.set('books', [{"id": 1176,"title": "life, the universe and stuff"},
                    {"id": 1177,"title": "yellowbrick road"},
                    {"id": 1178,"title": "grapes of wrath"} ]);

注意装饰器初始化记录到控制台。

当 Ractive 初始化时,"books" 列表中的两个初始项被正确修饰,如 console.log 消息所示。然而,当列表被更改为 "books.set()" 时,Ractive 只装饰列表中的第三项,而 id 为 1176 和 1177 的书籍的装饰器没有被初始化。

它似乎通过在第三个添加的项目上初始化装饰器来优化性能,跳过前两个,即使它们已被替换。

我是不是做错了,或者这是一个错误?请有人建议解决方法。

提前致谢

作为一种变通方法,您可以一次弹出和压入一个数组元素。 See full example。相关代码:

books.pop('books');
books.pop('books');

books.push('books', {"id": 1176,"title": "life, the universe and stuff"});
books.push('books', {"id": 1177,"title": "yellowbrick road"});
books.push('books', {"id": 1178,"title": "grapes of wrath"});

我不确定您看到的行为是否是由于错误造成的。就像你说的,可能是 Ractive 优化了性能。来自 Ractive docs 的一则趣闻:"Directives are just instructions to Ractive - they don't actually get added to the element as attributes."

Ractive 只会更新有变化的东西。对于节点列表 ({{each}}),有两种用于更新的策略:

  1. 推送或拼接(或合并)等显式数组修改会产生相同的 DOM 节点修改 - 这意味着 DOM 节点如果未修改则保持原样,否则将被删除或插入。
  2. 设置数组重用 DOM 节点,但即使在这种情况下,也只有已更改的内容会随着每个列表项的结构而更新。

所以对于装饰器来说,如果它们没有参数,Ractive 将不会更新它们。一种更改此设置以向装饰器添加参数的方法:

<div decorator='decoration:{{id}}' id="{{id}}">

现在,如果装饰器依赖的数据发生变化(参见 http://jsfiddle.net/ffmqd0zz/2/),装饰器将被拆除并重新初始化。

不知何故文档还没有更新,但是你也可以添加一个update方法到你的装饰器return参数中,它将被调用装饰器因参数值更改而被拆除:

var MyDecorator = function(node, title) {
    console.log('new decorator', title);
    function tooltip( title ) {
        node.title = title;
    }

    return { 
        teardown: function() {},
        update: function( title ) {
            tooltip(title);
        }
    };
  };

http://jsfiddle.net/ffmqd0zz/3/