模板变量需先赋值再渲染,assign()全局绑定供所有fetch()及子模板访问,fetch()参数仅限单次渲染;混用时fetch()覆盖assign()同名变量,变量名大小写敏感且不自动转驼峰,链式assign()在TP6+不可靠。

模板变量必须先赋值再渲染,否则模板里读不到任何值——这不是语法错误,而是生命周期问题。
assign() 是全局挂载,fetch() 第三个参数是单次快照
你写 View::assign('user', $user),这个 user 会绑定到当前视图引擎实例上,后续所有 fetch()(包括 {include} 子模板)都能访问;而 fetch('index', ['user' => $user]) 只影响这一次渲染,子模板里 {$user} 是空的。
- 适合用
assign()的场景:需要跨模板共享(比如导航栏用户信息)、控制器里多次调用fetch()、配合{block}和{extend}布局 - 适合用
fetch()参数的场景:纯局部页面、AJAX 返回片段、不想污染视图实例状态 - 混用时注意覆盖规则:TP6+ 默认行为是
fetch()参数会合并并覆盖已assign()的同名变量,fetch('page', ['name' => 'Tom'])之后模板里{$name}就是'Tom',不管之前有没有assign('name', 'Jerry')
变量名大小写敏感,不自动转驼峰
传进去的键名就是模板里写的变量名,fetch('user', ['user_info' => $data, 'UserId' => 123]),模板里必须写 {$user_info} 和 {$UserId},写成 {$userInfo} 或 {$userid} 都取不到值。
- 数组嵌套用点号或中括号:
{$data.name}和{$data['name']}都可以,但$data必须是assign()进去的完整数组变量 - 对象属性用冒号语法:
{$obj:name}或{$obj->name},前提是$obj是标准对象(stdClass或模型实例) - 避免在键名里用空格、短横线、中文——模板解析器不支持
链式调用 assign() 在 TP6+ 不可靠
写了 View::assign('a', 1)->assign('b', 2) 却发现 b 没生效?不是语法错,是 TP6 默认视图类的 assign() 方法返回 void 或新实例,不保证链式可用。
立即学习“PHP免费学习笔记(深入)”;
- 最稳妥写法是分步:
View::assign('a', 1); View::assign('b', 2); - 批量赋值更安全:
View::assign(['a' => 1, 'b' => 2]) - 如果用了自定义视图类或替换了门面实现,务必检查
assign()的返回值类型,别依赖链式
助手函数 view() 和门面 View::fetch() 行为一致
return view('index', ['name' => 'ThinkPHP']) 和 return View::fetch('index', ['name' => 'ThinkPHP']) 效果完全一样,都是本次渲染快照;而 view('index') 不带参数时,只渲染,不传任何变量。
- 助手函数本质是封装了
View::fetch(),没有额外魔法 - 不要指望
view()调用后还能通过View::assign()补充变量——它已经触发了渲染流程 - 调试时可临时加
dump(View::getAssign())查看当前已挂载的全部变量(TP6+ 支持)
最容易被忽略的是变量作用域穿透性:你以为 assign() 一次就万事大吉,但中间件、基类控制器、甚至模板里的 {include file="header"} 都可能悄悄覆盖或清空它。真要稳,就少混用,明确谁负责生命周期管理。



















