
Security News
GitHub Actions Checkout Now Blocks Risky pull_request_target Checkouts
GitHub Actions checkout now blocks risky pull_request_target checkouts by default to help prevent pwn request supply chain attacks.
@snack-kit/core
Advanced tools
Snack 动态模块加载与渲染 SDK,基于 React + RequireJS 构建。支持从服务端加载 UMD 格式的 Snack 模块,并将设计器页面 JSON 配置渲染为 React 组件树,同时提供完整的表单数据管理、校验、国际化与事件系统。
npm install @snack-kit/core
Peer Dependencies
npm install react react-dom @snack-kit/lib
import React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { SnackSDK } from '@snack-kit/core';
import * as SnackKitLib from '@snack-kit/lib';
const sdk = new SnackSDK({
service: 'https://your-snack-service.com',
importMaps: {
'react': React,
'react-dom': ReactDOM,
'@snack-kit/lib': SnackKitLib
}
});
// 渲染设计器页面为 React 组件
const Page = await sdk.createPageComponent({ id: 'page-id', type: 'portal' });
createRoot(document.getElementById('app')!).render(<Page />);
| 参数 | 类型 | 说明 |
|---|---|---|
service | string | Snack 服务端地址 |
importMaps | object | 模块依赖注入,key 为模块名,value 为模块对象 |
defaultFormAttr | SnackFormAttribute | 表单组件 FormAttr 全局默认值 |
runtime | boolean | 是否为运行时模式,默认 true;设计器模式设为 false |
runtimeMapping | object | 运行时模块名映射 |
modUrl | string | 模块加载根路径,默认 service + modPath |
modPath | string | 模块请求路径,默认 /package |
cdnUrl | string | CDN 根路径,默认 service + cdnPath |
cdnPath | string | CDN 路径,默认 /lib |
pageUrl | string | 页面数据请求根路径 |
pagePath | string | 页面请求路径,默认 /page |
staticUrl | string | 模块静态资源 CDN 路径 |
importModules | ImportModules | 预置 Snack 模块 class,加载时优先从此查找 |
basicsType | string | 基础模块分类名,默认 basics |
i18n | I18nData | 国际化数据复写 |
localeMapping | object | 国际化语言映射关系,例如 { zh: 'zh_CN' } |
localeDefault | string | 默认国际化语言,默认 zh |
message | object | 消息提示对象,供 evalJS 脚本使用 |
argv | Record<string, ...> | 全局环境变量,在表达式中通过 $.argv 访问 |
sdk.createPageComponent(params, config?)将设计器页面渲染为 React 函数组件。
const Page = await sdk.createPageComponent(
{ id: 'my-page', type: 'portal' },
{
data: { username: 'admin' }, // 表单回填数据
onComplete: (event) => {
console.log('渲染完成', event.fields);
}
}
);
<Page />
sdk.createPageForElement(el, params, config?)将页面直接渲染到指定 DOM 元素。
await sdk.createPageForElement(document.getElementById('container')!, { id: 'my-page' });
sdk.renderPage(params, config?)渲染页面并返回根容器 DropOjb 实例(低层 API)。支持传入数组一次渲染多个页面到同一表单容器。
渲染完成后,onComplete 回调参数 event: SnackPageCompleteEvent 提供以下方法:
// 获取表单数据(全量)
const formData = event.getData();
// 获取单个字段值
const username = event.getData('username');
// 设置表单数据
event.setData('username', 'new-value');
event.setData({ username: 'a', password: 'b' });
// 表单校验(返回 Promise<boolean>)
const passed = await event.verify();
const fieldPassed = await event.verify('username');
// 清空表单
event.empty();
// 销毁页面(解绑事件、释放模块)
event.destroy();
sdk.createModuleComponent(args, data?)加载并渲染单个 Snack 模块为 React 函数组件。
const Editor = await sdk.createModuleComponent(
{ name: 'codeeditor', type: 'code-editor' },
{ readOnly: false }
);
<Editor />
sdk.createClassModule(SnackClass, data?, main?)通过 Snack Class 直接创建模块实例。
const mod = sdk.createClassModule(MySnackClass, { value: 'hello' });
sdk.createClassModuleComponent(SnackClass, data?, main?)通过 Snack Class 创建模块并返回 React 函数组件。
sdk.module(config)按模块名称加载 UMD 模块,返回 Snack class 键值对。
const { mymodule } = await sdk.module({
module: [{ name: 'mymodule', type: 'custom' }]
});
sdk.moduleURL(config)按模块 URL 加载模块。
const mods = await sdk.moduleURL({
urls: 'https://cdn.example.com/package/custom/mymodule/index.js',
cdnUrl: 'https://cdn.example.com/lib'
});
// 注册事件
sdk.addEvent('onComplete', (root, params) => {
console.log('页面加载完成', root);
});
// 页面内组件事件(onChange、onBlur 等)由 SDK 自动分发
// 手动发送事件
sdk.sendEvent('custom-event', [arg1, arg2]);
// 卸载指定回调
sdk.removeEvent('onComplete', myCallback);
// 卸载所有同名事件
sdk.removeEvent('onComplete');
内置事件
| 事件名 | 触发时机 |
|---|---|
onComplete | 整个页面加载渲染完成 |
onRemove | 模块被移除 |
snack-event | 组件内部事件(onChange、onBlur 等)分发 |
// SDK 级别
sdk.intl('required'); // 返回 string | ReactNode
sdk.setI18n({ zh: { required: '此字段必填' } });
sdk.language('en-US'); // 切换所有已加载模块的语言
// 模块内(Snack 基础类)
this.$intl({ id: 'required' });
this.$intl('required', { label: '用户名' });
this.$lang; // 获取当前语言
this.$lang = 'en-US'; // 设置语言
Snack<T>所有 Snack 模块的基础类,继承此类实现自定义模块。
import { Snack, SnackData } from '@snack-kit/core';
interface MyData extends SnackData {
value: string;
label: { zh: string; en: string };
}
export class MyModule extends Snack<MyData> {
public $component(data?: MyData) {
return <div>{data?.value}</div>;
}
}
常用属性与方法
| 成员 | 说明 |
|---|---|
data | 模块数据对象 |
sdk | 所属 SDK 实例 |
$id | 模块唯一实例 ID |
$name | 模块名称 |
$parent | 父级 Snack 实例(通常为 Drag 容器) |
$page | 所属页面事件对象 |
$forceUpdate(reload?) | 强制重绘 |
$style(key?, value?) | 读写样式并触发重绘 |
$display(visible) | 显示 / 隐藏当前组件 |
$intl(ops, params?) | 获取国际化文本 |
$language(lang) | 切换语言 |
$destroy() | 销毁模块,从 SDK moduleMaps 中移除 |
FC(data) | React 渲染入口,由 SDK 调用 |
$component(data?) | 子类重写此方法实现 UI |
SnackSetting设置面板模块的基础类,通过 $setData 同步修改关联主模块(main)的数据。
import { SnackSetting } from '@snack-kit/core';
export class MyModuleSetting extends SnackSetting {
public $component() {
return (
<input
value={String(this.main.data?.value ?? '')}
onChange={(e) => this.$setData('value', e.target.value)}
/>
);
}
}
页面 JSON 配置中支持 {{$.xxx}} 占位符,在运行时由 SDK 自动替换:
{
"data": {
"M": {
"label": "{{$.argv.username}}"
}
}
}
可用变量
| 变量 | 说明 |
|---|---|
$.argv | SDK 构造时传入的 argv 全局变量 |
$.data | 当前组件的 data 对象 |
$.sdk | SDK 实例 |
Snack 模块需打包为 UMD 格式并通过 snackdefine 注册(由 scripts/config/webpack.snack.config.js 提供):
// webpack.snack.config.js 关键配置
output: {
filename: 'index.js',
library: { type: 'umd' }
},
externals: {
'react': 'react',
'@snack-kit/lib': '@snack-kit/lib'
}
打包产物头部会自动注入:
if (typeof window !== 'undefined' && window.snackdefine) {
var define = window.snackdefine;
}
# 本地开发调试(webpack-dev-server)
npm run dev
# 构建库(ESM + CJS + Types)
npm run build
# 运行单元测试
npm test
# 类型检查
npm run lint
# 生成 API 文档
npm run docs
Bug 修复
outExtension: () => ({ js: '.js' }) 配置,强制输出 dist/es/index.js,与 package.json 的 exports.import 路径一致,解决 Vite 等构建工具无法解析 @snack-kit/core 入口("Failed to resolve entry")的问题新增
src/utils/reactAdapter.ts:createReactRoot / SnackReactRoot 运行时兼容层,支持 React 17 / 18 / 19 三版本并存
createRoot(react-dom/client)ReactDOM.render / unmountComponentAtNodereact-dom/client 不存在报 "Module not found"createReactRoot 与 SnackReactRoot 供外部使用依赖调整
react / react-dom 由 dependencies 移至 peerDependencies,支持版本范围 >=17.0.0,宿主项目可自行管理 React 版本破坏性变更
SnackVue、SnackVueSetting 及相关导出)@paraview/lib 迁移至 @snack-kit/lib(@paraview/lib 作为 importMaps 别名保持向后兼容)安全修复
new Function 动态字段访问,改用 safeGet / safeSet 路径工具(消除代码注入风险)window.define 全局污染修复:改用 window.snackdefine,不再覆盖标准 AMD defineBug 修复
$defaultDisplay(false) 逻辑反转问题,调用后组件现可正确隐藏childCount 未递减,导致 onComplete 永不触发的问题key / field 辅助定位新增
src/utils/pathAccess.ts:safeGet / safeSet 支持 . 与 [] 混合路径语法destroy() 方法现会清理所有待处理的 verifyTimeout 防抖计时器,防止内存泄漏tests/utils/pathAccess.test.ts)npm run docs 生成 API 文档类型系统
any 使用SnackRequireFunction、SnackDefineFunction、SnackSettingModelItem、ExprArgvISC
FAQs
Snack by Para FED
We found that @snack-kit/core 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
GitHub Actions checkout now blocks risky pull_request_target checkouts by default to help prevent pwn request supply chain attacks.

Product
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.

Product
Socket MCP now lets AI assistants review org alerts, investigate threats using the Socket threat feed, and inspect package files in addition to dependency scoring.