Как исправить «StackLevelError (Stack Overflow)» в шаблоне навигации Jekyll - PullRequest
2 голосов
/ 30 марта 2020

Я пытаюсь написать рекурсивный шаблон навигации Jekyll (include), как описано в « Навигация по вложенному дереву с рекурсией ». У меня есть минимальный пример, зафиксированный в jekyll-min , который в основном содержит:

  • два каталога верхнего уровня, каждый с одной страницей
  • другой каталог под второй каталог верхнего уровня, содержащий одну страницу
  • шаблон навигации (_includes/docs_contents.html), который просматривает каталоги верхнего уровня и инициирует рекурсивный обход для каждого
  • рекурсивного включения (_includes/nav.html) ), который принимает элемент навигации, отображает его заголовок и дочерние ссылки и рекурсивно вызывает себя для любых каталогов в своем дочернем списке
  • макет (_layouts/doc.html), который отображает панель навигации и содержимое для каждой страницы

Я использую Ruby v2.7.0 и Jekyll v3.8.5.

# docs structure

_docs
|
|_a/
| |_index.md
|
|_b/
  |_index.md
  |
  |_1/
    |_index.md  
# _data/docs-nav.yml

- title: a
  docs:
    - link: /a/
- title: b
  docs:
    - link: /b/
    - title: 1
      docs:
        - link: /b/1/
# _includes/nav.html

{% assign section=include.nav %}
<div class="ui accordion">
    <div class="title active">
        <i class="dropdown icon"></i>
        {{ section.title }}
    </div>
    <div class="content active">
        <div class="ui vertical text menu">
            {% for item in section.docs %}
            {% if item.link %}
            {%- assign p = site.documents | where: "url", item.link | first %}
            <a {%- if page.url== p.url %} class="current item" {% endif %} class="item" href="{{ p.url }}">
                {{ p.menu_name | default: p.title }}
            </a>
            {% endif %}
            {% if item.docs %}
            {% include nav.html nav=item %}
            {% endif %}
            {% endfor %}
        </div>
    </div>
</div>
# _includes/docs_contents.html

<div class="unit one-fifth hide-on-mobiles">
    <aside>
        {% for section in site.data.docs_nav %}
        {% include nav.html nav=section %}
        {% endfor %}
    </aside>
</div>
# _layouts/doc.html

---
title: Docs
description: version 1.0
---

<html>
<body>
{% include docs_contents.html %}
{{ content }}
</body>
</html>

Насколько я понимаю, для каждой страницы визуализация шаблона навигации должна работать следующим образом:

  1. _layouts/doc.html
  2. _includes/docs_contents.html: повторять записи уровня root, вызывая _nav для каждого
  3. _nav(/a/ entry): отрендерить заголовок, повторить docs, отрендерить /a/ ссылку и выйти
  4. * 104 4 *: визуализировать заголовок, выполнить итерацию docs, визуализировать /b/ ссылку, а затем вызвать _nav(/b/1/ entry)
  5. _nav(/b/1/ entry): визуализировать заголовок, выполнить итерацию docs, визуализировать /b/1/ ссылку и выйти
  6. _nav(/b/ entry) (уже в стеке): выход
  7. _includes/docs_contents.html: выход

Однако, когда я выполняю bundle exec jekyll build, я получаю:

  Liquid Exception: Liquid error (/mnt/e/ThirdParty/jekyll-min/_includes/docs_contents.html line 17): 
Nesting too deep included in /_layouts/doc.html
jekyll 3.8.5 | Error:  Liquid error (/mnt/e/ThirdParty/jekyll-min/_includes/docs_contents.html line 17): 
Nesting too deep included
Traceback (most recent call last):

[...]

В чем проблема с моим контентом или рекурсивным шаблоном? Я боролся с этим часами без удачи.

JEKYLL_LOG_LEVEL=debug

не дал никакой дополнительной полезной информации.

Фактическая структура документа более сложна и может go сколь угодно глубокой поэтому написание нерекурсивного шаблона для ручной обработки вложенных уровней может оказаться невозможным.

1 Ответ

1 голос
/ 30 марта 2020

Отличный вопрос.

С помощью {{ myvar | inspect }} и рекурсии, ограничивающей флаг, я успешно отладил ваш код и понял, почему происходит эта бесконечная рекурсия.

Это происходит из-за того, что что переменная section в docs_contents. html назначена в for l oop и freezed : ее нельзя изменить.

При первом включении nav. html, {% assign section=include.nav %} не изменяется section, а ваш код просто использует код, назначенный в вашем for l oop.

Когда вы выполняете повторный вызов и вызываете nav. html во второй раз, он будет использовать ту же самую заблокированную глобальную переменную section и будет выполнять рекурсивный процесс неопределенно долго.

Решение состоит в том, чтобы изменить имя переменной в nav. html от section до чего-то другого. Например: sub_section, и это будет работать, потому что эта новая переменная не будет замораживаться и может быть переназначена по мере необходимости во время рекурсии.

{% assign sub_section=include.nav %}
{{ sub_section.title }}
{% for item in sub_section.docs %}
...

Если вы хотите поэкспериментировать, вот мой тестовый код с некоторыми комментариями :

docs_contents. html

<code>{% for section in site.data.docs_nav %}

{% comment %} ++++ Try to reassign "section" ++++ {% endcomment %}
{% assign section = "yolo from docs_contents.html" %}

{% assign recursion = 0 %}
<pre>
&gt;&gt; docs_contents.html
++++ "recursion" var is assigned and becomes global
recursion : {{ recursion | inspect }}
++++ "section" is freezed to loop value ++++
including nav with include nav.html nav=section &gt;&gt; {{ section | inspect }}
{% include nav. html nav = section%} {% endfor%}

nav. html

<code>{% comment %} ++++ Try to reassign "section" ++++ {% endcomment %}
{% assign section = "yolo from nav.html" %}

<pre>
    &gt;&gt; nav.hml
    recursion : {{ recursion }}
    include.nav : {{ include.nav | inspect }}
    ++++ "section" is freezed to loop value ++++
    section : {{ section | inspect }}
{% comment%} ++++ бесполезное назначение ++++ {% endcomment%} {% assign section = include.nav%} {% для элемента в section.docs%} {% if item.link%} {% - назначить p = site.documents | где: "url", item.link | первый%} {{p.menu_name | по умолчанию: p.title}} {% endif%} {% comment%} ++++ ограничение рекурсии до 2 уровней ++++ {% endcomment%} {% if item.docs и рекурсия <2%} {% comment%} ++++ инкрементная глобальная переменная "рекурсия" ++++ {% endcomment%} {% assign recursion = recursion | плюс: 1%} {% include nav. html nav = item%} {% endif%} {% endfor%} </code>
...