Blade 模板中有關 section 的那些事
Laravel 框架中的 Blade 模板引擎,很好用,但是在官方文件中有關 Blade 的介紹並不詳細,有些東西沒有寫出來,而有些則是沒有說清楚。比如,使用中可能會遇到這樣的問題:
-
@yield
和@section
都可以預定義可替代的區塊,這兩者有什麼區別呢? -
@section
可以用@show
,@stop
,@overwrite
以及@append
來結束,這三者又有什麼區別呢?
本文試對這些問題做一個比較淺顯但是直觀的介紹。
@yield 與 @section
首先,@yield
是不可擴充套件的,如果你要定義的部分沒有預設內容讓子模板擴充套件的,那麼用 @yield($name, $default)
與之相比, @section
則既可以被替代,又可以被擴充套件,這是最大的區別。比如:
{{-- layout.master --}}
@yield('title','預設標題')
@section('content')
預設的內容
@show
{{-- home.index --}} @extends('layout.master') @section('title') @parent 新的標題 @stop @section('content') @parent 擴充套件的內容 @stop
上面的例子中,模板用 @yield
和 @section
分別定義了一個區塊,然後在子模板中去定義內容,由於 @yield
不能被擴充套件,所以即使加上了 @parent
也不起作用,輸出的內容只有“新的標題”,替換了“預設的標題”。因此最終生成的頁面只能是“預設的標題”或者“新的標題”,不能並存。而 @section
定義的部分,由於使用了 @parent
關鍵字,父模板中的內容會被保留,然後再擴充套件後新增的內容進去,輸出的內容會是 “預設的內容 擴充套件的內容”。
官方網站上的文件中並沒有涉及 @parent
關鍵字,說的是預設行為是“擴充套件”,要覆蓋需要用 @override
來結束,這是錯的,[github 上的最新文件][docs] 已經做了修正。@section
@stop
,預設是替換(注入),必須用 @parent
關鍵字才能擴充套件。而@override
關鍵字實際上有另外的應用場景。
@show 與 @stop
接下來再說說與 @section
對應的結束關鍵字,@show
, @stop
有什麼區別呢?(網上的部分文章,以及一些編輯器外掛還會提示 @endsection
, 這個在 4.0 版本中已經被移除,雖然向下相容,但是不建議使用)。
@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
</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',這會在執行到這裡時立即顯示內容,並按照模板繼承機制繼續覆蓋內容,因此最終顯示的內容會是:
ccc // 來自 page.view
<div class="zoneA">
aaa
</div>
<div class="zoneB">
</div>
<div class="zoneC">
ccc
</div>
從結果可以看到,zoneB 的內容丟失,因為沒有用 @show
告訴引擎輸出這部分的內容,而 zoneC 的內容會顯示兩次,並且還破壞了 layout.master 的頁面結構,因為 @show
出現了兩次。
@append 和 @override
剛才說到了,@override
並不是在子模板中指明內容替換父模板的預設內容,而是另有用途,那麼是如何使用呢?這又涉及到一個 section 在模板中可以多次使用的問題。也即我們所定義的每一個 section ,在隨後的子模板中其實是可以多次出現的。比如:
{{-- master --}}
@yield('content')
{{-- subview --}}
@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 --}}
@yield('content')
@yield('message')
{{-- master --}}
@section('content')
加一行內容
@append
@section('content')
再加一行內容
@append
@section('content')
加夠了,結束吧
@stop
@section('content')
都不要了,我說的。
@override
這個例子和剛才的類似,只不過最後加了一組定義。最終的輸出會是:
都不要了,我說的。
所以,在正式的專案中,有時候需要對資料進行遍歷輸出的,可以使用 @append
,而如果遍歷到了某個資料發現前面的都錯了呢?用 @override
就可以全部推翻。