Blade中的@section与@yield等标签介绍
Laravel 框架中的 Blade 模板引擎,很好用,但是在官方文档中有关 Blade 的介绍并不详细,有些东西没有写出来,而有些则是没有说清楚。
比如,使用中可能会遇到这样的问题:
@yield
和@section
都可以预定义可替代的区块,这两者有什么区别呢?@section
可以用@show
,@stop
,@overwrite
以及@append
来结束,又有什么区别呢?
本文试对这些问题做一个比较浅显但是直观的介绍。
blade模板基础使用
Blade 是使用模板继承(template inheritance)
和区块(sections)
。所有的 Blade 模板后缀名都要命名为.blade.php
<!-- 文件存储在 resources/views/layouts/master.blade.php -->
<html>
<head>
<title>App Name - @yield('title')</title>
</head>
<body>
@section('sidebar')
This is the master sidebar.
@show
<div class="container">
@yield('content')
</div>
</body>
</html>
在视图模板中使用 Blade 页面布局
@extends('layouts.master')
@section('title', 'Page Title')
@section('sidebar')
@parent
<p>添加在sidebar中的内容</p>
@stop
@section('content')
<p>替换yield中的内容</p>
@stop
请注意 如果视图继承(extend)
了一个 Blade 页面布局会将页面布局中定义的区块用视图的所定义的区块重写。
如果想要将页面布局中的区块内容也能在继承此布局的视图中呈现,那就要在区块中使用@parent
语法指令,通过这种方式可以把内容附加到页面布局中,我们会在侧边栏区块或者页脚区块看到类似的使用。
有时候,如您不确定这个区块内容有没有被定义,您可能会想要传一个默认的值给@yield
。您可以传入第二个参数作为默认值给@yield
:
@yield('content', '默认值')
@yield 与 @section
首先,@yield
是不可扩展的,如果你要定义的部分没有默认内容让子模板扩展的,那么用@yield($name, $default)
的形式会比较方便。如果你在子模板中并没有指定这个区块的内容,它就会显示默认内容,如果定义了,就会显示你定义的内容。
而@section
则既可以被替代,又可以被扩展,这是最大的区别。
{{-- layout.master --}}
@yield('title','默认标题')
@section('content')
默认的内容
@show
{{-- home.index子模板 --}}
@extends('layout.master')
@section('title')
@parent // yield无法扩展,加了parent也无效
新的标题
@stop
@section('content')
@parent
扩展的内容
@stop
上面的例子中,模板用@yield
和@section
分别定义了一个区块,然后在子模板中去定义内容,由于@yield
不能被扩展,所以即使加上了@parent
也不起作用,输出的内容只有“新的标题”,替换了父模板中的“默认的标题”。因此最终生成的页面只能是“新的标题”,子模板中的内容与父模板中的内容无法共存。
@section
定义的部分,由于使用了@parent
关键字,父模板中的内容会被保留,然后再扩展后添加的内容进去,输出的内容会是 “默认的内容 扩展的内容”。
@section
加上@stop
,默认是替换(注入),必须用@parent
关键字才能扩展。
@show 与 @stop
接下来再说说与 @section 对应的结束关键字,@show
,@stop
有什么区别呢?
@show
指的是执行到此处时将该 section 中的内容输出到页面。
@stop
则只是进行内容解析,并且不再处理当前模板中后续对该section的处理,除非用 @override覆盖。
通常来说,在首次定义某个 section 的时候,应该用@show
,而在替换它或者扩展它的时候,不应该用@show
,应该用@stop
。
{{-- layout.master --}}
<div id="zoneA">
@section('zoneA')
AAA
@show
</div>
<div id="zoneB">
@section('zoneB')
BBB
@stop // 没有以@show结束 所以不会显示
</div>
<div id="zoneC">
@section('zoneC')
CCC
@show
</div>
子模板中:
{{-- page.view --}}
@extends('layout.master')
@section('zoneA')
aaa
@stop
@section('zoneB')
bbb
@stop
@section('zoneC')
ccc
@show
在layout.master
中,用@stop
来结束 "zoneB",由于整个模板体系中,没有以@show
结束的 "zoneB" 的定义,因此这个区块不会被显示。
在page.view
中,用@show
定义了 'zoneC',这会在执行到这里时立即显示内容,并按照模板继承机制继续覆盖内容。
因此最终显示的内容会是:
从结果可以看到,zoneB 的内容丢失,因为没有用@show
告诉引擎输出这部分的内容,而 zoneC 的内容会显示两次,并且还破坏了layout.master 的页面结构,因为@show
出现了两次。
如果我们针对zoneB再来修改一下:
父模板中的zoneB不变,在子模板中的zoneB改变一下:
@section('zoneB')
bbb
@show
@append 和 @override(仅供参考)
@override
并不是在子模板中指明内容替换父模板的默认内容,而是另有用途,那么是如何使用呢?
这又涉及到一个 section 在模板中可以多次使用的问题。也即我们所定义的每一个 section ,在随后的子模板中其实是可以多次出现的。比如:
{{-- 父模板master --}}
<div>
@yield('content')
</div>
在子模板中:
@extends('master')
@section('content')
加一行内容
@append
@section('content')
再加一行内容
@append
@section('content')
加够了,到此为止吧。
@stop
在上例中,我在父级模板中只定义了一个名为 "content
" 的 section,而在子模板中三次指定了这个 section 的内容。 这个例子最终的输出是:
<div>
加一行内容
再加一行内容
加够了,到此为止吧。
</div>
三次指定的内容都显示出来了,关键就在于@append
这个关键字,它表明“此处的内容添加到”,因此内容会不断扩展。而最后用了@stop
,表示这个 section 的处理到此为止。
如果在后面继续用@append
或者@stop
来指定这个section
的内容,都不会生效。除非用@override
来处理。
@override
的意思就是“覆盖之前的所有定义,以这次的为准”。比如:
{{-- master --}}
<div>
@yield('content')
@yield('message')
</div>
<div>
@section('content')
加一行内容
@append
@section('content')
再加一行内容
@append
@section('content')
加够了,结束吧
@stop
@section('content')
都不要了,我说的。
@override
</div>
这个例子和刚才的类似,只不过最后加了一组定义。最终的输出会是:
代码如下:
<div>
都不要了,我说的。
</div>
所以,在正式的项目中,有时候需要对数据进行遍历输出的,可以使用@append
,而如果遍历到了某个数据发现前面的都错了呢?用@override
就可以全部推翻。