Рекурсивный частичный в Ractivejs, превышающий стек вызовов

С момента открытия Ractivejs я постепенно перевожу свое приложение на него.

До того, как я построил древовидную структуру html, используя ванильный JavaScript для создания элементов, а затем добавил ее на страницу после ее завершения. Недавно я перенес его на Ractivejs, который очень хорошо работает с небольшой древовидной структурой, но если дело доходит до глубоко вложенного объекта, он превышает стек вызовов.

Есть ли способ, которым я могу добавить элементы, которые я создал в JavaScript, к шаблону Ractivejs и сохранить на нем события Ractivejs, или есть способ рекурсивно перебирать большой объект, не выходя за пределы стека вызовов?

Примеры

Как строится дерево в JavaScript

var tree = {
  'childPages' : [{    
    'name': 'first child',
    'childPages': []
  }, {
    'name': 'second child',
    'childPages': [{
       'name': 'second first sub child',
       'childPages': []
     }]
  }, {
    'name': 'third child',
    'childPages': [{
        'name': 'third first sub child',
        'childPages': [{
            'name': 'sub sub child',
            'childPages': []
         }]
     }]
  }]
};


function buildTree (tree) {
  var ul = document.createElement('ul');;
  for(var i =0; i < tree.childPages.length; i++){
    var li = document.createElement('li');
    li.innerText = tree.childPages[i].name;
    ul.appendChild(li);
    if (tree.childPages[i].childPages.length) {
       li.appendChild (buildTree(tree.childPages[i]));
    }
  }
  return ul;
}

document.body.appendChild(buildTree(tree));

Как будет построено дерево в шаблоне Ractivejs

var ractive = new Ractive({
  el : 'body',
  template: '#treeNode',
  data: {
'childPages' : [{    
  'name': 'first child',
  'childPages': []
}, {
  'name': 'second child',
  'childPages': [{
    'name': 'second first sub child',
    'childPages': []
  }, {
    'name': 'second second sub child',
    'childPages': []
  }]
}, {
  'name': 'third child',
  'childPages': [{
    'name': 'third first sub child',
    'childPages': [{
      'name': 'sub sub child',
      'childPages': []
    }]
  }]
}]
  }
});
<script src="http://cdn.ractivejs.org/latest/ractive.js"></script>
<script id="treeNode" type="text/ractive">
  <ul>
    {{#each childPages}}
      <li>
         {{name}}
          {{#if childPages}}
           {{> treeNode}}
          {{/if}}
      </li>
     {{/each}}
  </ul>
</script>


person Dan    schedule 11.05.2015    source источник


Ответы (1)


Обычно, когда вы превышаете стек вызовов, это происходит из-за того, как Ractive обрабатывает отсутствующие свойства данных — если у вас есть {{with foo}}{{bar}}{{/with}}, а foo.bar не существует, ссылка {{bar}} не разрешена, поэтому она «поднимается вверх по стеку контекста» и вместо этого ищет bar .

В большинстве случаев это действительно удобно, но в подобных случаях это может быть проблематично, потому что, если элемент в дереве не имеет свойства childPages, Ractive поднимется по стеку к родителю, что отправит нас в бесконечность. рекурсивная кроличья нора.

Есть два решения:

  • Убедитесь, что каждый узел в дереве имеет свойство childPages, даже если это пустой массив, или...
  • Используйте ограниченные ссылки. Если вы используете this.childPages вместо просто childPages (или ./childPages - это эквивалентно, у вас может быть эстетическое предпочтение одного перед другим), тогда Ractive не будет искать фрагмент данных вне текущего контекста. Вот пример этого, когда все пустые массивы childPages удалены - без this. мы немедленно превышаем стек вызовов, но поскольку он там, мы в полной безопасности.

var ractive = new Ractive({
  el : 'body',
  template: '#treeNode',
  data: {
'childPages' : [{    
  'name': 'first child'
}, {
  'name': 'second child',
  'childPages': [{
    'name': 'second first sub child'
  }, {
    'name': 'second second sub child'
  }]
}, {
  'name': 'third child',
  'childPages': [{
    'name': 'third first sub child',
    'childPages': [{
      'name': 'sub sub child'
    }]
  }]
}]
  }
});
<script src="http://cdn.ractivejs.org/latest/ractive.js"></script>
<script id="treeNode" type="text/ractive">
  <ul>
    {{#each this.childPages}}
      <li>
         {{name}}
          {{#if this.childPages}}
           {{> treeNode}}
          {{/if}}
      </li>
     {{/each}}
  </ul>
</script>

person Rich Harris    schedule 11.05.2015
comment
Спасибо большое, теперь все делает молниеносно! Также нашел несколько циклических ссылок внутри объекта... Так что все эти оптимизации, работающие вместе, великолепны :thumbsup: - person Dan; 11.05.2015