
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
一个可扩展的电商产品卡组件方案:以「卡片容器 + 插件系统 + 插槽区域」为核心,通过复合组件语法和插件增强能力,快速搭建价格、优惠、比价、库存、物流、可访问性、分析上报等场景。
[在线演示]| 路由与页面 | 快速开始 | 插件系统 | API 文档 | 开发指南
onMount / onVisibilityChange / 插槽渲染钩子config.order 控制展示顺序,确保跨插件一致的视觉与语义AnalyticsPlugin 提供曝光、可见性、点击、加购等事件上报transport(HTTP/SDK),支持最小曝光阈值与自定义扩展A11yPlugin 等基础增强能力,保证交互可用与信息可达VideoPlugin)、交互插件(如 HoverPlugin)npm install
npm run dev
# 构建与预览
npm run build
npm run preview
import { CardCore, withPlugins } from "vane-card";
import { createImagePlugin } from "vane-card/custom-plugins/ImagePlugin";
import { createBadgePlugin } from "vane-card/custom-plugins/BadgePlugin";
import { createActionsPlugin } from "vane-card/custom-plugins/ActionsPlugin";
const ProductCard = withPlugins(CardCore, {
plugins: [
createImagePlugin({ src: "image.jpg" }),
createBadgePlugin({ text: "热卖", type: "hot" }),
createActionsPlugin({ showCartButton: true }),
],
});
// 使用卡片
<ProductCard cardId="product-1" data={productData} containerStyle={{ borderRadius: "8px" }} />
提示:插件按需组合,建议在生产环境中仅引入必要插件,以控制打包体积。
应用由 src/App.tsx 管理路由,入口 src/main.tsx 使用 HashRouter。
/(src/pages/CardPlugin/Home.tsx)/compound/*(结构由复合组件表达,行为由插件增强)/layouts/*(垂直/横向/右图/下图/背景图/双列等)/slots/*(children、Header/Overlay、Footer、Gallery+Video、Responsive)/advanced/*(动画、分析、倒计时、综合联动)/readme(通过 ReadmeDemo 渲染本 README 文档)onMount(context): 插件初始化与总线订阅,返回 cleanuponVisibilityChange(context): 处理首次曝光和可见性变化renderPriceArea(context): 渲染价格区域片段;通过 config.order 控制排序PriceCalculatorPlugin:order: 10ComparePricePlugin:order: 20CouponRecommendPlugin:order: 30sku:change、price:changeonMount 中订阅,在 cleanup 中释放no-unused-expressions,使用显式 ifsku:change、price:changeminExposureMs)transport(payload) 或集成外部 SDK(如 Sentry/自研埋点)PriceCalculatorPlugin:计算并展示价格信息(支持多态价格)ComparePricePlugin:展示对比价与折扣信息CouponPlugin / CouponRecommendPlugin:展示与推荐优惠券InventoryPlugin:库存状态与售罄提示VideoPlugin:媒体播放与交互控制A11yPlugin:可访问性增强ActionsPlugin:操作区按钮(加购/查看详情)ResponsivePlugin:响应式行为与样式细节完整列表详见
src/components/CardPlugin/custom-plugins。
src/
App.tsx # 路由与侧边导航
main.tsx # 应用入口(HashRouter)
components/
CardPlugin/ # 卡片容器与内置插件
custom-plugins/ # 业务定制插件(价格、优惠、比价、分析等)
pages/
CardPlugin/ # 演示页面
Home.tsx # 首页(已重构为设计升级版)
ReadmeDemo.tsx # README 渲染页
layouts/* # 布局示例
slots/* # 插槽示例
advanced/* # 高级示例
compound/* # 复合组件示例
以下 API 为常规用法摘要,详细类型请参考源码与注释。
cardId: string:卡片唯一标识data: Record<string, any>:业务数据(价格、SKU、库存等)containerStyle?: React.CSSProperties:容器样式const ProductCard = withPlugins(CardCore, { plugins: [...] })CardCoretype PluginContext = {
cardId: string;
bus: { on: Function; off: Function; emit: Function };
refs: Record<string, any>;
data: Record<string, any>;
config?: Record<string, any>;
};
type Plugin = {
name: string;
config?: { order?: number; [key: string]: any };
onMount?: (ctx: PluginContext) => () => void;
onVisibilityChange?: (ctx: PluginContext) => void;
renderPriceArea?: (ctx: PluginContext) => React.ReactNode;
};
no-unused-expressions,请使用显式 ifconfig.order 控制 priceArea 内的展示顺序onMount 订阅,在返回的 cleanup 中取消订阅transport 实现(HTTP/SDK),保证可靠与可控priceArea 输出顺序AnalyticsPlugin 上报触发条件与 payload 结构由于 CardPlugin 为演示与组件方案,以下建议偏向“工程策略与模式”。
| 模块 | 原始大小 | Gzip 后 | 说明 |
|---|---|---|---|
| 核心容器(CardCore) | ~10 KB | ~3.5 KB | 状态持有 + 插件生命周期驱动 |
| 插件系统(Bus/HOC) | ~14 KB | ~4.5 KB | 事件总线 + withPlugins 组合 |
| 单个插件 | ~2-6 KB | ~1-2 KB | 根据功能复杂度不同 |
| 演示页面集合 | ~120 KB | ~35 KB | 仅在 Demo 环境打包 |
提示:生产环境仅引入必要插件与核心容器,演示页面不参与生产打包。
原则:尽量使用命名导出与独立模块,避免副作用,确保打包器能静态分析去除未使用代码。
// package.json(建议)
{
"sideEffects": false,
"type": "module",
"exports": {
".": {
"import": "./dist/index.es.js",
"types": "./dist/index.d.ts"
},
"./core": {
"import": "./dist/core.es.js",
"types": "./dist/core.d.ts"
},
"./plugins": {
"import": "./dist/plugins.es.js",
"types": "./dist/plugins.d.ts"
}
}
}
import { CardCore, withPlugins } from "vane-card/core";
import { createPriceCalculatorPlugin } from "vane-card/custom-plugins/PriceCalculatorPlugin";
import { createComparePricePlugin } from "vane-card/custom-plugins/ComparePricePlugin";
const ProductCard = withPlugins(CardCore, {
plugins: [
createPriceCalculatorPlugin(),
createComparePricePlugin(),
],
});
// ❌ 不推荐:使用通配导入(示例)
import * as Card from "vane-card";
// 可能引入所有导出,无法充分 Tree-Shaking
// 按需加载大型或低频插件
import { CardCore, withPlugins } from "vane-card/core";
const basePlugins = [];
async function loadAdvancedPlugins() {
const [
{ createVideoPlugin },
{ createAnalyticsPlugin },
{ createCouponRecommendPlugin },
] = await Promise.all([
import("vane-card/custom-plugins/VideoPlugin"),
import("vane-card/custom-plugins/AnalyticsPlugin"),
import("vane-card/custom-plugins/CouponRecommendPlugin"),
]);
return [
createVideoPlugin({ autoplay: false }),
createAnalyticsPlugin({ minExposureMs: 600 }),
createCouponRecommendPlugin({}),
];
}
const ProductCard = withPlugins(CardCore, { plugins: basePlugins });
// 场景触发后再加载
loadAdvancedPlugins().then((plugins) => {
// 在实际库中,这里可能需要重新组合或使用运行时插件挂载机制
// Demo 环境可直接在页面层按需切换示例
});
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
manualChunks: {
card: ["vane-card"],
},
},
},
},
});
// webpack.config.js
module.exports = {
optimization: {
usedExports: true,
sideEffects: false,
splitChunks: {
cacheGroups: {
card: {
test: /[\\/]node_modules[\\/]vane-card[\\/]/,
name: "card",
chunks: "all",
},
},
},
},
};
# Vite 项目
npm run build
npx vite-bundle-visualizer
# 或使用 rollup-plugin-visualizer
npm i -D rollup-plugin-visualizer
// vite.config.ts(可选)
import { visualizer } from "rollup-plugin-visualizer";
export default defineConfig({
plugins: [
react(),
visualizer({ open: true, gzipSize: true, brotliSize: true }),
],
});
onMount 中订阅事件,并在 cleanup 释放资源,避免泄漏。transport,保证可靠性与可观测性。本节按照项目当前真实存在的内容进行梳理,改为“表格形式”列出清单,包含名称、说明与路径,便于查阅与对照代码。
| 插件名称 | 简要说明 | 入口路径 |
|---|---|---|
| A11yPlugin | 无障碍辅助与键盘导航,提升可访问性 | src/components/CardPlugin/custom-plugins/A11yPlugin/index.ts |
| ActionsPlugin | 动作区按钮(购买、收藏、加购)统一行为 | src/components/CardPlugin/custom-plugins/ActionsPlugin/index.tsx |
| AnalyticsPlugin | 埋点与曝光/点击事件上报,支持可插拔传输 | src/components/CardPlugin/custom-plugins/AnalyticsPlugin/index.ts |
| AnimationPlugin | 卡片动效与过渡,增强交互体验 | src/components/CardPlugin/custom-plugins/AnimationPlugin/index.ts |
| BadgePlugin | 角标/标签展示,用于促销标识等 | src/components/CardPlugin/custom-plugins/BadgePlugin/index.tsx |
| ComparePricePlugin | 原价对比与折扣显示,展示优惠力度 | src/components/CardPlugin/custom-plugins/ComparePricePlugin/index.tsx |
| CountdownPlugin | 倒计时促销组件,适用于限时活动 | src/components/CardPlugin/custom-plugins/CountdownPlugin/index.tsx |
| CouponPlugin | 优惠券展示与领取逻辑 | src/components/CardPlugin/custom-plugins/CouponPlugin/index.tsx |
| CouponRecommendPlugin | 自动推荐最佳优惠方案 | src/components/CardPlugin/custom-plugins/CouponRecommendPlugin/index.tsx |
| GalleryPlugin | 图片画廊与缩略图联动 | src/components/CardPlugin/custom-plugins/GalleryPlugin/index.tsx |
| HoverPlugin | 悬停反馈与交互态控制 | src/components/CardPlugin/custom-plugins/HoverPlugin/index.tsx |
| ImagePlugin | 主图展示与懒加载 | src/components/CardPlugin/custom-plugins/ImagePlugin/index.tsx |
| InventoryPlugin | 库存状态与阈值提醒 | src/components/CardPlugin/custom-plugins/InventoryPlugin/index.tsx |
| OrderSummaryPlugin | 订单摘要/结算信息展示 | src/components/CardPlugin/custom-plugins/OrderSummaryPlugin/index.tsx |
| PriceCalculatorPlugin | 价格计算与促销规则整合 | src/components/CardPlugin/custom-plugins/PriceCalculatorPlugin/index.tsx |
| QuantityPlugin | 数量步进与校验 | src/components/CardPlugin/custom-plugins/QuantityPlugin/index.tsx |
| ResponsivePlugin | 响应式布局适配 | src/components/CardPlugin/custom-plugins/ResponsivePlugin/index.ts |
| ReviewPlugin | 评分与评论展示 | src/components/CardPlugin/custom-plugins/ReviewPlugin/index.tsx |
| SKUPlugin | SKU 编码与规格组合展示 | src/components/CardPlugin/custom-plugins/SKUPlugin/index.tsx |
| StatePlugin | 共享状态与事件总线桥接 | src/components/CardPlugin/custom-plugins/StatePlugin/index.tsx |
| UserTagPlugin | 用户标签/画像展示 | src/components/CardPlugin/custom-plugins/UserTagPlugin/index.tsx |
| VideoPlugin | 商品视频播放 | src/components/CardPlugin/custom-plugins/VideoPlugin/index.tsx |
注:以上清单来自
src/components/CardPlugin/custom-plugins/*实际目录结构,均已在演示站可用。建议在生产环境中按需引入以控制体积。
| 组件名称 | 简要说明 | 组件路径 |
|---|---|---|
| A11yToolbar | 无障碍工具条,辅助键盘/阅读器 | src/components/CardPlugin/compound/components/A11yToolbar.tsx |
| Actions | 行为按钮集合(购买、收藏、加购等) | src/components/CardPlugin/compound/components/Actions.tsx |
| Badge | 单个标签/角标 | src/components/CardPlugin/compound/components/Badge.tsx |
| BadgeGroup | 标签分组显示 | src/components/CardPlugin/compound/components/BadgeGroup.tsx |
| Brand | 品牌标识与信息 | src/components/CardPlugin/compound/components/Brand.tsx |
| ColorSwatches | 颜色样例选择 | src/components/CardPlugin/compound/components/ColorSwatches.tsx |
| CouponBar | 优惠/券信息栏 | src/components/CardPlugin/compound/components/CouponBar.tsx |
| DeliveryTimeline | 配送时效/进度 | src/components/CardPlugin/compound/components/DeliveryTimeline.tsx |
| Description | 商品文案描述 | src/components/CardPlugin/compound/components/Description.tsx |
| Discount | 折扣信息展示 | src/components/CardPlugin/compound/components/Discount.tsx |
| Divider | 分隔线 | src/components/CardPlugin/compound/components/Divider.tsx |
| FeatureList | 特性/卖点列表 | src/components/CardPlugin/compound/components/FeatureList.tsx |
| Image | 商品主图展示 | src/components/CardPlugin/compound/components/Image.tsx |
| Inventory | 库存状态 | src/components/CardPlugin/compound/components/Inventory.tsx |
| OldPrice | 原价显示 | src/components/CardPlugin/compound/components/OldPrice.tsx |
| OptionPicker | 选项选择器(尺码/颜色等) | src/components/CardPlugin/compound/components/OptionPicker.tsx |
| OrderSummaryPanel | 订单摘要面板 | src/components/CardPlugin/compound/components/OrderSummaryPanel.tsx |
| Price | 当前价格 | src/components/CardPlugin/compound/components/Price.tsx |
| PricePerUnit | 单位价格 | src/components/CardPlugin/compound/components/PricePerUnit.tsx |
| QuantityStepper | 数量步进器 | src/components/CardPlugin/compound/components/QuantityStepper.tsx |
| Rating | 评分展示 | src/components/CardPlugin/compound/components/Rating.tsx |
| ReturnPolicy | 退换政策说明 | src/components/CardPlugin/compound/components/ReturnPolicy.tsx |
| SKUCode | SKU 编码 | src/components/CardPlugin/compound/components/SKUCode.tsx |
| SavingsBadge | 节省金额角标 | src/components/CardPlugin/compound/components/SavingsBadge.tsx |
| Section | 区块容器 | src/components/CardPlugin/compound/components/Section.tsx |
| SelectedInfo | 已选规格/信息 | src/components/CardPlugin/compound/components/SelectedInfo.tsx |
| Shipping | 运费信息 | src/components/CardPlugin/compound/components/Shipping.tsx |
| ShippingEstimator | 运费预估 | src/components/CardPlugin/compound/components/ShippingEstimator.tsx |
| SizeSelector | 尺寸选择 | src/components/CardPlugin/compound/components/SizeSelector.tsx |
| SpecsTable | 参数规格表 | src/components/CardPlugin/compound/components/SpecsTable.tsx |
| StockBadge | 库存角标 | src/components/CardPlugin/compound/components/StockBadge.tsx |
| Subtitle | 副标题 | src/components/CardPlugin/compound/components/Subtitle.tsx |
| Tags | 标签集合 | src/components/CardPlugin/compound/components/Tags.tsx |
| Title | 标题 | src/components/CardPlugin/compound/components/Title.tsx |
| UpsellBundle | 加购/捆绑推荐 | src/components/CardPlugin/compound/components/UpsellBundle.tsx |
| VariantMediaSwitcher | 变体媒体切换 | src/components/CardPlugin/compound/components/VariantMediaSwitcher.tsx |
| VariantSelector | 变体选择 | src/components/CardPlugin/compound/components/VariantSelector.tsx |
| Warranty | 质保信息 | src/components/CardPlugin/compound/components/Warranty.tsx |
复合组件入口:
src/components/CardPlugin/compound/ProductCard.tsx,通过组合上述组件形成完整页面区块,适合作为业务页面的基础拼装单元。
export interface CardCoreProps<TData = Record<string, any>> {
cardId: string;
data: TData;
containerStyle?: React.CSSProperties;
className?: string;
onEvent?: (evt: { type: string; payload?: any }) => void;
}
cardId:全局唯一标识,便于事件与分析维度对齐。data:承载价格、SKU、库存、物流等业务数据。containerStyle:容器样式,建议仅做轻定制,避免风格割裂。onEvent:容器级事件回调(可与总线联动)。export type PluginBus = {
on: (type: string, handler: Function) => void;
off: (type: string, handler: Function) => void;
emit: (type: string, payload?: any) => void;
};
export type PluginContext<TData = Record<string, any>> = {
cardId: string;
bus: PluginBus;
refs: Record<string, any>;
data: TData;
config?: Record<string, any>;
visible?: boolean;
};
export type CardPlugin = {
name: string;
config?: { order?: number; [key: string]: any };
onMount?: (ctx: PluginContext) => () => void;
onVisibilityChange?: (ctx: PluginContext) => void;
renderPriceArea?: (ctx: PluginContext) => React.ReactNode;
renderHeader?: (ctx: PluginContext) => React.ReactNode;
renderFooter?: (ctx: PluginContext) => React.ReactNode;
renderOverlay?: (ctx: PluginContext) => React.ReactNode;
};
// 示例:统一价格 Chip 的风格与排序
export function createComparePricePlugin(config?: { order?: number }) {
const cfg = { order: 20, ...config };
return {
name: "ComparePricePlugin",
config: cfg,
renderPriceArea(ctx: PluginContext) {
const { data } = ctx;
if (!data?.comparePrice || !data?.price) return null;
const discount = Math.round(
((data.comparePrice - data.price) / data.comparePrice) * 100
);
return (
<span
style={{
display: "inline-block",
borderRadius: 12,
background: "#f2f3f5",
padding: "4px 8px",
marginLeft: 8,
fontSize: 12,
}}
>
省{discount}%
</span>
);
},
} as CardPlugin;
}
12px500#111 / 灰层级 #8f9aa3 / 背景 #f2f3f58px,行间 6px0 4px 12px rgba(0,0,0,0.08)160mstransport 或页面层代理。Q:如何控制 priceArea 的展示顺序?
config.order 中设置数值,数值越小越靠前。Q:为什么要避免短路表达式?
no-unused-expressions,建议改为显式 if 语句。Q:如何实现曝光与可见性上报?
AnalyticsPlugin 订阅 onVisibilityChange 与相关总线事件,采用 transport 进行上报。Q:演示页面是否参与生产构建?
Q:如何引入大型插件而不影响首屏?
priceArea 规范type PriceData = {
price: number;
comparePrice?: number;
currency?: string;
installments?: { months: number; interestFree?: boolean };
};
type LogisticsData = {
region?: string;
shippingCost?: number;
etaDays?: number;
};
type ProductData = {
sku: string;
title: string;
images: string[];
video?: string;
priceInfo: PriceData;
logistics?: LogisticsData;
inventory?: { stock: number; status: "in_stock" | "sold_out" | "low" };
};
:root {
--card-bg: #fff;
--card-fg: #111;
--card-muted: #8f9aa3;
--card-chip-bg: #f2f3f5;
--card-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
--card-radius: 12px;
}
// eslint.config.js(片段)
export default [
{
rules: {
"no-unused-expressions": "error",
"@typescript-eslint/no-explicit-any": "warn",
},
},
];
alt 文本或合理占位文案。import { CardCore, withPlugins } from "vane-card/core";
import { createPriceCalculatorPlugin } from "vane-card/custom-plugins/PriceCalculatorPlugin";
import { createComparePricePlugin } from "vane-card/custom-plugins/ComparePricePlugin";
const ProductCard = withPlugins(CardCore, {
plugins: [
createPriceCalculatorPlugin({ currency: "CNY" }),
createComparePricePlugin({ showPercent: true }),
],
});
export default function BasicPriceDemo() {
return (
<ProductCard
cardId="p-1001"
data={{ price: 199, comparePrice: 299 }}
containerStyle={{ width: 320 }}
/>
);
}
import { CardCore, withPlugins } from "vane-card/core";
import { createCouponPlugin } from "vane-card/custom-plugins/CouponPlugin";
import { createCouponRecommendPlugin } from "vane-card/custom-plugins/CouponRecommendPlugin";
import { createInstallmentPlugin } from "vane-card/custom-plugins/InstallmentPlugin";
const ProductCard = withPlugins(CardCore, {
plugins: [
createCouponPlugin({ maxShown: 2 }),
createCouponRecommendPlugin({ strategy: "best-discount" }),
createInstallmentPlugin({ months: [3, 6], interestFree: true }),
],
});
import { CardCore, withPlugins } from "vane-card/core";
import { createInventoryPlugin } from "vane-card/custom-plugins/InventoryPlugin";
import { createLogisticsETAPlugin } from "vane-card/custom-plugins/LogisticsETAPlugin";
const ProductCard = withPlugins(CardCore, {
plugins: [
createInventoryPlugin({ lowStockThreshold: 3 }),
createLogisticsETAPlugin({ riskAware: true }),
],
});
import { CardCore, withPlugins } from "vane-card/core";
import { createGalleryPlugin } from "vane-card/custom-plugins/GalleryPlugin";
import { createVideoPlugin } from "vane-card/custom-plugins/VideoPlugin";
import { createHoverPlugin } from "vane-card/custom-plugins/HoverPlugin";
const ProductCard = withPlugins(CardCore, {
plugins: [
createGalleryPlugin(),
createVideoPlugin({ autoplay: false }),
createHoverPlugin({ prefetch: true }),
],
});
import { CardCore, withPlugins } from "vane-card/core";
import { createAnalyticsPlugin } from "vane-card/custom-plugins/AnalyticsPlugin";
const ProductCard = withPlugins(CardCore, {
plugins: [
createAnalyticsPlugin({
minExposureMs: 800,
transport: async (payload) => {
await fetch("/collect", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
},
}),
],
});
import { CardCore, withPlugins } from "vane-card/core";
import { createRetryOnErrorPlugin } from "vane-card/custom-plugins/RetryOnErrorPlugin";
import { createFallbackContentPlugin } from "vane-card/custom-plugins/FallbackContentPlugin";
const ProductCard = withPlugins(CardCore, {
plugins: [
createRetryOnErrorPlugin({ maxRetries: 3, retryDelay: 1200 }),
createFallbackContentPlugin({ label: "稍后再试" }),
],
});
sku:change:规格变更price:change:价格变更coupon:apply:优惠券应用inventory:low:库存告警inventory:soldout:售罄提示logistics:update:物流信息更新video:play:视频播放video:pause:视频暂停gallery:open:图集打开gallery:close:图集关闭analytics:exposure:曝光事件analytics:click:点击事件analytics:addToCart:加购事件analytics:visibility:可见性变化error:occurred:错误事件fallback:used:回退启用建议:统一事件负载结构与命名,避免后期维护成本。
:root {
--card-bg: #fff;
--card-fg: #111;
--card-muted: #8f9aa3;
--card-chip-bg: #f2f3f5;
--card-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
--card-radius: 12px;
--card-primary: #2f6fed;
--card-success: #10b981;
--card-warning: #f59e0b;
--card-danger: #ef4444;
--card-border: #e5e7eb;
--card-hover-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
--chip-padding-x: 8px;
--chip-padding-y: 4px;
--chip-gap: 8px;
--chip-radius: 12px;
}
Q:如何避免插件之间的视觉冲突?
order 控制排序,强调信息靠前。Q:插件是否可以跨页面复用?
Q:如何在 SSR 环境使用?
onMount 中直接访问 window;将依赖放到可用时机。Q:是否支持国际化?
Q:是否有统一的埋点口径?
AnalyticsPlugin 给出基础口径;生产环境可按需扩展 transport。showCents 控制{
"cardId": "p-1001",
"event": "analytics:addToCart",
"timestamp": 1712345678901,
"visibility": {
"exposed": true,
"duration": 1234
},
"sku": {
"id": "sku-01",
"title": "黑色 128G",
"price": 1999,
"comparePrice": 2399
},
"context": {
"page": "/product/1001",
"referrer": "/search?q=phone",
"sessionId": "abcdefg",
"user": {
"loggedIn": true,
"memberLevel": "gold"
}
}
}
{
"cardId": "p-1001",
"event": "analytics:exposure",
"timestamp": 1712345678000,
"visibility": {
"exposed": true,
"duration": 850
},
"context": { "page": "/" }
}
{
"cardId": "p-1001",
"event": "analytics:click",
"timestamp": 1712345680000,
"target": {
"role": "cta",
"label": "立即购买"
}
}
规范建议:
- 事件命名统一使用
analytics:*前缀(或统一域)。- 在生产环境中提供
transport,批量与重试策略避免丢数。- 附加维度(如页面、来源、会话)统一由页面层注入。
| 路由 | 组件文件 | 说明 |
|---|---|---|
/ | pages/CardPlugin/Home.tsx | 首页与导览 |
/readme | pages/CardPlugin/ReadmeDemo.tsx | README 渲染 |
/layouts/vertical | pages/CardPlugin/layouts/Vertical.tsx | 垂直布局 |
/layouts/horizontal | pages/CardPlugin/layouts/Horizontal.tsx | 横向布局 |
/layouts/right-image | pages/CardPlugin/layouts/RightImage.tsx | 右图布局 |
/layouts/bottom-image | pages/CardPlugin/layouts/BottomImage.tsx | 下图布局 |
/layouts/bg-image | pages/CardPlugin/layouts/BackgroundImage.tsx | 背景图布局 |
/layouts/two-column | pages/CardPlugin/layouts/TwoColumn.tsx | 双列布局 |
/slots/header | pages/CardPlugin/slots/HeaderSlotDemo.tsx | Header 插槽示例 |
/slots/footer | pages/CardPlugin/slots/FooterSlotDemo.tsx | Footer 插槽示例 |
/slots/overlay | pages/CardPlugin/slots/OverlaySlotDemo.tsx | Overlay 插槽示例 |
/slots/gallery | pages/CardPlugin/slots/GallerySlotDemo.tsx | Gallery 插槽示例 |
/slots/responsive | pages/CardPlugin/slots/ResponsiveSlotDemo.tsx | 响应式插槽示例 |
/advanced/animation | pages/CardPlugin/advanced/AnimationDemo.tsx | 动画示例 |
/advanced/analytics | pages/CardPlugin/advanced/AnalyticsDemo.tsx | 分析与上报示例 |
/advanced/countdown | pages/CardPlugin/advanced/CountdownDemo.tsx | 倒计时示例 |
/advanced/composite | pages/CardPlugin/advanced/CompositeDemo.tsx | 综合联动示例 |
feat/*、fix/*、docs/*)priceArea、Header、Footer。onMount、onVisibilityChange。on/off/emit 能力。Q:如何在极端低网速下保证关键信息可见?
Q:插件之间如何共享上下文数据?
data 字段,仅共享必要的只读数据,避免耦合。Q:卡片支持哪些插槽?
Header、Overlay、Footer、priceArea 等;扩展插槽需遵循命名与排序规范。Q:如何控制 Chip 的语义层级?
Q:上报失败如何处理?
Q:是否支持自定义主题?
Q:如何保障可访问性?
Q:如何集成三方埋点 SDK?
transport 注入或在页面层统一处理,插件仅负责采集与触发。Q:如何做单元与集成测试?
Q:如何防止事件名称冲突?
analytics:*、sku:*、price:*;统一维护字典。Q:如何在移动端优化体验?
Q:加载顺序如何安排?
Q:如何监控性能问题?
Q:插件之间如何避免重复渲染?
Q:如何做路由与页面结构的扩展?
Q:是否支持国际化与货币格式?
PriceCalculatorPlugin 提供 currency 与 format;文案由页面层管理。Q:如何统一视觉风格?
Q:如何进行版本迁移?
export const demoProducts = [
{
sku: "p-1001",
title: "旗舰手机 128G",
images: ["/img/p-1001-1.jpg", "/img/p-1001-2.jpg"],
priceInfo: { price: 1999, comparePrice: 2399, currency: "CNY" },
logistics: { region: "华东", shippingCost: 0, etaDays: 2 },
inventory: { stock: 8, status: "in_stock" },
},
{
sku: "p-1002",
title: "轻薄笔记本 16G/512G",
images: ["/img/p-1002-1.jpg"],
priceInfo: { price: 5999, comparePrice: 6999, currency: "CNY" },
logistics: { region: "华南", shippingCost: 15, etaDays: 3 },
inventory: { stock: 2, status: "low" },
},
{
sku: "p-1003",
title: "真无线耳机",
images: ["/img/p-1003-1.jpg", "/img/p-1003-2.jpg", "/img/p-1003-3.jpg"],
priceInfo: { price: 399, comparePrice: 499, currency: "CNY" },
logistics: { region: "华北", shippingCost: 0, etaDays: 1 },
inventory: { stock: 0, status: "sold_out" },
},
{
sku: "p-1004",
title: "电竞显示器 27"",
images: ["/img/p-1004-1.jpg"],
priceInfo: { price: 2299, comparePrice: 2699, currency: "CNY" },
logistics: { region: "西南", shippingCost: 25, etaDays: 5 },
inventory: { stock: 15, status: "in_stock" },
},
{
sku: "p-1005",
title: "机械键盘",
images: ["/img/p-1005-1.jpg", "/img/p-1005-2.jpg"],
priceInfo: { price: 499, comparePrice: 699, currency: "CNY" },
logistics: { region: "东北", shippingCost: 12, etaDays: 4 },
inventory: { stock: 6, status: "in_stock" },
},
// ...(更多示例条目)
];
AnalyticsPlugin 的 transport 抽象为接口,便于接入不同埋点平台。priceArea 定义可复用的 Chip 组件,集中维护风格与状态。visualizer 或 Bundle 分析工具,监控依赖与按需导入效果。ReactMarkdown 渲染 README,保证文档与演示一致性。MIT License © 2025 Frank Vane
感谢所有为这个项目做出贡献的开发者!
如果这个项目对您有帮助,欢迎 ⭐ Star 支持!
Made with ❤️ by Frank Vane
FAQs
vane-card
The npm package vane-card receives a total of 6 weekly downloads. As such, vane-card popularity was classified as not popular.
We found that vane-card demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.