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,并允许我接管它。