doc: update docs/leaf.md 3f1b8a15c4

This commit is contained in:
jaywcjlove
2025-11-22 10:39:42 +00:00
parent 6d894f4c0e
commit 7d9b381ac5
4 changed files with 45 additions and 4 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -38,7 +38,7 @@
<p><a href="https://github.com/vapor/leaf">Leaf</a><a href="https://github.com/vapor/vapor">Vapor</a> 的轻量级模板引擎,用于在服务端生成动态 HTML 页面。</p>
</div></header><div class="menu-tocs"><div class="menu-btn"><svg aria-hidden="true" fill="currentColor" height="1em" width="1em" viewBox="0 0 16 16" version="1.1" data-view-component="true">
<path fill-rule="evenodd" d="M2 4a1 1 0 100-2 1 1 0 000 2zm3.75-1.5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zm0 5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zm0 5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zM3 8a1 1 0 11-2 0 1 1 0 012 0zm-1 6a1 1 0 100-2 1 1 0 000 2z"></path>
</svg></div><div class="menu-modal"><a aria-hidden="true" class="leve2 tocs-link" data-num="2" href="#入门">入门</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#leaf">Leaf</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#package">Package</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#配置">配置</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#目录结构">目录结构</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#渲染视图">渲染视图</a><a aria-hidden="true" class="leve2 tocs-link" data-num="2" href="#leaf-概述">Leaf 概述</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#模板语法">模板语法</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#内置标签示例">内置标签示例</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#表达式">表达式</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#上下文">上下文</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#条件">条件</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#elseif">#elseif</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#循环">循环</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#模板示例">模板示例</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#扩展模板">扩展模板</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#count">#count</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#lowercased">#lowercased</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#capitalized">#capitalized</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#contains">#contains</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#date">#date</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#unsafehtml">#unsafeHTML</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#dumpcontext">#dumpContext</a><a aria-hidden="true" class="leve2 tocs-link" data-num="2" href="#自定义标签">自定义标签</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#leaftag">LeafTag</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#配置标签">配置标签</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#上下文属性">上下文属性</a><a aria-hidden="true" class="leve4 tocs-link" data-num="4" href="#parameters-包含标签参数的数组">parameters 包含标签参数的数组</a><a aria-hidden="true" class="leve4 tocs-link" data-num="4" href="#使用-datarender__-方法作为上下文视图的数据">使用 Datarender(_:_:) 方法作为上下文视图的数据</a></div></div><div class="h1wrap-body"><div class="wrap h2body-exist"><div class="wrap-header h2wrap"><h2 id="入门"><a aria-hidden="true" tabindex="-1" href="#入门"><span class="icon icon-link"></span></a>入门</h2><div class="wrap-body">
</svg></div><div class="menu-modal"><a aria-hidden="true" class="leve2 tocs-link" data-num="2" href="#入门">入门</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#leaf">Leaf</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#package">Package</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#配置">配置</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#目录结构">目录结构</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#渲染视图">渲染视图</a><a aria-hidden="true" class="leve2 tocs-link" data-num="2" href="#leaf-概述">Leaf 概述</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#模板语法">模板语法</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#内置标签示例">内置标签示例</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#表达式">表达式</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#上下文">上下文</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#条件">条件</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#elseif">#elseif</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#循环">循环</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#模板示例">模板示例</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#扩展模板">扩展模板</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#count">#count</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#lowercased">#lowercased</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#capitalized">#capitalized</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#contains">#contains</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#date">#date</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#unsafehtml">#unsafeHTML</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#dumpcontext">#dumpContext</a><a aria-hidden="true" class="leve2 tocs-link" data-num="2" href="#自定义标签">自定义标签</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#leaftag">LeafTag</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#配置标签">配置标签</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#上下文属性">上下文属性</a><a aria-hidden="true" class="leve4 tocs-link" data-num="4" href="#parameters-包含标签参数的数组">parameters 包含标签参数的数组</a><a aria-hidden="true" class="leve4 tocs-link" data-num="4" href="#使用-datarender__-方法作为上下文视图的数据">使用 Datarender(_:_:) 方法作为上下文视图的数据</a><a aria-hidden="true" class="leve3 tocs-link" data-num="3" href="#相对路径拼接标签">相对路径拼接标签</a></div></div><div class="h1wrap-body"><div class="wrap h2body-exist"><div class="wrap-header h2wrap"><h2 id="入门"><a aria-hidden="true" tabindex="-1" href="#入门"><span class="icon icon-link"></span></a>入门</h2><div class="wrap-body">
</div></div><div class="h2wrap-body"><div class="wrap h3body-not-exist"><div class="wrap-header h3wrap"><h3 id="leaf"><a aria-hidden="true" tabindex="-1" href="#leaf"><span class="icon icon-link"></span></a>Leaf</h3><div class="wrap-body">
<p><code>Leaf</code> 是一种强大的模板语言,其语法受 <code>Swift</code> 启发。</p>
<ul>
@@ -75,6 +75,9 @@
<p>设置模板引擎</p>
<pre class="language-swift"><code class="language-swift code-highlight"><span class="code-line">app<span class="token punctuation">.</span>views<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token punctuation">.</span>leaf<span class="token punctuation">)</span>
</span></code></pre>
<p>配置自定义标签</p>
<pre class="language-swift"><code class="language-swift code-highlight"><span class="code-line">app<span class="token punctuation">.</span>leaf<span class="token punctuation">.</span>tags<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"relative"</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token class-name">CustomTag</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span></code></pre>
</div></div></div><div class="wrap h3body-not-exist"><div class="wrap-header h3wrap"><h3 id="目录结构"><a aria-hidden="true" tabindex="-1" href="#目录结构"><span class="icon icon-link"></span></a>目录结构</h3><div class="wrap-body">
<pre><code class="code-highlight"><span class="code-line">VaporApp
</span><span class="code-line">├── Package.swift
@@ -199,6 +202,11 @@
</span><span class="code-line"> There are no users yet :(
</span><span class="code-line">#endif
</span></code></pre>
<p>多个条件满足时才渲染内容的模板</p>
<pre class="language-html"><code class="language-html code-highlight"><span class="code-line">#if(title == "user" &#x26;&#x26; count(users) > 0):
</span><span class="code-line"> You have users!
</span><span class="code-line">#endif
</span></code></pre>
</div></div></div><div class="wrap h3body-not-exist"><div class="wrap-header h3wrap"><h3 id="elseif"><a aria-hidden="true" tabindex="-1" href="#elseif"><span class="icon icon-link"></span></a>#elseif</h3><div class="wrap-body">
<pre class="language-html"><code class="language-html code-highlight"><span class="code-line">#if(title == "Welcome"):
</span><span class="code-line"> Hello new user!
@@ -361,6 +369,7 @@
</span><span class="code-line"> <span class="token string-literal"><span class="token string">"home"</span></span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"name"</span></span><span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"John"</span></span><span class="token punctuation">]</span>
</span><span class="code-line"><span class="token punctuation">)</span>
</span></code></pre>
<p>自定义标签使用 <code>Data</code></p>
<pre class="language-swift"><code class="language-swift code-highlight"><span class="code-line"><span class="token keyword">struct</span> <span class="token class-name">NowTag</span><span class="token punctuation">:</span> <span class="token class-name">LeafTag</span> <span class="token punctuation">{</span>
</span><span class="code-line"> <span class="token keyword">func</span> <span class="token function-definition function">render</span><span class="token punctuation">(</span>
</span><span class="code-line"> <span class="token omit keyword">_</span> ctx<span class="token punctuation">:</span> <span class="token class-name">LeafContext</span>
@@ -370,6 +379,38 @@
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p><code>LeafContext</code> 包含两个重要的属性</p>
</div></div></div><div class="wrap h3body-not-exist col-span-2"><div class="wrap-header h3wrap"><h3 id="相对路径拼接标签"><a aria-hidden="true" tabindex="-1" href="#相对路径拼接标签"><span class="icon icon-link"></span></a>相对路径拼接标签</h3><div class="wrap-body">
<!--rehype:wrap-class=col-span-2-->
<pre class="language-swift"><code class="language-swift code-highlight"><span class="code-line"><span class="token keyword">struct</span> <span class="token class-name">RelativePathTag</span><span class="token punctuation">:</span> <span class="token class-name">LeafTag</span> <span class="token punctuation">{</span>
</span><span class="code-line"> <span class="token keyword">func</span> <span class="token function-definition function">render</span><span class="token punctuation">(</span><span class="token omit keyword">_</span> ctx<span class="token punctuation">:</span> <span class="token class-name">LeafContext</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-></span> <span class="token class-name">LeafData</span> <span class="token punctuation">{</span>
</span><span class="code-line"> <span class="token keyword">guard</span> ctx<span class="token punctuation">.</span>parameters<span class="token punctuation">.</span>count <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token keyword">let</span> filename <span class="token operator">=</span> ctx<span class="token punctuation">.</span>parameters<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>string <span class="token keyword">else</span> <span class="token punctuation">{</span>
</span><span class="code-line"> <span class="token keyword">throw</span> <span class="token string-literal"><span class="token string">"Missing #relative parameters"</span></span>
</span><span class="code-line"> <span class="token punctuation">}</span>
</span><span class="code-line"> <span class="token keyword">if</span> <span class="token keyword">let</span> filepath <span class="token operator">=</span> ctx<span class="token punctuation">.</span>request<span class="token operator">?</span><span class="token punctuation">.</span>url<span class="token punctuation">.</span>path<span class="token punctuation">,</span> filename<span class="token punctuation">.</span><span class="token function">hasPrefix</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"/"</span></span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token boolean">false</span> <span class="token punctuation">{</span>
</span><span class="code-line"> <span class="token keyword">return</span> <span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"</span><span class="token interpolation-punctuation punctuation">\(</span><span class="token interpolation"><span class="token function">relativePrefix</span><span class="token punctuation">(</span><span class="token keyword">for</span><span class="token punctuation">:</span> filepath<span class="token punctuation">,</span> targetFile<span class="token punctuation">:</span> filename<span class="token punctuation">)</span></span><span class="token interpolation-punctuation punctuation">)</span><span class="token string">"</span></span><span class="token punctuation">)</span>
</span><span class="code-line"> <span class="token punctuation">}</span>
</span><span class="code-line"> <span class="token keyword">return</span> <span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"</span><span class="token interpolation-punctuation punctuation">\(</span><span class="token interpolation">filename</span><span class="token interpolation-punctuation punctuation">)</span><span class="token string">"</span></span><span class="token punctuation">)</span>
</span><span class="code-line"> <span class="token punctuation">}</span>
</span><span class="code-line"> <span class="token keyword">private</span> <span class="token keyword">func</span> <span class="token function-definition function">relativePrefix</span><span class="token punctuation">(</span><span class="token keyword">for</span> pagePath<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span> targetFile<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">String</span> <span class="token punctuation">{</span>
</span><span class="code-line"> <span class="token keyword">var</span> components <span class="token operator">=</span> pagePath
</span><span class="code-line"> <span class="token punctuation">.</span><span class="token function">trimmingCharacters</span><span class="token punctuation">(</span><span class="token keyword">in</span><span class="token punctuation">:</span> <span class="token class-name">CharacterSet</span><span class="token punctuation">(</span>charactersIn<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"/"</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</span><span class="code-line"> <span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span>separator<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"/"</span></span><span class="token punctuation">)</span>
</span><span class="code-line"> <span class="token keyword">if</span> <span class="token keyword">let</span> last <span class="token operator">=</span> components<span class="token punctuation">.</span>last<span class="token punctuation">,</span> last<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"."</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line"> components <span class="token operator">=</span> components<span class="token punctuation">.</span><span class="token function">dropLast</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span><span class="code-line"> <span class="token punctuation">}</span>
</span><span class="code-line"> <span class="token keyword">let</span> cleanTarget <span class="token operator">=</span> targetFile<span class="token punctuation">.</span><span class="token function">hasPrefix</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"./"</span></span><span class="token punctuation">)</span>
</span><span class="code-line"> <span class="token operator">?</span> <span class="token class-name">String</span><span class="token punctuation">(</span>targetFile<span class="token punctuation">.</span><span class="token function">dropFirst</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</span><span class="code-line"> <span class="token punctuation">:</span> targetFile
</span><span class="code-line"> <span class="token keyword">return</span> <span class="token class-name">String</span><span class="token punctuation">(</span>repeating<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"../"</span></span><span class="token punctuation">,</span> count<span class="token punctuation">:</span> components<span class="token punctuation">.</span>count<span class="token punctuation">)</span> <span class="token operator">+</span> cleanTarget
</span><span class="code-line"> <span class="token punctuation">}</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>配置标签</p>
<pre class="language-swift"><code class="language-swift code-highlight"><span class="code-line">app<span class="token punctuation">.</span>leaf<span class="token punctuation">.</span>tags<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">"relative"</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token class-name">RelativePathTag</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span></code></pre>
<p>现在可以在 Leaf 中使用我们的自定义标签了</p>
<pre class="language-html"><code class="language-html code-highlight"><span class="code-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&#x3C;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#relative(<span class="token punctuation">"</span></span><span class="token attr-name">main.css")"</span> <span class="token punctuation">/></span></span>
</span></code></pre>
</div></div></div></div></div></div><script src="https://giscus.app/client.js" data-repo="jaywcjlove/reference" data-repo-id="R_kgDOID2-Mw" data-category="Q&#x26;A" data-category-id="DIC_kwDOID2-M84CS5wo" data-mapping="pathname" data-strict="0" data-reactions-enabled="1" data-emit-metadata="0" data-input-position="bottom" data-theme="dark" data-lang="zh-CN" crossorigin="anonymous" async></script><div class="giscus"></div></div><footer class="footer-wrap"><footer class="max-container">© 2022 <a href="https://wangchujiang.com/#/app" target="_blank">Kenny Wang</a>.</footer></footer><script async src="https://www.googletagmanager.com/gtag/js?id=G-9MWEWXSDQK"></script><script>window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

View File

@@ -1891,7 +1891,7 @@
<li><a href="https://appstore.lazycat.cloud/#/shop/detail/io.zeroc.app.quickref">懒猫微服应用商店</a></li>
</ul>
</div></div><div class="h2wrap-body"></div></div></div></div><footer class="footer-wrap"><footer class="max-container">© 2022 <a href="https://wangchujiang.com/#/app" target="_blank">Kenny Wang</a>. Updated on 2025/11/17 23:30:38</footer></footer><script async src="https://www.googletagmanager.com/gtag/js?id=G-9MWEWXSDQK"></script><script>window.dataLayer = window.dataLayer || [];
</div></div><div class="h2wrap-body"></div></div></div></div><footer class="footer-wrap"><footer class="max-container">© 2022 <a href="https://wangchujiang.com/#/app" target="_blank">Kenny Wang</a>. Updated on 2025/11/22 18:38:48</footer></footer><script async src="https://www.googletagmanager.com/gtag/js?id=G-9MWEWXSDQK"></script><script>window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-9MWEWXSDQK');</script><script src="data.js?v=1.11.1" defer></script><script src="js/fuse.min.js?v=1.11.1" defer></script><script src="js/main.js?v=1.11.1" defer></script><div id="mysearch"><div class="mysearch-box"><div class="mysearch-input"><div><svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 18 18">