(Shopify) Liquid


最近一直在维护 Shopify 店铺,做一些 theme 功能定制的需求。接触了2个付费的 theme,里面的代码虽然不敢恭维,但是店铺的页面性能都非常好。再次重拾了 server 渲染能力。

Liquid 由 Shopify 创建的开源的模版语言,是 Shopify theme 的基石。Shopify Liquid文档,包含一些内置的对象,可用于读取店铺的数据。

对了,这个博客是用 Jekyll 生成页面,也使用 Liquid 作为其模版语言。

语法

使用 {{}} 输出 Liquid Object 或变量的内容。

使用 {%%} 包裹称为 Tags,用来 assign 变量、创建 condition/loops,从而控制模版的 flow,实现动态输出。Tags不会渲染到页面上。

{{--}}{%--%} 加一个 -,来移除渲染 tag 左右边的 whitesapce。

{{}} 和 变量赋值时,使用 | 管道符,结合 Filters,可以改变 Liquid 对象或变量的输出。

基本数据类型 String、Number、Boolean、Array(跟js差不多)、Nil。

只有变量值为 Nil, false 条件判断结果是 falsy,其余情况都是 truthy。

Liquid 语言里使用 snippet 封装代码,同 js 的模块概念。

实践tips

  • 移除变量赋值语句之后的whitespace,{% assign variable = "value" -%} 只需要在闭合标记处添加横线
  • 当 Tags 或输出返回 Nil,不会有任何东西打印到页面上
  • 不能直接创建/initialize array,可以通过 split filter 把一个 string 打断成一个 substring 的数组
  • 某个条件发生的概率小时,使用 {% unless condition %}logic{% endunless%},即仅当 condition 不满足时,才执行logic
  • 可以给变量 assgin 布尔值,但不能把 condition 判断的结果直接赋值给变量。解法:
{%- assign hasAdProducts = false -%}
{%- if ad_products.size > 0 -%}
  {%- assign hasAdProducts = true -%}
{%- endif -%}

变量作用域

渲染进模版里的 snippet 代码并不自动 access 父模版中使用变量 tags assign 赋值的 variables。同理,snippet 内部的变量,不能被外部的代码读取。

读取父模版变量的方法:

{% assign all_products = collections.all.products %}
{% render 'snippet', products: all_products %}

可以以逗号分隔,传递多个变量。

此外,通过 with 参数,将父模版中定义的变量值传递给与 snippet 文件同名的内部变量。

父模版:

{% assign c = collections.all.products %}
{% render 'collection-product-list' with c %}

snippet:

<ul>
{% for product in collection-product-list %}
<li><a href="{{ product.url}}">{{ product.title}}</a>
{% endfor %}
<ul>

javascript liquid 标签

Each section can can contain JavaScript and Stylesheets, these two languages are used within liquid tags: {% javascript %}{% endjavascript %}{% stylesheet %}{% endstylesheet %}.

When placing code inside of these two tags, Shopify compiles each piece into shopify_compiled.js & shopify_compiled.css. This allows for greater readability within each section’s code as you don’t have to search through a long document to find each piece.

实例

capture

捕获开闭标签之间的字符串,赋值给变量。使用 capture 创建的变量也是 string.

理论上,只要 snippet 的输出结果是字符串,就可以通过这个方法实现代码复用。

{%- capture t_title -%}
  {%- render 'translation_text_node', original_text : settings.title, prefix: 'layout.header_menu.' -%}
{%- endcapture -%}
{%- render 'my_title',title:t_title -%}

读取URL参数

一开始想用官方的 request 对象,发现 request.path 输出值不含 query。在论坛里搜到了一个答案

尝试了一下,是可以的。写成 snippet 如下,文件名 get_url_param.liquid

{%- assign page_url = content_for_header | split:'"pageurl":"' | last | split:'"' | first -%}
{%- assign query_param = '' -%}
{%- if page_url contains "?" -%}
  {%- assign query_string = page_url | split:'?' | last -%}
  {%- assign qry_parts = query_string | split:'&' -%}
  {%- for part in qry_parts -%}
    {%- assign key_and_value = part | split:'=' -%}
    {%- if key_and_value.size > 1 -%}
      {% if key_and_value[0] == param_key %}
        {%- assign query_param = key_and_value[1] -%}
      {% endif%}
    {%- endif -%}
  {%- endfor -%}
{%- endif -%}
{{ query_param }}

使用:

{%- capture query -%}{%- render 'get_url_param', param_key: 'pid' -%}{%- endcapture -%}

Convert String to Number

{% assign num = "2021" -%}
{{ num | abs }}

Convert Number to String

{% assign str = 2021 -%}
{{ str | append: '' }}

Array 操作

是否可以使用 filter 排除数组中的某些 items?目前好像不行。

Find a special item in string array

官方文档里对于 where 过滤器的使用只有 object array。需求是从字符串型数组中找到并返回含有指定 substring 的数组项。参考主题代码试了下,是可以的,但不确定这种方法的稳定性。

{%- assign tag_str = product.tags | where: 'pno_' -%}