Solid.js 最新官方文档翻译(9)—— 控制流程(条件渲染、动态渲染、列表渲染)

295 阅读7分钟

前言

Solid.js,一个比 React 更 react 的框架。每一个使用 React 的同学,你可以不使用,但不应该不了解

目前 Solid.js 发布了最新的官方文档,但却缺少对应的中文文档。为了帮助大家学习 Solid.js,为爱发电翻译文档。

我同时搭建了 Solid.js 最新的中文文档站点:solid.yayujs.com ,欢迎勘误。

虽说是翻译,但个人并不喜欢严格遵守原文,为了保证中文阅读流畅,会删减部分语句,对难懂的部分也会另做补充解释,希望能给大家带来一个好的中文学习体验。

欢迎围观我的“朋友圈”、加入“低调务实优秀中国好青年”前端社群,分享技术,带你成长。

条件渲染

条件渲染是指根据条件显示不同 UI 元素的过程。这是 UI 开发中的常见模式,通常用于根据用户输入、数据或其他条件显示或隐藏元素。

Solid 提供了专用的组件以更直接和可读的方式处理条件渲染。

Show

当条件评估为真时,<Show> 渲染其子级。与 JavaScript 中的三元运算符类似,它使用 JSX 中的控制逻辑流来确定要渲染的内容。

<Show> 有一个 when 属性,用于确定是否渲染其子级。当它所依赖的状态或属性发生变化时,该属性将被重新评估。该属性可以是布尔值,也可以是返回布尔值的函数。

import { Show } from "solid-js"

<Show when={data.loading}>
  <div>Loading...</div>
</Show>

<Show> 有一个 fallback 属性,用于指定条件评估为 false 时要渲染的内容。该属性可以返回一个 JSX 元素。

import { Show } from "solid-js"

<Show when={!data.loading} fallback={<div>Loading...</div>}>
    <h1>Hi, I am {data().name}.</h1>
</Show>

如果需要处理多个条件,可以嵌套 <Show> 来处理每个条件。

import { Show } from "solid-js"

<Show when={data.loading}>
  <div>Loading...</div>

  <Show when={data.error}>
    <div>Error: {data.error}</div>
  </Show>
</Show>

Switch and Match

当需要处理多个条件时,使用嵌套的 <Show> 组件来管理逻辑流可能会很困难。为此,Solid 提供了 <Switch><Match> 组件。

与 JavaScript 的 switch/case 结构类似,<Switch> 包装了多个 <Match> 组件,以便按顺序评估每个条件。第一个值为 true 的 <Match> 组件将渲染其子组件,其余组件将被忽略。

import { Switch, Match } from "solid-js"

<Switch>
  <Match when={condition1}>
    <p>Outcome 1</p>
  </Match>

  <Match when={condition2}>
    <p>Outcome 2</p>
  </Match>
</Switch>

<Show> 类似,每个 <Match> 组件都有一个 when 属性,用于确定是否渲染其子组件。还可以将可选的 fallback 属性传递给 <Switch>,以指定当没有 <Match> 组件的计算结果为 true 时渲染的内容。

import { Switch, Match } from "solid-js"

<Switch fallback={<p>Fallback content</p>}>
    <Match when={condition1}>
        <p>Outcome 1</p>

    </Match>

    <Match when={condition2}>
        <p>Outcome 2</p>
    </Match>
</Switch>

动态渲染

<Dynamic> 是一个 Solid 组件,允许您根据数据动态渲染组件。通过将表示原生 HTML 元素的字符串或组件函数传递给 component 属性,您可以选择最终要渲染的组件。

import { createSignal, For } from "solid-js"
import { Dynamic } from "solid-js/web"

const RedDiv = () => <div style="color: red">Red</div>

const GreenDiv = () => <div style="color: green">Green</div>

const BlueDiv = () => <div style="color: blue">Blue</div>

const options = {
    red: RedDiv,
    green: GreenDiv,
    blue: BlueDiv,
}

function App() {
    const [selected, setSelected] = createSignal("red")

    return (
        <>
            <select
                value={selected()}
                onInput={(e) => setSelected(e.currentTarget.value)}
            >
                <For each={Object.keys(options)}>
                    {(color) => <option value={color}>{color}</option>} 
                </For>
            </select>
            <Dynamic component={options[selected()]} />
        </>
    )
}

这个例子渲染了一个 <select> 元素,允许您在三种颜色之间进行选择。一旦选择了颜色,<Dynamic> 组件将渲染所选颜色的对应组件或元素。

<Dynamic> 比其他条件渲染拥有更简洁的代码。例如,以下代码渲染的结果与前面的示例相同:

import { createSignal, Switch, Match, For } from "solid-js"

const RedDiv = () => <div style="color: red">Red</div>

const GreenDiv = () => <div style="color: green">Green</div>

const BlueDiv = () => <div style="color: blue">Blue</div>

const options = {
    red: RedDiv,
    green: GreenDiv,
    blue: BlueDiv,
}

function App() {
    const [selected, setSelected] = createSignal("red")

    return (
        <>
            <select
                value={selected()}
                onInput={(e) => setSelected(e.currentTarget.value)}
            >
                <For each={Object.keys(options)}>
                    {(color) => <option value={color}>{color}</option>} 
                </For>

            </select>

            <Switch fallback={<BlueDiv />}>
                <Match when={selected() === "red"}>
                    <RedDiv />
                </Match>

                <Match when={selected() === "green"}>
                    <GreenDiv />
                </Match>
            </Switch>

        </>
    )
}

<Dynamic> 提供了一种更简洁的动态渲染组件的方式,而不是更详细的 <Switch><Match> 语句。

Props

使用这些组件时,您可以通过将 props 传递给 <Dynamic> 组件来将它们传递给正在渲染的组件,类似于在 JSX 中将 props 传递给组件的方式。

import { Dynamic } from "solid-js/web"

function App() {
    return (
        <Dynamic component={someComponent} someProp="someValue" />
    )
}

译者注:也就是说,在这个例子中,someComponent 中的组件可以访问到 someProp="someValue"

列表渲染

列表渲染用于渲染数据集合(例如数组或对象)。

Solid 提供了两种渲染列表的方式:<For><Index> 组件。这两个组件都可以遍历数据集合生成元素,但它们适合不同的场景。

<For>

<For> 是一个循环组件,允许您根据数组或对象的内容渲染元素。该组件被设计用于复杂的数据结构,例如对象数组,其中列表的顺序和长度可能会频繁更改。

<For> 的唯一属性是 each ,用于指定要循环的数据集合。此属性需要一个数组,如果使用对象,需要先通过 Object.entriesObject.values 等方法转换为数组。

import { For } from "solid-js"

<For each={data()}>
  {(item, index) =>
    // 每个元素的渲染逻辑
  }
</For>

<For> 标签之间,组件需要一个回调函数,该函数将指定如何渲染数据集合中的每个条目。此结构类似于 JavaScript 的 map 方法中使用的回调。

该函数接收两个参数:

  • item: 表示正在渲染的数据集合中的当前条目。
  • index: 表示当前条目的索引。

您可以访问当前的 itemindex 来动态设置 JSX 元素的属性或内容。 index 是一个信号,必须作为函数调用才能获取其值。

<For each={data()}>
    {(item, index) => (
        <li
            style={{
                color: index() % 2 === 0 ? "red" : "blue"
            }}
        >
            {item.name}
        </li>
    )}
</For>

Index

<Index><For> 类似,也是一个循环组件,允许您根据数组或对象的内容渲染元素。但是,当列表的顺序和长度保持稳定,但内容可能频繁更改时,<Index> 是更好的选择,因为它会减少重新渲染的次数。

import { Index } from "solid-js"

<Index each={data()}>
    {(item, index) => (
        // 每个元素的渲染逻辑
    )}
</Index>

<For> 组件类似, <Index> 接受名为 each 的单个属性,用于指定要循环遍历的结构。

<For>中,index 是一个信号,但在 <Index> 中是固定的。这是因为 <Index> 更关心带有索引的元素,因此,item是一个信号,允许每个索引的内容更改而无需重新渲染,同时索引保持固定。

import { Index } from "solid-js"

<Index each={data()}>
    {(item, index) => (
        <li>
            {item().name} - {item().completed}
        </li>
    )}
</Index>

<Index> vs <For>

<For> 用于列表的顺序和长度可能频繁变化的情况。当 <For> 中的列表值发生变化时,整个列表将重新渲染。但是,如果数组发生更改,例如元素移动位置,<For> 将通过简单地移动相应的 DOM 节点并更新索引来管理这种情况。

<Index> 用于当列表的顺序和长度保持稳定,但内容可能频繁变化时。当 <Index> 中的列表值发生变化时,仅更新指定索引处的内容。

何时使用 <For>

在不需要信号、嵌套循环或动态列表的情况下,<For> 是最佳选择。例如,在创建静态元素列表(例如链接列表)时,<For> 是最好的选择。这是因为它只会修改列表中元素的索引,而不会重新渲染整个列表。

import { createSignal, For } from "solid-js"

function StringList() {
    const [items, setItems] = createSignal(["Item 1", "Item 2", "Item 3"])

    return (
        <ul>
            <input 
                type="text"
                onInput={(e) => {
                    // 向列表添加新的条目
                }}
            />
            <For each={items()}>
                {(item, index) => (
                    <li> 
                        {item} - {index()}
                    </li>
                )}
            </For>
        </ul>
    )
}

如果您正在使用信号、JavaScript 原始值(例如字符串和数字)或输入字段,<Index> 是更好的选择。如果您使用 <For> ,则当值更改时,即使列表的长度保持不变,整个列表也会重新渲染。而 <Index> 将更新指定索引处的内容,列表的其余部分保持不变。

import { createSignal, Index } from "solid-js"

function FormList() {
    const [inputs, setInputs] = createSignal(['input1','input2','input3'])
    return(
        <form>
            <Index each={inputs()}>
                {(input, index) => (
                    <input
                        type="text"
                        value={input()}
                        onInput={(e) => {
                            // 更新 input 值
                        }}
                    />
                )}
            </Index>

        </form>

    )
}

Solid.js 中文文档

本篇已收录在掘金专栏 《Solid.js 中文文档》,该系列一共 25 篇。下一篇:Solid.js 最新官方文档翻译(10)—— Portal 与错误边界

此外我还写过 JavaScript 系列TypeScript 系列React 系列Next.js 系列VuePress 博客搭建系列等 14 个系列文章, 全系列文章目录:github.com/mqyqingfeng…

通过文字建立交流本身就是一种缘分,欢迎围观我的“朋友圈”、加入“低调务实优秀中国好青年”前端社群,分享技术,带你成长。