See how Stencil fits into the entire Ionic Ecosystem ->
Stencil is part of the Ionic Ecosystem ->

使用函数式组件

函数式组件与普通的 Stencil Web 组件完全不同,因为它们是 JSX 编译器的一部分。函数式组件基本上是一个函数,它接受一个 props 对象并将其转换为 JSX。

const Hello = props => <h1>Hello, {props.name}!</h1>;

当 JSX 转译器遇到这样的组件时,它会获取它的属性,将它们作为 props 对象传递给函数,并用函数返回的 JSX 替换该组件。

<Hello name="World" />

函数式组件也接受第二个参数 children

const Hello = (props, children) => [
  <h1>Hello, {props.name}</h1>,
  children
];

JSX 转译器将组件的所有子元素作为数组传递到函数的 children 参数中。

<Hello name="World">
  <p>I'm a child element.</p>
</Hello>

Stencil 提供了一个 FunctionalComponent 泛型类型,允许为组件的属性指定一个接口。

// Hello.tsx

import { FunctionalComponent, h } from '@stencil/core';

interface HelloProps {
  name: string;
}

export const Hello: FunctionalComponent<HelloProps> = ({ name }) => (
  <h1>Hello, {name}!</h1>
);

使用子组件

函数式组件的第二个参数接收传递的子组件,但为了使用它们,FunctionalComponent 提供了一个 utils 对象,该对象公开了一个 map() 方法来转换子组件,以及 forEach() 来读取他们。不推荐读取 children 数组,因为模板编译器可以在 prod 模式下重命名 vNode 属性。

export interface FunctionalUtilities {
  forEach: (children: VNode[], cb: (vnode: ChildNode, index: number, array: ChildNode[]) => void) => void;
  map: (children: VNode[], cb: (vnode: ChildNode, index: number, array: ChildNode[]) => ChildNode) => VNode[];
}
export interface ChildNode {
  vtag?: string | number | Function;
  vkey?: string | number;
  vtext?: string;
  vchildren?: VNode[];
  vattrs?: any;
  vname?: string;
}

Example:

export const AddClass: FunctionalComponent = (_, children, utils) => (
  utils.map(children, child => ({
    ...child,
    vattrs: {
      ...child.vattrs,
      class: `${child.vattrs.class} add-class`
    }
  }
  ))
);

在 JSX 中使用函数组件时,其名称必须以大写字母开头。因此,将其导出的操作是有意义的。

差异

函数组件和类组件之间有一些主要区别。由于函数式组件只是 JSX 中的语法糖,它们... ..

  • 没有编译成网络组件,
  • 不要创建 DOM 节点,
  • 没有 Shadow DOM 或作用域样式,
  • 没有生命周期钩子,
  • 没有状态值

在决定是否使用函数组件时,要记住的一个概念是应用程序的 UI 通常可以是其状态的函数,

即。例如,给定相同的状态,它总是呈现相同的 UI。如果一个组件必须保持状态、处理事件等,它可能应该是一个类组件。如果一个组件的目的是简单地封装一些标记以便它可以在您的应用程序中重用,那么它可能是一个功能组件(特别是如果您使用的是组件库,因此不需要对其进行样式设置)。

BackNext
Translators
Contributors