6: Views
视图直接位于 .phlo 文件中。视图被转译为返回 HTML 的 PHP 方法。您可以使用 view(...) 来渲染视图。
在一切之前有一个规则:空行结束视图。在 view ...: 之后的第一个空行结束该块;其后的 HTML 将变为控制器代码,构建将停止并显示 HTML outside a view。切勿在视图内部插入空行以进行视觉间隔。
6.1: 声明
匿名视图:
view:
<p>Test</p>
命名视图:
view home:
<h1>Welcome</h1>
带参数的视图:
view greeting($name):
<p>Hello $name</p>
调用它:
method show => view($this->greeting('Jordi'))6.2: 多行块
多行视图会一直运行,直到遇到空行。因此,请不要在视图 HTML 中间放置空行。
view:
<section>
<h1>Welcome</h1>
<p>Intro</p>
</section>
view footer:
<footer>Phlo</footer>6.3: HTML 简写
Phlo 支持紧凑的 id/class 简写:
view:
<p#intro.lead/>
这变成了:
<p id="intro" class="lead"></p>
一个尾随斜杠使得标签在源代码中自闭合,而 Phlo 将其转换为正常的开闭标签。
一个硬性规则:一个标签要么使用简写,要么使用显式的 class/id 属性,绝不能两者兼用。将它们结合在一起可能会导致重复属性,浏览器只保留第一个,默默丢弃动态的那个。当类列表的一部分是动态时,将整个内容写为一个属性;对于完全静态的标签,保留简写:
<a.site-logo href="/"> valid: fully static
<a class="card {( $active ? 'is-active' : void )}"> valid: dynamic, one attribute
<a.card class="$extra"> INVALID: duplicate class attribute6.4: 文本和变量
您可以直接在文本中使用普通变量和简单属性:
view($name):
<p>Hello $name</p>
<p>$this->title</p>
对于方法调用、链式访问或表达式,请使用 {{ ... }}:
view:
<p>{{ $this->label('start') }}</p>
<p>{{ $this->record->title }}</p>
<p>{{ $this->count > 1 ? 'Multiple' : 'One' }}</p>
{( ... )} 作为一种简短的表达形式存在,并在内部转换为 {{ (...) }},但不要将其用作默认示例。在文档和应用代码中,{{ ... }} 通常更清晰。
{{ ... }} 和 {( ... )} 都会原样输出它们的值。对于不受信任或用户提供的文本,请使用 {[ ... ]},它会对其值进行 HTML 转义(与写 {{ esc(...) }} 相同):
view($comment):
<p>{[ $comment ]}</p>
<p>{[ $this->user->name ]}</p>
当值可能包含浏览器会解释为标记的字符时,请使用 {[ ... ]};对于您已经安全处理的值,请保留 {{ ... }}。
6.5: 可翻译的视图文本
对于静态可翻译文本,请使用语言简写:
view:
<h1>{nl: Welkom}</h1>
<p>{nl: Hallo wereld}</p>
带参数:
view($name):
<p>{nl: Hallo %s ($name)}</p>
使用简写形式;它更短,并且一目了然地显示文本的源语言。
6.6: 属性
属性值如果没有空格或变量,可以不加引号:
view:
<a href=/contact>Contact</a>
使用引号来表示变量或表达式:
view:
<a href="$this->url">Link</a>
<a href="{{ $this->url('contact') }}">Contact</a>
属性值直接插入 $var、$this->prop 和 %instance->prop,包括带有字面后缀的情况。将普通属性访问包裹在 {{ }} 中是多余的;请将 {{ }} 保留用于调用,将 {( )} 用于表达式:
<a href="%base->view/install"> valid: direct interpolation plus suffix
<a href="{{ %base->view }}/install"> works, but redundant and ugly: avoid6.7: 控制流
将控制流标签放在单独的行上:
view:
<ul>
<foreach $this->items AS $item>
<li>$item->title</li>
</foreach>
</ul>
使用 if:
view:
<if $this->active>
<p>Active</p>
<else>
<p>Inactive</p>
</if>6.8: Rendering
view(...) builds and renders the response, but it does not stop PHP execution. Return it from a route guard or let the routine end immediately afterwards. Build composite pages in a single view, or let a view include other view methods inline with {{ ... }}.
route both GET home => view($this)
view:
<main>
{{ $this->hero }}
{{ $this->content }}
</main>
view hero:
<header>
<h1>$this->title</h1>
</header>
view content:
<section>
<p>{nl: Welkom op de site}</p>
</section>
All view() parameters are optional and named:
| Parameter | Does |
|---|---|
title |
Page title, combined with the app title via title() |
css / js / defer |
Extra assets next to the namespace bundles |
options |
Body class list |
settings |
Body data-* attributes |
ns |
Bundle namespace (default app; see chapter 2) |
path |
Browser URL; false keeps the current URL |
inline |
Embed local css/js into the HTML instead of linking |
bodyAttrs / htmlAttrs |
Extra attributes on <body> / <html> |
lang |
Page language |
| trailing named args | Any apply command, e.g. scroll: 0, trans: 'fade' |
App-level defaults come from %app props with the same names. The <head> is further fed by %app->description, %app->viewport, %app->themeColor, %app->nonce, %app->head, %app->link and %app->version (the asset cache-buster).
6.9: Apply commands
apply() accepts named arguments where each key is a DOM mutation or UI action. The runtime core provides the basics; resources can register extra commands via app.mod.<name> (such as DOM/toasts for toast: or DOM/dialog for alert:).
DOM mutations
| Cmd | Argument | Effect |
|---|---|---|
inner |
{selector: html} |
el.innerHTML = html |
outer |
{selector: html} |
el.outerHTML = html |
before / after |
{selector: html} |
Insert adjacent |
prepend / append |
{selector: html} |
Insert inside, first/last |
remove |
selector or array | Remove elements |
attr |
{selector: {attr: value}} |
Set/remove (null = remove) |
class |
{selector: 'a b -c !d'} |
Add / remove (-) / toggle (!) |
value |
{selector: value} |
Form value |
data |
{selector: {key: value}} |
el.dataset[key] |
App state
| Cmd | Effect |
|---|---|
title |
document.title |
lang |
html.lang |
options |
Body classes (replaces) |
settings |
Body data attributes |
path |
history.pushState (URL changes without reload) |
trans |
View-transition class (forward/backward/...) |
scroll |
int (pixels) or #anchor |
Assets (once per href/src)
css, js, defer, add a link or script; already-loaded URLs are ignored.
Navigation and callbacks
| Cmd | Effect |
|---|---|
location |
Path or true (reload current path); an external URL does location.assign() |
call |
Call app[name]() after the apply |
Meta
| Cmd | Effect |
|---|---|
log / error |
console.log / console.error on the client |
phlo |
Server-side debug trace, logged in the browser console (debug mode) |
Resource mods, available once the corresponding resource is loaded:
| Cmd | Resource |
|---|---|
toast |
DOM/toasts |
alert / confirm / prompt |
DOM/dialog |
store |
DOM/store |
sync |
DOM/store (a mirrored value: applied, never sent on again) |
setvar |
DOM/CSS.var |
template |
DOM/template |
No build-time check on apply keys. A typo (
inner→innr) is silently ignored. Keep this table at hand, or consult/opt/phlo/docs/apply-protocol.mdfor the complete, up-to-date reference including edge cases and stream semantics.
Example combining multiple commands:
route async POST item save {
if (!$item = item::save(%payload)) return apply(
error: 'Save failed',
class: ['[name=title]' => '!error'],
)
apply(
outer: ['#item-'.$item->id => $this->itemView($item)],
toast: 'Saved',
scroll: '#item-'.$item->id,
trans: 'fade',
)
}
Reference. The DOM and view resources are documented per node in the Manual, generated from the resource files so it never drifts from the code.
最近更新于 2026年8月23日