如何修复 Jekyll 导航模板中的 "StackLevelError (Stack Overflow)"
How to fix "StackLevelError (Stack Overflow)" in Jekyll navigation template
我正在尝试编写递归 Jekyll 导航模板(包括),如“Nested tree navigation with recursion". I have a minimal example committed in jekyll-min 中所述,它基本上具有:
- 两个 top-level 个目录,每个目录一页
- 第二个top-level目录下的另一个目录,包含一页
- 一个导航模板(
_includes/docs_contents.html
)循环遍历 top-level 个目录并为每个目录启动递归遍历
- 一个递归包含(
_includes/nav.html
),它接受一个导航条目,呈现它的标题和child links,并递归地为它的[=84=中的任何目录调用它自己]仁榜
- 为每个页面呈现导航窗格和内容的布局 (
_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>
据我了解,对于每个页面,导航模板呈现应该像这样工作:
_layouts/doc.html
_includes/docs_contents.html
:迭代根级条目,为每个 调用_nav
_nav(/a/ entry)
:渲染标题,迭代docs
,渲染/a/
link,然后退出
_nav(/b/ entry)
:渲染标题,迭代docs
,渲染/b/
link,然后调用_nav(/b/1/ entry)
_nav(/b/1/ entry)
:渲染标题,迭代docs
,渲染/b/1/
link,然后退出
_nav(/b/ entry)
(已经在堆栈中):退出
_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
没有产生任何额外的有用信息。
实际文档结构更复杂,可以任意深入,因此编写 non-recursive 模板来手动处理嵌套级别可能不是一种选择。
问得好。
在 {{ myvar | inspect }}
和限制递归的标志的帮助下,我已经成功调试了您的代码并理解了为什么会出现这种无限递归。
这是因为 docs_contents.html 中的 section
变量是在 for
循环中赋值的 freezed : 无法更改。
你第一次包含 nav.html 时,{% assign section=include.nav %}
没有改变 section
,你的代码只使用你的 for
循环.
当您第二次递归并调用 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
{% for section in site.data.docs_nav %}
{% comment %} ++++ Try to reassign "section" ++++ {% endcomment %}
{% assign section = "yolo from docs_contents.html" %}
{% assign recursion = 0 %}
<pre>
>> 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 >> {{ section | inspect }}
</pre>
{% include nav.html nav=section %}
{% endfor %}
nav.html
{% comment %} ++++ Try to reassign "section" ++++ {% endcomment %}
{% assign section = "yolo from nav.html" %}
<pre>
>> nav.hml
recursion : {{ recursion }}
include.nav : {{ include.nav | inspect }}
++++ "section" is freezed to loop value ++++
section : {{ section | inspect }}
</pre>
{% comment %} ++++ useless assignement ++++ {% endcomment %}
{% assign section=include.nav %}
{% 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 %}
{% comment %}++++ limiting recursion to 2 levels ++++{% endcomment %}
{% if item.docs and recursion < 2 %}
{% comment %}++++ incrementing "recursion" global variable ++++{% endcomment %}
{% assign recursion = recursion | plus: 1 %}
{% include nav.html nav=item %}
{% endif %}
{% endfor %}
我正在尝试编写递归 Jekyll 导航模板(包括),如“Nested tree navigation with recursion". I have a minimal example committed in jekyll-min 中所述,它基本上具有:
- 两个 top-level 个目录,每个目录一页
- 第二个top-level目录下的另一个目录,包含一页
- 一个导航模板(
_includes/docs_contents.html
)循环遍历 top-level 个目录并为每个目录启动递归遍历 - 一个递归包含(
_includes/nav.html
),它接受一个导航条目,呈现它的标题和child links,并递归地为它的[=84=中的任何目录调用它自己]仁榜 - 为每个页面呈现导航窗格和内容的布局 (
_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>
据我了解,对于每个页面,导航模板呈现应该像这样工作:
_layouts/doc.html
_includes/docs_contents.html
:迭代根级条目,为每个 调用_nav(/a/ entry)
:渲染标题,迭代docs
,渲染/a/
link,然后退出_nav(/b/ entry)
:渲染标题,迭代docs
,渲染/b/
link,然后调用_nav(/b/1/ entry)
_nav(/b/1/ entry)
:渲染标题,迭代docs
,渲染/b/1/
link,然后退出_nav(/b/ entry)
(已经在堆栈中):退出_includes/docs_contents.html
: 退出
_nav
但是,当我执行 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
没有产生任何额外的有用信息。
实际文档结构更复杂,可以任意深入,因此编写 non-recursive 模板来手动处理嵌套级别可能不是一种选择。
问得好。
在 {{ myvar | inspect }}
和限制递归的标志的帮助下,我已经成功调试了您的代码并理解了为什么会出现这种无限递归。
这是因为 docs_contents.html 中的 section
变量是在 for
循环中赋值的 freezed : 无法更改。
你第一次包含 nav.html 时,{% assign section=include.nav %}
没有改变 section
,你的代码只使用你的 for
循环.
当您第二次递归并调用 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
{% for section in site.data.docs_nav %}
{% comment %} ++++ Try to reassign "section" ++++ {% endcomment %}
{% assign section = "yolo from docs_contents.html" %}
{% assign recursion = 0 %}
<pre>
>> 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 >> {{ section | inspect }}
</pre>
{% include nav.html nav=section %}
{% endfor %}
nav.html
{% comment %} ++++ Try to reassign "section" ++++ {% endcomment %}
{% assign section = "yolo from nav.html" %}
<pre>
>> nav.hml
recursion : {{ recursion }}
include.nav : {{ include.nav | inspect }}
++++ "section" is freezed to loop value ++++
section : {{ section | inspect }}
</pre>
{% comment %} ++++ useless assignement ++++ {% endcomment %}
{% assign section=include.nav %}
{% 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 %}
{% comment %}++++ limiting recursion to 2 levels ++++{% endcomment %}
{% if item.docs and recursion < 2 %}
{% comment %}++++ incrementing "recursion" global variable ++++{% endcomment %}
{% assign recursion = recursion | plus: 1 %}
{% include nav.html nav=item %}
{% endif %}
{% endfor %}