头图

前言

Svelte,一个语法简洁、入门容易,面向未来的前端框架。

从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1

image.png

Svelte 以其独特的编译时优化机制著称,具有轻量级高性能易上手等特性,非常适合构建轻量级 Web 项目

为了帮助大家学习 Svelte,我同时搭建了 Svelte 最新的中文文档站点。

如果需要进阶学习,也可以入手我的小册《Svelte 开发指南》,语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

欢迎围观我的“网页版朋友圈”、加入“冴羽·成长陪伴社群”,踏上“前端大佬成长之路”

自定义元素

<!-- - 基本上与我们今天所拥有的一样 -->

Svelte 组件也可以使用 customElement: true 编译器选项编译为自定义元素(又称 Web 组件)。你应该使用 <svelte:options> 元素 为组该件指定一个标签名。

<svelte:options customElement="my-element" />

<script>
  let { name = 'world' } = $props();
</script>

<h1>Hello {name}!</h1>
<slot />

对于那些你不想暴露的内部组件,你可以省略标签名,并像使用常规 Svelte 组件一样使用它们。如果需要,组件的使用者仍然可以在之后通过静态 element 属性来命名它,该属性包含自定义元素构造函数,并且在 customElement 编译器选项为 true 时可用。

// @noErrors
import MyElement from './MyElement.svelte';

customElements.define('my-element', MyElement.element);

一旦自定义元素被定义,它就可以作为常规 DOM 元素一样使用:

document.body.innerHTML = `
  <my-element>
    <p>这是一些插槽内容</p>
  </my-element>
`;

任何 props 都会作为 DOM 元素的属性暴露出来(并且在可能的情况下作为属性可读/可写)。

// @noErrors
const el = document.querySelector('my-element');

// 获取 'name' 属性的当前值
console.log(el.name);

// 设置新值,更新 shadow DOM
el.name = 'everybody';

注意,你需要明确列出所有属性,即在 组件选项 中没有声明 props 时使用 let props = $props() 意味着 Svelte 无法知道要在 DOM 元素上暴露哪些属性。

组件生命周期

自定义元素是使用包装器方法从 Svelte 组件创建的。这意味着内部 Svelte 组件并不知道它是一个自定义元素。自定义元素包装器负责适当地处理其生命周期。

当创建一个自定义元素时,它包裹的 Svelte 组件并不会立即创建。只有在调用 connectedCallback 后的下一个 tick 中创建。在将自定义元素插入 DOM 之前分配给它的属性会被临时保存,然后在组件创建时设置,因此它们的值不会丢失。

但是这对于调用自定义元素上导出的函数并不奏效,它们只有在元素挂载后才可用。如果你需要在组件创建之前调用函数,可以使用 extend 选项 选项来解决这个问题。

当使用 Svelte 编写的自定义元素被创建或更新时,shadow DOM 将在下一个 tick 反映该值,而不是立即反映。这种方式可以对更新进行批处理,并且临时(但同步地)将元素从 DOM 中分离的 DOM 移动不会导致内部组件被卸载。

内部 Svelte 组件会在调用 disconnectedCallback 后的下一个 tick 中被销毁。

组件选项

构造自定义元素时,自 Svelte 4 起,你可以通过在 <svelte:options> 中定义 customElement 为一个对象来定制多个方面。这个对象可以包含以下属性:

  • tag: string:自定义元素名称的可选 tag 属性。如果设置,则在导入此组件时,会在文档的 customElements 注册表中定义具有此标记名称的自定义元素。
  • shadow:一个可选属性,可以设置为 "none" 以避免创建 shadow root。注意,样式就不再被封装,并且你不能使用插槽
  • props:一个可选属性,用于修改组件属性的某些细节和行为。它提供以下设置:

    • attribute: string:要更新自定义元素的 prop,您有两个替代方法:要么像上面演示的那样在自定义元素的引用上设置属性,要么使用 HTML 属性。对于后者,默认属性名称是小写的属性名。通过分配 attribute: "<desired name>" 来修改此项。
    • reflect: boolean:默认情况下,更新后的 prop 值不会反映到 DOM 上。要启用此行为,设置 reflect: true
    • type: 'String' | 'Boolean' | 'Number' | 'Array' | 'Object':在将属性值转换为 prop 值并反映回去时,prop 值默认为 String。这可能并不总是准确的。例如,对于数字类型,使用 type: "Number" 定义。您不需要列出所有属性,未列出的属性将使用默认设置。
  • extend:一个可选属性,它期望一个函数作为其参数。它会传入由 Svelte 生成的自定义元素类,并期望返回一个自定义元素类。如果你对自定义元素的生命周期有非常具体的要求,或者想要增强该类,例如使用 ElementInternals 以获得更好的 HTML 表单集成,这会很有用。
<svelte:options
  customElement={{
    tag: 'custom-element',
    shadow: 'none',
    props: {
      name: { reflect: true, type: 'Number', attribute: 'element-index' }
    },
    extend: (customElementConstructor) => {
      // 扩展类,以便让它参与 HTML 表单
      return class extends customElementConstructor {
        static formAssociated = true;

        constructor() {
          super();
          this.attachedInternals = this.attachInternals();
        }

        // 在这里添加函数,而不是在下面的组件中
        // 这样它始终可用,而不仅仅是在内部 Svelte 组件
        // 挂载时才可用
        randomIndex() {
          this.elementIndex = Math.random();
        }
      };
    }
  }}
/>

<script>
  let { elementIndex, attachedInternals } = $props();
  // ...
  function check() {
    attachedInternals.checkValidity();
  }
</script>

...

注意事项和限制

自定义元素可以作为一种有用的方式来打包组件以在非 Svelte 应用程序中使用,因为它们可以与原生 HTML 和 JavaScript 以及大多数框架一起工作。然而,有一些重要的差异需要注意:

  • 样式是封装的,而不是仅仅 scoped 的(除非你设置 shadow: "none")。这意味着任何非组件样式(比如你在 global.css 文件中的样式)都不会应用到自定义元素上,包括带有 :global(...) 修饰符的样式
  • 样式被内联到组件中作为 JavaScript 字符串,而不是被提取出来作为单独的 .css 文件。
  • 自定义元素通常不适合服务端渲染,因为在 JavaScript 加载之前,shadow DOM 是不可见的
  • 在 Svelte 中,插槽内容是延迟渲染的。在 DOM 中,它是立即渲染的。换句话说,即使组件的 <slot> 元素在 {#if ...} 块内,它也会始终被创建。类似地,在 {#each ...} 块中包含 <slot> 不会导致插槽内容被多次渲染
  • 已废弃的 let: 指令没有效果,因为自定义元素没有办法将数据传递给填充插槽的父组件
  • 需要 polyfills 来支持较旧的浏览器
  • 你可以在自定义元素内的常规 Svelte 组件之间使用 Svelte 的上下文功能,但不能在自定义元素之间使用。换句话说,您不能在父自定义元素上使用 setContext 并在子自定义元素中用 getContext 读取它。

Svelte 中文文档

点击查看《Svelte 自定义元素》介绍。

系统学习 Svelte,欢迎入手小册《Svelte 开发指南》。语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

此外我还写过 JavaScript 系列TypeScript 系列React 系列Next.js 系列冴羽答读者问等 14 个系列文章, 全系列文章目录:https://github.com/mqyqingfeng/Blog

欢迎围观我的“网页版朋友圈”、加入“冴羽·成长陪伴社群”,踏上“前端大佬成长之路”


冴羽
9.4k 声望6.3k 粉丝