如何让 one-time 推入 laravel blade
How to have one-time push in laravel blade
我正在尝试使用 Laravel blade 创建一个 HTML 小部件,类似于以下内容 (widget.blade.php):
@push('scripts')
<script src="{{ asset('js/foo.js') }}"></script>
<script>
...
</script>
@endpush
@push('styles')
<link href="{{ asset('css/bar.css') }}" rel="stylesheet">
@endpush
<div>
... HTML contents
</div>
我在另一个 blade 中使用小部件,例如:
<div>
...
@include('widget')
</div>
<div>
...
@include('widget')
</div>
问题是当我在一个页面中多次使用该小部件时,'scripts' 和 'styles' 重复了多次。
如何防止 Laravel 多次推送 'scripts' 和 'styles'?
一个解决方案是通过创建 pushonce
指令来扩展 Blade,如下所示:
Blade::directive('pushonce', function ($expression) {
$isDisplayed = '$__pushonce_'.trim(substr($expression, 2, -2));
return "<?php if(!isset({$isDisplayed})): {$isDisplayed} = true; $__env->startPush{$expression}; ?>";
});
Blade::directive('endpushonce', function ($expression) {
return '<?php $__env->stopPush(); endif; ?>';
});
必须添加到 AppServiceProvider
boot
方法中。
用法:
@pushonce('scripts')
<script src="{{ asset('js/foo.js') }}"></script>
<script>
...
</script>
@endpushonce
@pushonce('styles')
<script src="{{ asset('js/foo.js') }}"></script>
<script>
...
</script>
@endpushonce
请测试一下,如果有帮助请告诉我。
从 Laravel 7.25 开始,Blade 现在包含一个新的 @once 组件,它只会渲染标签中的项目一次。
https://laravel.com/docs/8.x/blade#the-once-directive
在下面的回答中,我假设您熟悉 Blade extension。此方法已在 Laravel 5.2 和 5.3 上进行测试(请参阅下面的注释)。
经过测试(所以请阅读),解决方案似乎有两个问题:
1- $isDisplayed 变量与其他包含的小部件不在同一范围内,因此每个 @include 都将其脚本推送到堆栈。结果我将其更改为:
Blade::directive('pushonce', function ($expression) {
$isDisplayed = '__pushonce_'.trim(substr($expression, 2, -2));
return "<?php if(!isset($__env->{$isDisplayed})): $__env->{$isDisplayed} = true; $__env->startPush{$expression}; ?>";
});
Blade::directive('endpushonce', function ($expression) {
return '<?php $__env->stopPush(); endif; ?>';
});
2- 该解决方案将@pushonce 的使用限制为一个小部件。即在 2 个或更多小部件(widget1.blade.php、widget2.blade.php、...)的情况下,它会阻止推送其他小部件脚本。因此,我使用以下代码将域添加到@pushonce:
Blade::directive('pushonce', function ($expression) {
$domain = explode(':', trim(substr($expression, 2, -2)));
$push_name = $domain[0];
$push_sub = $domain[1];
$isDisplayed = '__pushonce_'.$push_name.'_'.$push_sub;
return "<?php if(!isset($__env->{$isDisplayed})): $__env->{$isDisplayed} = true; $__env->startPush('{$push_name}'); ?>";
});
Blade::directive('endpushonce', function ($expression) {
return '<?php $__env->stopPush(); endif; ?>';
});
用法:
widget1.blade.php
@pushonce('scripts:widget1')
<script src="{{ asset('js/foo.js') }}"></script>
<script>
...
</script>
@endpushonce
widget2.blade.php
@pushonce('scripts:widget2')
<script src="{{ asset('js/bar.js') }}"></script>
<script>
...
</script>
@endpushonce
L 5.3 注意事项:
更改以下行:
$domain = explode(':', trim(substr($expression, 2, -2)));
到
$domain = explode(':', trim(substr($expression, 1, -1)));
我稍微修改了一下代码:
Blade::directive('pushonce', function ($expression) {
$var = '$__env->{"__pushonce_" . md5(__FILE__ . ":" . __LINE__)}';
return "<?php if(!isset({$var})): {$var} = true; $__env->startPush({$expression}); ?>";
});
Blade::directive('endpushonce', function ($expression) {
return '<?php $__env->stopPush(); endif; ?>';
});
我使用文件和行号来设置变量,所以可以简单地做:
@pushonce('scripts')
Whatever
@endpushonce
而不是:
@pushonce('scripts:widget1')
Whatever
@endpushonce
显然我没有足够的信用或其他东西来回复上面的评论。
我建议更改
$isDisplayed = '__pushonce_'.$push_name.'_'.$push_sub;
到
$isDisplayed = '__pushonce_' . md5($push_name.'_'.$push_sub);
因为如果你的推送看起来像
@pushonce('after-styles')
PHP不会因为字符串中的_
而不满意你
Appstract/laravel-blade-directives 提供了 @pushonce('scripts:widget')
的实现,以及其他方便的指令。
只需 composer require appstract/laravel-blade-directives
即可。
从 Laravel 7.25.0 开始,您可以使用 @once
blade 指令。
从 v7.25 开始,您可以使用 @once
指令。将此与 @push
和 @stack
一起使用将得到您想要的。
参考:https://laravel.com/docs/7.x/blade#the-once-directive
例子....
reveal.blade.php
<div>
<h2 onclick="reveal(this)">{{$title}}</h2>
<p class="hide">{{$text}}</p>
</div>
@once
@push('head.styles')
<style>
.hide { display: none; }
</style>
@endpush
@endonce
@once
@push('body.scripts')
<script>
window.reveal = function(element) {
let nextElementStyle = element.nextElementSibling.style;
nextElementStyle.display = nextElementStyle.display ==='block'?'none':'block';
}
</script>
@endpush
@endonce
master.blade.php
<html>
<head>
@stack('head.styles')
</head>
<body>
@include('reveal', ['title' => 'hello world', 'text'=>'This is some text'])
@include('reveal', ['title' => 'hello world', 'text'=>'This is some text'])
@stack('body.scripts')
</body>
</html>
这对我有用。
@once
@push('page_scripts')
<script type="text/javascript">
</script>
@endpush
@endonce
我正在尝试使用 Laravel blade 创建一个 HTML 小部件,类似于以下内容 (widget.blade.php):
@push('scripts')
<script src="{{ asset('js/foo.js') }}"></script>
<script>
...
</script>
@endpush
@push('styles')
<link href="{{ asset('css/bar.css') }}" rel="stylesheet">
@endpush
<div>
... HTML contents
</div>
我在另一个 blade 中使用小部件,例如:
<div>
...
@include('widget')
</div>
<div>
...
@include('widget')
</div>
问题是当我在一个页面中多次使用该小部件时,'scripts' 和 'styles' 重复了多次。
如何防止 Laravel 多次推送 'scripts' 和 'styles'?
一个解决方案是通过创建 pushonce
指令来扩展 Blade,如下所示:
Blade::directive('pushonce', function ($expression) {
$isDisplayed = '$__pushonce_'.trim(substr($expression, 2, -2));
return "<?php if(!isset({$isDisplayed})): {$isDisplayed} = true; $__env->startPush{$expression}; ?>";
});
Blade::directive('endpushonce', function ($expression) {
return '<?php $__env->stopPush(); endif; ?>';
});
必须添加到 AppServiceProvider
boot
方法中。
用法:
@pushonce('scripts')
<script src="{{ asset('js/foo.js') }}"></script>
<script>
...
</script>
@endpushonce
@pushonce('styles')
<script src="{{ asset('js/foo.js') }}"></script>
<script>
...
</script>
@endpushonce
请测试一下,如果有帮助请告诉我。
从 Laravel 7.25 开始,Blade 现在包含一个新的 @once 组件,它只会渲染标签中的项目一次。 https://laravel.com/docs/8.x/blade#the-once-directive
在下面的回答中,我假设您熟悉 Blade extension。此方法已在 Laravel 5.2 和 5.3 上进行测试(请参阅下面的注释)。
经过测试
1- $isDisplayed 变量与其他包含的小部件不在同一范围内,因此每个 @include 都将其脚本推送到堆栈。结果我将其更改为:
Blade::directive('pushonce', function ($expression) {
$isDisplayed = '__pushonce_'.trim(substr($expression, 2, -2));
return "<?php if(!isset($__env->{$isDisplayed})): $__env->{$isDisplayed} = true; $__env->startPush{$expression}; ?>";
});
Blade::directive('endpushonce', function ($expression) {
return '<?php $__env->stopPush(); endif; ?>';
});
2- 该解决方案将@pushonce 的使用限制为一个小部件。即在 2 个或更多小部件(widget1.blade.php、widget2.blade.php、...)的情况下,它会阻止推送其他小部件脚本。因此,我使用以下代码将域添加到@pushonce:
Blade::directive('pushonce', function ($expression) {
$domain = explode(':', trim(substr($expression, 2, -2)));
$push_name = $domain[0];
$push_sub = $domain[1];
$isDisplayed = '__pushonce_'.$push_name.'_'.$push_sub;
return "<?php if(!isset($__env->{$isDisplayed})): $__env->{$isDisplayed} = true; $__env->startPush('{$push_name}'); ?>";
});
Blade::directive('endpushonce', function ($expression) {
return '<?php $__env->stopPush(); endif; ?>';
});
用法:
widget1.blade.php
@pushonce('scripts:widget1')
<script src="{{ asset('js/foo.js') }}"></script>
<script>
...
</script>
@endpushonce
widget2.blade.php
@pushonce('scripts:widget2')
<script src="{{ asset('js/bar.js') }}"></script>
<script>
...
</script>
@endpushonce
L 5.3 注意事项: 更改以下行:
$domain = explode(':', trim(substr($expression, 2, -2)));
到
$domain = explode(':', trim(substr($expression, 1, -1)));
我稍微修改了一下代码:
Blade::directive('pushonce', function ($expression) {
$var = '$__env->{"__pushonce_" . md5(__FILE__ . ":" . __LINE__)}';
return "<?php if(!isset({$var})): {$var} = true; $__env->startPush({$expression}); ?>";
});
Blade::directive('endpushonce', function ($expression) {
return '<?php $__env->stopPush(); endif; ?>';
});
我使用文件和行号来设置变量,所以可以简单地做:
@pushonce('scripts')
Whatever
@endpushonce
而不是:
@pushonce('scripts:widget1')
Whatever
@endpushonce
显然我没有足够的信用或其他东西来回复上面的评论。
我建议更改
$isDisplayed = '__pushonce_'.$push_name.'_'.$push_sub;
到
$isDisplayed = '__pushonce_' . md5($push_name.'_'.$push_sub);
因为如果你的推送看起来像
@pushonce('after-styles')
PHP不会因为字符串中的_
而不满意你
Appstract/laravel-blade-directives 提供了 @pushonce('scripts:widget')
的实现,以及其他方便的指令。
只需 composer require appstract/laravel-blade-directives
即可。
从 Laravel 7.25.0 开始,您可以使用 @once
blade 指令。
从 v7.25 开始,您可以使用 @once
指令。将此与 @push
和 @stack
一起使用将得到您想要的。
参考:https://laravel.com/docs/7.x/blade#the-once-directive
例子....
reveal.blade.php
<div>
<h2 onclick="reveal(this)">{{$title}}</h2>
<p class="hide">{{$text}}</p>
</div>
@once
@push('head.styles')
<style>
.hide { display: none; }
</style>
@endpush
@endonce
@once
@push('body.scripts')
<script>
window.reveal = function(element) {
let nextElementStyle = element.nextElementSibling.style;
nextElementStyle.display = nextElementStyle.display ==='block'?'none':'block';
}
</script>
@endpush
@endonce
master.blade.php
<html>
<head>
@stack('head.styles')
</head>
<body>
@include('reveal', ['title' => 'hello world', 'text'=>'This is some text'])
@include('reveal', ['title' => 'hello world', 'text'=>'This is some text'])
@stack('body.scripts')
</body>
</html>
这对我有用。
@once
@push('page_scripts')
<script type="text/javascript">
</script>
@endpush
@endonce