preact-custom-element

Preact 的微小尺寸和标准优先的方法使其成为构建 Web 组件的绝佳选择。

¥Preact's tiny size and standards-first approach make it a great choice for building web components.

Preact 旨在渲染完整的应用和页面的各个部分,使其非常适合构建 Web 组件。许多公司使用这种方法来构建组件或设计系统,然后将其封装成一组 Web 组件,以便在多个项目和其他框架中重复使用,同时继续提供熟悉的 Preact API。

¥Preact is designed to render both full applications and individual parts of a page, making it a natural fit for building Web Components. Many companies use this approach to build component or design systems that are then wrapped up into a set of Web Components, enabling re-use across multiple projects and within other frameworks whilst continuing to offer the familiar Preact APIs.



创建 Web 组件

¥Creating a Web Component

任何 Preact 组件都可以通过 preact-custom-element 转换为 Web 组件,preact-custom-element 是一个遵循 Custom Elements v1 规范的非常薄的封装器。

¥Any Preact component can be turned into a web component with preact-custom-element, a very thin wrapper that adheres to the Custom Elements v1 spec.

import register from 'preact-custom-element';

const Greeting = ({ name = 'World' }) => <p>Hello, {name}!</p>;

register(Greeting, 'x-greeting', ['name'], { shadow: false });
//          ^            ^           ^             ^
//          |      HTML tag name     |       use shadow-dom
//   Component definition      Observed attributes

注意:根据 自定义元素规范,标签名称必须包含连字符 (-)。

¥Note: As per the Custom Element Specification, the tag name must contain a hyphen (-).

在 HTML 中使用新的标签名称,属性键和值将作为 props 传入:

¥Use the new tag name in HTML, attribute keys and values will be passed in as props:

<x-greeting name="Billy Jo"></x-greeting>

输出:

¥Output:

<p>Hello, Billy Jo!</p>

观察到的属性

¥Observed Attributes

Web 组件需要显式列出你想要观察的属性名称,以便在其值更改时做出响应。这些可以通过传递给 register() 函数的第三个参数来指定:

¥Web Components require explicitly listing the names of attributes you want to observe in order to respond when their values are changed. These can be specified via the third parameter that's passed to the register() function:

// Listen to changes to the `name` attribute
register(Greeting, 'x-greeting', ['name']);

如果省略 register() 的第三个参数,则可以使用组件上的静态 observedAttributes 属性来指定要观察的属性列表。这也适用于自定义元素的名称,可以使用 tagName 静态属性指定:

¥If you omit the third parameter to register(), the list of attributes to observe can be specified using a static observedAttributes property on your Component. This also works for the Custom Element's name, which can be specified using a tagName static property:

import register from 'preact-custom-element';

// <x-greeting name="Bo"></x-greeting>
class Greeting extends Component {
	// Register as <x-greeting>:
	static tagName = 'x-greeting';

	// Track these attributes:
	static observedAttributes = ['name'];

	render({ name }) {
		return <p>Hello, {name}!</p>;
	}
}
register(Greeting);

如果未指定 observedAttributes,则将从 propTypes 的键(如果存在于组件上)推断它们:

¥If no observedAttributes are specified, they will be inferred from the keys of propTypes if present on the Component:

// Other option: use PropTypes:
function FullName({ first, last }) {
	return (
		<span>
			{first} {last}
		</span>
	);
}

FullName.propTypes = {
	first: Object, // you can use PropTypes, or this
	last: Object // trick to define un-typed props.
};

register(FullName, 'full-name');

将插槽作为属性传递

¥Passing slots as props

register() 函数有第四个参数来传递选项;目前,仅支持 shadow 选项,它将影子 DOM 树附加到指定元素。启用后,这允许使用命名的 <slot> 元素将自定义元素的子元素转发到影子树中的特定位置。

¥The register() function has a fourth parameter to pass options; currently, only the shadow option is supported, which attaches a shadow DOM tree to the specified element. When enabled, this allows the use of named <slot> elements to forward the Custom Element's children to specific places in the shadow tree.

function TextSection({ heading, content }) {
	return (
		<div>
			<h1>{heading}</h1>
			<p>{content}</p>
		</div>
	);
}

register(TextSection, 'text-section', [], { shadow: true });

用法:

¥Usage:

<text-section>
	<span slot="heading">Nice heading</span>
	<span slot="content">Great content</span>
</text-section>