neos/fusion-afx

Neos.Fusion 的 JSX 启发式紧凑语法

资助包维护!
shop.neos.io/neosfunding

安装次数: 729,723

依赖项: 57

建议者: 2

安全: 0

星标: 25

关注者: 5

分支: 9

公开问题: 7

类型:neos-package


README

Neos.Fusion 的 JSX 启发式紧凑语法

该存储库是 Neos 项目(更多信息请访问 www.neos.io <https://www.neos.io/>)的一部分包的只读 subsplit。

此包提供了一种融合预处理器,可以将紧凑的 xml-like 语法扩展为纯融合代码。这允许编写紧凑的组件,无需单独的模板文件,并使定义的原型能够实现未计划的扩展性,因为生成的融合代码可以根据需要被覆盖和从外部控制。

安装

Neos.Fusion.AFX 通过 Packagist 提供。只需将 "neos/fusion-afx" : "~1.0.0" 添加到 composer.json 的 require 部分即可,或者运行 composer require neos/fusion-afx

我们使用语义版本控制,每次重大更改都会增加主版本号。

用法

使用此包,以下融合代码

prototype(Vendor.Site:Example) < prototype(Neos.Fusion:Component) {

    title = 'title text'
    subtitle = 'subtitle line'
    imageUri = 'https://dummyimage.com/600x400/000/fff'

    #
    # The code afx`...` is converted to the fusion code below at parse time.
    # Attention: Currently there is no way to escape closing-backticks inside the Expression.
    #
    renderer = afx`
       <div>
         <h1 @key="headline" class="headline">{props.title}</h1>
         <h2 @key="subheadline" class="subheadline" @if.hasSubtitle={props.subtitle ? true : false}>{props.subtitle}</h2>
         <Vendor.Site:Image @key="image" uri={props.imageUri} />
       </div>
    `
}

将被转换,解析,然后缓存并评估为以下融合代码的等价物

prototype(Vendor.Site:Example) < prototype(Neos.Fusion:Component) {

    title = 'title text'
    subtitle = 'subtitle line'
    imageUri = 'https://dummyimage.com/600x400/000/fff'

    renderer = Neos.Fusion:Tag {
        tagName = 'div'
        content = Neos.Fusion:Join {
            headline = Neos.Fusion:Tag {
                tagName = 'h1'
                content = ${props.title}
                attributes.class = 'headline'
            }
            subheadline = Neos.Fusion:Tag {
                tagName = 'h2'
                content = ${props.subtitle}
                attributes.subheadline = 'subheadline'
                @if.hasSubtitle = ${props.subtitle ? true : false}
            }
            image = Vendor.Site:Image {
                uri = ${props.imageUri}
            }
        }
    }
}

AFX 语言规则

忽略外部元素周围的全部空白。与换行符连接的空白被视为无关紧要且被忽略。

HTML 标签(无命名空间的标签)

HTML 标签被转换为 Neos.Fusion:Tag 对象。afx 标签的所有属性都作为标签属性渲染。

以下 html

<h1 class="headline" @if.hasHeadline={props.headline ? true : false}>{props.headline}</h1>

转换为以下内容

Neos.Fusion:Tag {
    tagName = 'h1'
    attributes.class = 'headline'
    content = ${props.headline}
    @if.hasHeadline = ${props.headline ? true : false}
}

如果一个标签是自闭合的并且没有内容,它将作为自闭合融合标签渲染。

<br/>

转换为以下内容

Neos.Fusion:Tag {
    tagName = 'br'
    selfClosingTag = true
}

融合对象标签(命名空间标签)

所有命名空间标签都被解释为原型名称,所有属性都被传递为顶层融合属性。

以下 html

<Vendor.Site:Prototype type="headline" @if.hasHeadline={props.headline ? true : false}>{props.headline}</Vendor.Site:Prototype>

转换为以下内容

Vendor.Site:Prototype {
    type = 'headline'
    content = ${props.headline}
    @if.hasHeadline= ${props.headline ? true : false}
}

扩展语法

afx 支持从 ES6 中的扩展语法,以将多个属性应用于单个表达式中的融合原型。

<Vendor.Site:Component {...expression} />

转换为以下内容

Vendor.Site:Component {
    @apply.spread_1 = ${expression}
}

扩展可以与 props 结合使用,定义的顺序是保留的,扩展将覆盖之前定义的 props,但随后又会被后来的 props 覆盖。

保留顺序的扩展和属性的组合通过仅渲染第一个扩展之前的属性作为经典融合属性来实现。扩展和随后的 props 被转换为融合 @apply 语句,因此可以覆盖所有 props,并按定义的顺序评估。

<Vendor.Site:Component title="example" {...data} description="description" {...moreData} />

转换为以下内容

Vendor.Site:Component {
    title = 'example'
    @apply.spread_1 = ${data}
    @apply.spread_2 = Neos.Fusion:DataStructure {
        description = 'description'
    }
    @apply.spread_3 = ${moreData}

}

此功能基于融合的 @apply 语法,因此仅在 Neos > 4.2 中有效。

标签子元素

根据找到的子节点数量,afx 节点下子节点的处理方式不同。

单个标签子元素

如果一个 AFX 标签正好包含一个子元素,则此子元素将直接渲染到 content 属性中。然后解释此子元素为字符串、eel 表达式、html 或融合对象标签。

以下 AFX 代码

<h1>{props.title}</h1>

转换为以下内容

Neos.Fusion:Tag {
    tagName = 'h1'
    content = {props.title}
}

多个标签子元素

如果一个 AFX 标签包含多个子元素,则内容将被渲染为 Neos.Fusion:Joincontent 属性中。子元素被解释为字符串、eel 表达式、html 或融合对象标签。

以下 AFX 代码

<h1>{props.title}: {props.subtitle}</h1>

转换为以下内容

Neos.Fusion:Tag {
    tagName = 'h1'
    content = Neos.Fusion:Join {
        item_1 = {props.title}
        item_2 = ': '
        item_3 = ${props.subtitle}
    }
}

标签子元素内部的 @key 属性会更改融合属性的名称,以便将渲染数组子元素。如果没有提供 @key 属性,则使用 index_x 开始,x=1

<Vendor.Site:Prototype @children="text">
    <h2 @key="title">{props.title}</h1>
    <p @key="description">{props.description}</p>
</Vendor.Site:Prototype>

转换为以下内容

Vendor.Site:Prototype {
    text = Neos.Fusion:Join {
        title = Neos.Fusion:Tag {
            tagName = 'h2'
            content  = ${props.title}
        }
        description = Neos.Fusion:Tag {
            tagName = 'p'
            content  = ${props.description}
        }
    }
}

标签子元素的 @path 属性可用于将特定的 afx 子元素渲染到给定的融合路径中,而不是将其包含到 content 中。这允许将 AFX 子元素渲染到不同的 props 中,并将融合原型分配给 props。

<Vendor.Site:Prototype>
    <h2 @path="title">{props.title}</h1>
    <p @path="description">{props.description}</p>
</Vendor.Site:Prototype>

转换为以下内容

Vendor.Site:Prototype {
    title = Neos.Fusion:Tag {
        tagName = 'h2'
        content  = ${props.title}
    }
    description = Neos.Fusion:Tag {
        tagName = 'p'
        content  = ${props.description}
    }
}

元属性

通常,所有元属性都以前置的 @ 符号开头。

使用 @path 属性可以将子节点直接渲染到 Fusion:Object 的给定路径下,而不是包含到 content 属性中。

@children 属性定义了用于将当前标签的内容/子节点渲染到其中的属性。子节点的默认属性名称是 content

使用 @key 属性可以在渲染数组时定义其兄弟项中的项目属性名称。如果没有定义 @key,则从 `x=1` 开始使用 index_x

注意:@path@children@key 只支持字符串值,不支持表达式。

所有其他元属性都直接添加到生成的原型中,可用于 @if 或 @process 语句。

空白和换行符

Afx 不是 html,对代码进行了一些简化以优化生成的 fusion,并允许以结构化方式表示组件层次结构。

以下规则适用

  1. 文本字面量内部的换行符和空白字符合并为一个空格。
<h1>
    This is a string literal
    with multiple lines
    that shall collapse
    to spaces.
</h1>

转换为以下内容

Neos.Fusion:Tag {
    tagName = 'h1'
    content = 'This is a string literal with multiple lines that shall collapse to spaces.'
}
  1. 与换行符相连的换行符和空白字符被视为无关紧要,并会被忽略。
<h1>
	{'eelExpression 1'}
	{'eelExpression 2'}
</h1>

转换为以下内容

Neos.Fusion:Tag {
	tagName = 'h1'
	contents = Neos.Fusion:Join {
		item_1 = ${'eelExpression 1'}
		item_2 = ${'eelExpression 2'}
	}
}
  1. 单行元素之间的空格被视为有意义的,并会被保留。
<h1>
	{'eelExpression 1'} {'eelExpression 2'}
</h1>

转换为以下内容

Neos.Fusion:Tag {
	tagName = 'h1'
	contents = Neos.Fusion:Join {
		item_1 = ${'eelExpression 1'}
		item_2 = ' '
		item_3 = ${'eelExpression 2'}
	}
}

HTML 注释

Afx 接受 html 注释,但它们不会被转换到任何 fusion。然而,如果您正在将 html 转换为 afx,则允许在内部有注释,并且您可以使用注释在测试期间禁用 afx 的部分。

foo<!-- comment -->bar

转换为以下内容

Neos.Fusion:Join {
    item_1 = 'foo'
    item_2 = 'bar'
}

示例

使用 Neos.Fusion:Collection 渲染集合

对于列表或菜单的渲染,表示组件通常将接收预处理数据的数组作为 prop。要遍历此类数组,可以在 afx 中使用 Neos.Fusion:Collection

prototype(Vendor.Site:IterationExample) < prototype(Neos.Fusion:Component) {

    # array {[href:'http://www.example_1.com', title:'Title 1'], [href:'http://example_2.com', title:'Title 2']}
    items = null

    renderer = afx`
        <ul @if.has={props.items ? true : false}>
        <Neos.Fusion:Collection collection={props.items} itemName="item">
            <li @path='itemRenderer'>
                <Vendor.Site:LinkExample {...item} />
            </li>
        </Neos.Fusion:Collection>
        </ul>
    `
}

使用 Neos.Fusion:Augmenter 增强子组件

Neos.Fusion:Augmenter 可以用来向渲染内容添加额外的属性。这允许在不扩展组件 API 的情况下提供一些渲染灵活性。这是一个有用的模式,在只需要额外的类的情况下,可以避免不必要的标签包装。

prototype(PackageFactory.AtomicFusion.AFX:SliderExample) < prototype(Packagefactory.AtomicFusion:Component) {
  images = ${[]}
  renderer = afx`
     <div class="slider">
        <Neos.Fusion:Collection collection={props.images} itemName="image" iterationName="iteration" @children="itemRenderer">
            <Neos.Fusion:Augmenter class="slider__slide" data-index={iteration.index}>
                <Vendor.Site:ImageExample {...image} />
            </Neos.Fusion:Augmenter>
        </Neos.Fusion:Collection>
     </div>
  `
}

示例遍历图像列表,并使用 Vendor.Site:ImageExample 渲染每个图像,同时 Neos.Fusion:Augmenter 从外部添加类和数据属性。

许可证

LICENSE 文件