ttempleton / craft-nocache
一个用于在缓存块内避免缓存的Twig扩展
Requires
- php: ^8.0.2
- craftcms/cms: ^4.0.0|^5.0.0-alpha
Requires (Dev)
- craftcms/ecs: dev-main
- craftcms/rector: dev-main
README
Craft CMS的Twig扩展,用于在缓存块内避免缓存
{% cache %}
This will be cached
{% nocache %}
This won't be
{% endnocache %}
{% endcache %}
当禁用包含文件的缓存时,它也可以正常工作
{% cache %}
This will be cached
{% include 'template' %}
{% endcache %}
template.twig
{% nocache %}
This won't be
{% endnocache %}
如果您需要在nocache标签外部引用变量,您需要传递一个上下文——这就像在传递变量时include标签的工作方式一样。请注意,您不必传递全局变量,例如craft或currentUser,但您将不得不再次导入您的宏。
{% set variable = 5 %}
{% nocache with {x: variable} %}
The following value should be 5: {{ x }}
{% endnocache %}
要求
No-Cache需要Craft CMS 4.0.0或更高版本。
安装
No-Cache可以从Craft Plugin Store或使用Composer安装。
Craft Plugin Store
打开您的项目控制面板,转到插件商店,搜索No-Cache并点击安装。
Composer
打开您的终端,转到您的项目根目录并运行以下命令
composer require ttempleton/craft-nocache
然后打开您的项目控制面板,转到设置→插件,找到No-Cache并点击安装。
示例:用户信息
假设您有一个产品列表,您想在页面上显示这些产品。在每个产品下面,您想要一个“添加到购物车”按钮。但是,您只想在用户登录时显示此按钮。不仅如此,如果用户已经在他们的购物车中有此按钮,您还想禁用该按钮。不幸的是,您正在每页输出20个产品及其图像,因此缓存列表似乎是个明智的选择。
{% cache %}
{% for product in craft.entries().section('products').limit(20).all() %}
<article>
<figure>{{ product.image.one().img }}</figure>
<h1>{{ product.title }}</h1>
{% if currentUser %}
<button{{ currentUser.cart.id(product.id).count() > 0 ? ' disabled' }}>Add to cart</button>
{% endif %}
</article>
{% endfor %}
{% endcache %}
现在我们遇到了一个问题。围绕产品列表的缓存会导致currentUser逻辑几乎不起作用,因为它们将与产品一起被缓存。您不能通过将事物分开成多个缓存块来隔离用户逻辑,因为您在一个循环中,整个目的就是为了缓存获取产品条目的数据库调用。因此,您必须在JavaScript中应用您的用户检查(这远非理想),或者完全放弃缓存。
有了nocache标签,您可以非常容易地解决这个问题
{% cache %}
{% for product in craft.entries().section('products').limit(20).all() %}
<article>
<figure>{{ product.image.one().img }}</figure>
<h1>{{ product.title }}</h1>
{% nocache with {productId: product.id} %}
{% if currentUser %}
<button{{ currentUser.cart.id(productId).count() > 0 ? ' disabled' }}>Add to cart</button>
{% endif %}
{% endnocache %}
</article>
{% endfor %}
{% endcache %}
nocache块将允许您缓存整个产品列表,但仍然可以在缓存之外执行您的用户逻辑。它还允许传递上下文,因此您仍然可以在nocache块内引用产品和条目,并访问它们的属性,而无需任何额外的数据库调用。
示例:CSRF令牌
这是一个出色的安全功能,但它基本上使得使用缓存变得不可能。您可能会发现自己在前端输出一个表单,但它在模板包含和宏的堆栈中嵌得很深。在这个堆栈的顶部,您方便地围绕它包裹了一个缓存标签。
现在,您的CSRF令牌将被缓存,您基本上对此无能为力。使用nocache标签,这个问题就不再存在了
<form> {% nocache %}{{ csrfInput() }}{% endnocache %} ... </form>
现在您可以在模板中的任何地方包含这个表单,而不用担心您的CSRF令牌被缓存。作为附带说明,是的,nocache标签确实会在它们不在缓存块内时工作(尽管请尽量避免这样做,因为nocache标签确实会增加一些开销)。
注意
在nocache块内的内容将比正常情况下渲染得略有不同。在nocache块外部声明的变量实际上会在缓存块期间保留其值。
这会在以下情况中引起问题
{% set article = craft.entries().section('news').one() %}
{% cache %}
...
{% nocache with {article: article} %}
{{ article.title }}
{% endnocache %}
{% endcache %}
您可能期望,如果您更改文章的标题,它将在nocache块内更新。但这并不成立,因为由于缓存块的存在,文章本身会被缓存。
有几种解决方法。您可以将{% set articles %}语句移动到缓存块内部,这样更新文章就会导致缓存失效。在您在缓存内(但不在nocache块内)使用文章的情况下,这是一种首选方法,因为您不需要在nocache块内进行数据库调用以获取文章。
{% cache %}
{% set article = craft.entries().section('news').one() %}
...
{% nocache with {article: article} %}
{{ article.title }}
{% endnocache %}
{% endcache %}
另一种选择是在nocache块内查询文章。这可能比上述解决方案更好,因为更新文章标题不会导致缓存失效。在这种情况下,区别在于nocache块的内容现在将导致数据库调用。
{% cache %}
...
{% nocache %}
{% set article = craft.entries().section('news').one() %}
{{ article.title }}
{% endnocache %}
{% endcache %}
每种情况都不同,所以请根据您的最佳判断来使用。
非常感谢Ben Fleming创建No-Cache,并允许我接管它。