mcpField
W
wix-cli-site-plugin

wix-cli-site-plugin

github.com/wix开发36288 次安装今天更新
代码仓库

关于 wix-cli-site-plugin

wix-cli-site-plugin 是用于 Wix CLI 应用的开发技能,指导创建可放入 Wix 商业解决方案预设插槽的自定义元素插件。内容涵盖组件、编辑器设置面板与扩展注册,适合开发 Wix 网站扩展的开发者。

主要功能

  1. 01自定义元素插件结构
  2. 02Wix Editor 设置面板
  3. 03插槽放置与扩展注册
  4. 04属性与 HTML 特性绑定
  5. 05颜色和字体选择器

适用场景

  1. 01在商品页添加可配置徽章
  2. 02为预约页显示确认信息
  3. 03展示商品评价摘要

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wix/skills --skill wix-cli-site-plugin

安装后在 Claude Code 中输入/wix-cli-site-plugin确认已加载。

附带文件references/EXAMPLES.mdreferences/SLOTS.md

详细介绍

Wix 站点插件构建器

为 Wix CLI 应用创建站点插件扩展。站点插件是自定义元素,可集成到 Wix 商业解决方案(如 Wix Stores、Wix Bookings、Wix eCommerce)中预定义的 插槽,以扩展其功能和用户体验。

站点所有者可以使用 Wix 编辑器中的插件浏览器,将站点插件放入 UI 插槽。

快速入门清单

创建站点插件时,请按顺序完成以下步骤:

  1. 创建插件文件夹:src/extensions/site/plugins/<plugin-name>/
  2. 创建一个继承 HTMLElement 并带有 observedAttributes 的 <plugin-name>.tsx
  3. 创建包含 WDS 组件以及 widget.getProp/setProp 的 <plugin-name>.panel.tsx
  4. 创建包含 extensions.sitePlugin() 和唯一 UUID 的 <plugin-name>.extension.ts
  5. 更新 src/extensions.ts,导入并使用新扩展

架构

站点插件由 三个必需文件组成:

1. 插件组件(<plugin-name>.tsx)

使用原生 HTMLElement 在插槽中渲染的自定义元素组件:

  • 继承 HTMLElement 类
  • 定义 observedAttributes,实现响应式属性
  • 实现 connectedCallback() 和 attributeChangedCallback() 以进行渲染
  • 使用模板字符串设置内联样式
  • 属性使用 kebab-case(例如,display-name)
2. 设置面板(<plugin-name>.panel.tsx)

显示在 Wix 编辑器侧边栏中的设置面板:

  • 使用 Wix Design System(@wix/design-system)组件——组件参考请参阅 wds-docs
  • 通过 @wix/editor widget API 管理插件属性
  • 使用 widget.getProp('kebab-case-name') 加载初始值
  • 使用 widget.setProp('kebab-case-name', value) 更新属性
  • Widget 属性会绑定到自定义元素属性——任何属性更改都会自动更新相应的属性
  • 内容需封装在 WixDesignSystemProvider > SidePanel > SidePanel.Content 中
3. 扩展配置(<plugin-name>.extension.ts)

定义插件的放置配置:

  • 指定插件可以添加到哪些插槽
  • 配置应用安装时的自动添加行为
  • 设置标签名称和文件路径

插件组件模式

站点插件使用原生 HTMLElement 自定义元素:

// my-site-plugin.tsx
class MyElement extends HTMLElement {
  static get observedAttributes() {
    return ['display-name'];
  }

  constructor() {
    super();
  }

  connectedCallback() {
    this.render();
  }

  attributeChangedCallback() {
    this.render();
  }

  render() {
    const displayName = this.getAttribute('display-name') || "Your Plugin's Title";

    this.innerHTML = `
      <div style="font-size: 16px; padding: 16px; border: 1px solid #ccc; border-radius: 8px; margin: 16px;">
        <h2>${displayName}</h2>
        <hr />
        <p>
          This is a Site Plugin generated by Wix CLI.<br />
          Edit your element's code to change this text.
        </p>
      </div>
    `;
  }
}

export default MyElement;

要点:

  • 直接继承 HTMLElement 类
  • 定义 observedAttributes 静态 getter,列出响应式属性
  • 属性使用 kebab-case(例如,display-name、bg-color)
  • 实现 connectedCallback() 进行初始渲染
  • 实现 attributeChangedCallback(),在属性更改时重新渲染
  • 使用模板字符串设置内联样式
  • 使用 this.getAttribute('attribute-name') 读取属性值
  • Wix 会为你处理 define()——请勿在代码中调用 customElements.define()

设置面板模式

// my-site-plugin.panel.tsx
import React, { type FC, useState, useEffect, useCallback } from 'react';
import { widget } from '@wix/editor';
import {
  SidePanel,
  WixDesignSystemProvider,
  Input,
  FormField,
} from '@wix/design-system';
import '@wix/design-system/styles.global.css';

const Panel: FC = () => {
  const [displayName, setDisplayName] = useState<string>('');

  useEffect(() => {
    widget.getProp('display-name')
      .then(displayName => setDisplayName(displayName || "Your Plugin's Title"))
      .catch(error => console.error('Failed to fetch display-name:', error));
  }, [setDisplayName]);

  const handleDisplayNameChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
    const newDisplayName = event.target.value;
    setDisplayName(newDisplayName);
    widget.setProp('display-name', newDisplayName);
  }, [setDisplayName]);

  return (
    <WixDesignSystemProvider>
      <SidePanel width="300" height="100vh">
        <SidePanel.Content noPadding stretchVertically>
          <SidePanel.Field>
            <FormField label="Display Name">
              <Input
                type="text"
                value={displayName}
                onChange={handleDisplayNameChange}
                aria-label="Display Name"
              />
            </FormField>
          </SidePanel.Field>
        </SidePanel.Content>
      </SidePanel>
    </WixDesignSystemProvider>
  );
};

export default Panel;

要点:

  • widget.getProp() 和 widget.setProp() 中的属性名称使用 kebab-case(例如,"display-name")
  • 在 onChange 处理程序中,始终同时更新本地状态和 widget 属性
  • Widget 属性会绑定到自定义元素属性——更改会自动更新相应的属性
  • 将内容封装在 WixDesignSystemProvider > SidePanel > SidePanel.Content 中
  • 使用 @wix/design-system 中的 WDS 组件
  • 导入 @wix/design-system/styles.global.css 以加载样式
  • 添加 aria-label 以确保无障碍访问

颜色和字体选择器字段

站点插件设置面板可以使用 @wix/editor 中的 inputs.selectColor() 和 inputs.selectFont(),打开 Wix 编辑器原生的颜色和字体选择器对话框。

ColorPickerField

打开包含主题颜色、渐变等选项的 Wix 颜色选择器——不是基础 HTML <input type="color">。

import React, { type FC } from 'react';
import { inputs } from '@wix/editor';
import { FormField, Box, FillPreview, SidePanel } from '@wix/design-system';

interface ColorPickerFieldProps {
  label: string;
  value: string;
  onChange: (value: string) => void;
}

export const ColorPickerField: FC<ColorPickerFieldProps> = ({
  label,
  value,
  onChange,
}) => (
  <SidePanel.Field>
    <FormField label={label}>
      <Box width="30px" height="30px">
        <FillPreview
          fill={value}
          onClick={() => inputs.selectColor(value, { onChange: (val) => { if (val) onChange(val); } })}
        />
      </Box>
    </FormField>
  </SidePanel.Field>
);
FontPickerField

打开 Wix 字体选择器,其中包含字体系列、字号、粗体、斜体和其他排版功能。

import React, { type FC } from 'react';
import { inputs } from '@wix/editor';
import { FormField, Button, Text, SidePanel } from '@wix/design-system';

interface FontValue {
  font: string;
  textDecoration: string;
}

interface FontPickerFieldProps {
  label: string;
  value: FontValue;
  onChange: (value: FontValue) => void;
}

export const FontPickerField: FC<FontPickerFieldProps> = ({
  label,
  value,
  onChange,
}) => (
  <SidePanel.Field>
    <FormField label={label}>
      <Button
        size="small"
        priority="secondary"
        onClick={() => inputs.selectFont(value, { onChange: (val) => onChange({ font: val.font, textDecoration: val.textDecoration || "" }) })}
        fullWidth
      >
        <Text size="small" ellipsis>Change Font</Text>
      </Button>
    </FormField>
  </SidePanel.Field>
);

重要:

  • 始终将 @wix/editor 中的 inputs.selectColor() 与 FillPreview 搭配使用 — 不要使用 <Input type="color">
  • 始终将 @wix/editor 中的 inputs.selectFont() 与回调模式 inputs.selectFont(value, { onChange }) 搭配使用
  • 从 @wix/editor 导入 inputs(不要从 @wix/sdk 导入)

属性命名约定

站点插件的 HTML 属性统一使用 kebab-case:

文件约定示例
<plugin>.tsx (getAttribute)kebab-casethis.getAttribute('display-name')
<plugin>.tsx (observedAttributes)kebab-case['display-name', 'bg-color']
<plugin>.panel.tsx (widget API)kebab-casewidget.getProp('display-name')

输出结构

站点插件位于 src/extensions/site/plugins 下。每个插件都有自己的文件夹,文件以插件名称命名。

src/extensions/site/plugins/
└── {plugin-name}/
    ├── {plugin-name}.tsx           # Main plugin component (HTMLElement)
    ├── {plugin-name}.panel.tsx     # Settings panel component
    └── {plugin-name}.extension.ts  # Extension registration
public/
└── {plugin-name}-logo.svg          # Plugin logo (optional)

参考

主题参考
完整示例EXAMPLES.md
插槽(应用 ID、多个放置位置、查找插槽)SLOTS.md
WDS 组件wds-docs

可用插槽

站点插件会集成到 Wix 商业解决方案中的预定义插槽。每个插槽由以下内容标识:

  • appDefinitionId:Wix 应用的 ID(例如 Stores、Bookings)
  • widgetId:包含该插槽的页面的 ID
  • slotId:特定插槽的标识符

常见的放置区域包括商品页面(Wix Stores)、结账和侧边购物车(Wix eCommerce)、预约页面(Wix Bookings)、服务页面、活动页面和博客文章页面。

有关受支持的页面、常见 Wix 应用 ID 以及如何查找插槽 ID,请参阅 SLOTS.md。

扩展注册

扩展注册是必需的,且包含两个必要步骤。

步骤 1:创建插件专属扩展文件

每个网站插件都需要在其文件夹中创建一个扩展文件:

// my-site-plugin.extension.ts
import { extensions } from '@wix/astro/builders';

export default extensions.sitePlugin({
  id: '{{GENERATE_UUID}}',
  name: 'My Site Plugin',
  marketData: {
    name: 'My Site Plugin',
    description: 'Marketing Description',
    logoUrl: '{{BASE_URL}}/my-site-plugin-logo.svg',
  },
  placements: [{
    appDefinitionId: 'a0c68605-c2e7-4c8d-9ea1-767f9770e087',
    widgetId: '6a25b678-53ec-4b37-a190-65fcd1ca1a63',
    slotId: 'product-page-details-6',
  }],
  installation: { autoAdd: true },
  tagName: 'my-site-plugin',
  element: './extensions/site/plugins/my-site-plugin/my-site-plugin.tsx',
  settings: './extensions/site/plugins/my-site-plugin/my-site-plugin.panel.tsx',
});

重要:生成 UUID

id 必须是唯一且固定的 UUID v4 字符串。每个扩展都要生成一个全新的 UUID——不要使用 randomUUID(),也不要复制示例中的 UUID。将 {{GENERATE_UUID}} 替换为新生成的 UUID,例如 "95a28afd-7df1-4e09-9ec1-ce710b0389a0"。

属性类型说明
id字符串唯一且固定的 UUID v4(请生成新的 UUID)
name字符串插件的内部名称
marketData.name字符串插件浏览器和应用控制面板中显示的名称
marketData.description字符串插件浏览器和应用控制面板中显示的描述
marketData.logoUrl字符串Logo 文件路径({{BASE_URL}} 会解析为公共文件夹)
placements数组可添加插件的插槽位置数组
placements.appDefinitionId字符串包含该插槽的 Wix 应用的 ID
placements.widgetId字符串包含该插槽的页面的 ID
placements.slotId字符串特定插槽的 ID
installation.autoAdd布尔值应用安装时是否自动将插件添加到插槽中
tagName字符串HTML 自定义元素标签(kebab-case,必须包含连字符)
element字符串插件组件的相对路径
settings字符串设置面板组件的相对路径
步骤 2:在主扩展文件中注册

重要: 创建插件专属扩展文件后,必须阅读 wix-cli-extension-registration,并按照“应用注册”部分更新 src/extensions.ts。

如果未完成步骤 2,该网站插件将不会显示在插件浏览器中。

结账插件

如果你正在为结账页面构建插件,该页面可能不支持在安装时自动添加插件。你必须创建一个控制面板页面,让用户能够将插件添加到自己的网站。有关控制面板页面的实现模式,请参阅 EXAMPLES.md。

示例

有关包含所有三个必需文件(插件组件、设置面板和扩展配置)的完整示例,请参阅 EXAMPLES.md。

示例用例:

  • 畅销商品徽章 - 可在商品页面上自定义文字和颜色的徽章
  • 预订确认信息 - 预订页面上的自定义确认信息
  • 商品评价摘要 - 显示星级和评价数量
  • 数据驱动插件 - 集成 Wix Data API 并处理编辑器环境的插件

最佳实践

实施指南
  • 使用内联样式 - 自定义元素不支持 CSS 导入
  • 处理编辑器环境 - 对依赖数据的插件,在编辑器模式下显示占位内容
  • 不要调用 define() - Wix 会自动为你处理 customElements.define()
  • 验证所有输入 - 检查必需的属性是否存在
  • 遵循命名约定 - 所有属性和 widget API 均使用 kebab-case
  • 聚焦插件功能 - 每个插件都应专注做好一件事
  • 在多个插槽中测试 - 如果支持多个位置,请逐一测试
  • 同时支持两个版本的 Stores - 为 Wix Stores 新旧商品页面都添加位置配置,以获得最大兼容性
编辑器沙盒

在编辑器中渲染站点插件时,它们会被置于沙盒中。这意味着它们会被视为来自不同的域名,从而影响对浏览器存储 API 的访问。

编辑器中受限的 API:

  • localStorage 和 sessionStorage(Web Storage API)
  • document.cookie(Cookie Store API)
  • IndexedDB API
  • Cache API

如何处理沙盒限制:

在访问受限的 API 之前,使用 @wix/site-window 中的 viewMode() 函数检查当前模式:

import { window as wixWindow } from '@wix/site-window';

const viewMode = await wixWindow.viewMode();

if (viewMode === 'Site') {
  const item = localStorage.getItem('myKey');
} else {
  // Mock storage or modify API usage for editor mode
}
在站点插件中使用 Wix SDK 模块

站点插件可以直接导入和使用 Wix SDK 模块——无需使用 createClient()。Wix 运行时会自动提供客户端上下文。

// ✅ CORRECT — Import SDK modules directly
import { items } from "@wix/data";
import { currentCart } from "@wix/ecom";
import { products } from "@wix/stores";

class MyPlugin extends HTMLElement {
  async loadData() {
    // Call SDK methods directly — no createClient needed
    const result = await items.query("MyCollection").find();
    const cart = await currentCart.getCurrentCart();
    const productList = await products.queryProducts().limit(10).find();
  }
}
// ❌ WRONG — Do NOT use createClient in site plugins
import { createClient } from "@wix/sdk";
const wixClient = createClient({ modules: { items, products } });
await wixClient.items.query(...); // Wrong — API surface differs through client
性能注意事项
  • 尽量减小包体积——插件会在面向用户的页面上加载
  • 避免在初始渲染时进行大量计算
  • 尽可能延迟加载数据
  • 使用高效的重新渲染模式

评论

支持 Markdown