实践指南

组件架构

使用组件的规则和模式

Component Architecture Illustration

简介

在 Symfony UX 中,有两个包:TwigComponentsLiveComponent。 这些包允许您在 Symfony 应用程序中创建可重用的组件。 然而,组件架构并非 Symfony 独有;它是一种可以应用于任何编程语言或框架的设计模式。 JavaScript 世界长期以来一直在许多框架(如 React、Vue 或 Svelte)中实施这种架构。 一组用于组件工作的规则和模式已经被定义。 这就是 Symfony UX 尝试紧密遵循这些规则的原因。 让我们探索一下这些规则是什么!

4 个规则

组合

页面不再仅仅是一个页面,而是一个由小的、可重用组件组成的集合。 这些组件可以组装成一个页面。 例如,可以有一个用于标题的组件,另一个用于培训列表的组件。 培训列表组件甚至可以由更小的组件组成,例如培训卡组件。 目标是创建尽可能原子化和可重用的组件。

在 Symfony 中如何工作?

在 Symfony 中,例如,您可以拥有一个 Alert 组件,其模板如下

<div class="alert alert-{{ type }}">
    <twig:ux:icon name="{{ icon }}" />
    {{ message }}
</div>

在这里您可以看到我们有一个 Alert 组件,它本身使用了 Icon 组件。或者您可以使用以下语法进行组合

<twig:Card>
    <twig:ux:icon name="info"/>
    <twig:Button>
        <twig:ux:icon name="close" />
    </twig:Button>
</twig:Card>

在这里我们有一个 Card 组件,我们使用另外两个组件来提供此组件的内容。

独立性

这是一个非常重要的规则,但并不明显。 您的组件应该存在于自己的上下文中;它不应该意识到页面的其余部分。 您应该能够将一个组件从一个页面移动到另一个页面,并且它应该完全相同地工作。 此规则使您的组件真正可重用。

在 Symfony 中如何工作?

Symfony 将页面的上下文保留在组件的上下文中。 因此,您有责任遵守这些规则。 请注意,如果上下文页面中的变量与您的组件之间存在冲突,则您的组件上下文会覆盖页面上下文。

Props (属性)

我们的组件必须保持独立性,但我们可以自定义其 props。 例如,考虑一个按钮组件。 您希望您的组件在每个页面上看起来都相同,唯一的变化是标签。 为此,您可以在按钮组件中声明一个 label prop。 当您使用按钮组件时,您可以传递您想要的标签作为 prop。 组件将在初始化时获取此 prop,并在其整个生命周期中保留它。

在 Symfony 中如何工作?

让我们以 Alert 组件作为匿名组件为例。 我们有以下模板

{% props type, icon, message %}

<div class="alert alert-{{ type }}">
    <twig:ux:icon name="{{ icon }}" />
    {{ message }}
</div>

就像这样,我们为 Alert 组件定义了三个 props。我们现在可以像这样使用它

<twig:Alert type="success" icon="check" message="Your account has been created." />

如果您的组件不是匿名组件而是类组件,您可以通过向您的类添加属性来定义 props。

#[AsTwigComponent]
class Alert
{
    public string $type;
    public string $icon;
    public string $message;
}

关于 props 有一点很重要需要注意:它们应该只沿一个方向流动,从父组件到子组件。 Props 永远不应该向上流动。 如果您的子组件需要更改父组件中的某些内容,则应使用事件。

State (状态)

State 很像 prop,但主要区别在于 state 可以在组件的生命周期内更改。 让我们以按钮组件为例。 您可以拥有一个 loading state,它可以是 truefalse。 当按钮被点击时,loading state 可以设置为 true,按钮可以显示加载器而不是标签。 加载完成后,可以将 loading state 设置为 false,按钮可以再次显示标签。

在 Symfony 中如何工作?

在 Symfony 中,您有两种不同的方法来处理 state。 第一种是直接在您的组件中使用 Stimulus。 我们建议在组件的根目录设置一个 Stimulus 控制器。

{% props label %}

<button data-controller="button" data-button-label-value="{{ label }}">
    {{ label }}
</button>

然后,您可以像这样定义您的控制器

import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
    static values = { label: String };

    connect() {
        this.element.textContent = this.labelValue;
    }

    loading() {
        this.element.textContent = 'Loading...';
    }
}

第二种方法是使用 LiveComponent 包。 如何在这两者之间做出选择? 如果您的组件的状态不需要任何后端逻辑,请保持简单并使用 Stimulus 方法。 但是,如果您的状态需要处理后端逻辑,请使用 LiveComponent。 使用 LiveComponent,live prop 就是 state。 因此,如果您想存储按钮上的点击次数,可以使用以下组件来实现

<?php

#[AsLiveComponent]
class Button
{
    use DefaultActionTrait;
    
    #[LiveProp]
    public int $clicks = 0;

    #[LiveAction]
    public function increment(): void
    {
        $this->clicks++;

        $this->save();
    }
}

结论

即使在 Symfony 中,您也可以使用组件架构。 遵循这些规则有助于您的前端开发人员处理他们熟悉的代码库,因为这些规则已经在 JavaScript 世界中得到广泛使用。

作者 Mathéo Daninos
发布时间 2024-08-02