npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vschema-ui

v1.3.12

Published

A Vue 3 plugin for building dynamic UI from JSON Schema - declarative, reactive, and powerful

Readme

VSchema-UI

npm version License: MIT

一个强大的 Vue 3 插件,通过 JSON Schema 声明式构建动态 UI。支持响应式数据、计算属性、事件处理、条件渲染、循环渲染、API 调用、WebSocket 等完整功能。

English | 简体中文

✨ 特性

  • 🎯 声明式配置 - 通过 JSON Schema 定义组件结构,无需编写 Vue 模板
  • 🔄 响应式数据 - 完整支持 Vue 3 响应式系统
  • 📊 计算属性 - 支持表达式计算和派生状态
  • 🎪 事件处理 - 灵活的事件绑定和动作系统
  • 🔀 条件渲染 - 支持 ifshow 指令
  • 🔁 循环渲染 - 支持 for 指令遍历数组
  • 📝 表单绑定 - 支持 model 双向绑定
  • 🌐 API 调用 - 内置 fetch 动作和 initApi/uiApi 配置
  • 🔌 WebSocket - 支持长连接和实时通信
  • 📋 剪贴板 - 内置 copy 动作,兼容各种浏览器
  • 🎰 插槽支持 - 支持默认插槽、具名插槽和作用域插槽
  • 🔄 生命周期 - 支持 onMountedonUnmountedonUpdated 钩子
  • 👁️ 监听器 - 支持 watch 监听状态变化
  • 🧩 组件注册 - 支持注册自定义组件
  • 🔒 安全 - 内置表达式安全检查,防止 XSS 攻击

📦 安装

pnpm add vschema-ui
# 或
npm install vschema-ui
# 或
yarn add vschema-ui

🚀 快速开始

方式一:全局注册插件

import { createApp } from 'vue';
import { VSchemaPlugin } from 'vschema-ui';
import App from './App.vue';

const app = createApp(App);
app.use(VSchemaPlugin);
app.mount('#app');
<template>
  <VSchema :schema="schema" />
</template>

方式二:按需导入组件

<template>
  <VSchema :schema="schema" />
</template>

<script setup lang="ts">
import { VSchema } from 'vschema-ui';
import type { JsonNode } from 'vschema-ui';

const schema: JsonNode = {
  data: { count: 0 },
  com: 'div',
  children: [
    { com: 'p', children: '计数: {{ count }}' },
    {
      com: 'button',
      events: { click: { set: 'count', value: '{{ count + 1 }}' } },
      children: '增加'
    }
  ]
};
</script>

方式三:创建自定义配置的组件

import { createVSchema } from 'vschema-ui';
import MyButton from './components/MyButton.vue';

// 创建带配置的 VSchema 组件
const VSchema = createVSchema({
  baseURL: 'https://api.example.com',
  components: {
    MyButton,  // 注册自定义组件
  },
  defaultHeaders: {
    'Authorization': 'Bearer token'
  }
});

export default VSchema;

基础示例

<template>
  <VSchema :schema="schema" />
</template>

<script setup lang="ts">
import type { JsonNode } from 'vschema-ui';

const schema: JsonNode = {
  data: { count: 0 },
  com: 'div',
  children: [
    { com: 'p', children: '计数: {{ count }}' },
    {
      com: 'button',
      events: { click: { set: 'count', value: '{{ count + 1 }}' } },
      children: '增加'
    }
  ]
};
</script>

3. 注入外部数据和方法

<template>
  <VSchema 
    :schema="schema" 
    :initial-data="initialData"
    :methods="externalMethods"
  />
</template>

<script setup lang="ts">
import type { JsonNode } from 'vschema-ui';

const schema: JsonNode = {
  data: { form: { username: '', password: '' } },
  com: 'div',
  children: [
    {
      com: 'input',
      model: 'form.username',
      props: { placeholder: '用户名' }
    },
    {
      com: 'button',
      events: {
        click: {
          script: 'await $methods.login(state.form.username, state.form.password);'
        }
      },
      children: '登录'
    }
  ]
};

const initialData = { appName: 'My App' };

const externalMethods = {
  login: async (username: string, password: string) => {
    console.log('登录:', username, password);
  }
};
</script>

📖 Schema 结构

VSchema 组件 Props

| 属性 | 类型 | 说明 | |------|------|------| | schema | JsonNode \| string | JSON Schema 定义(对象或 JSON 字符串) | | config | GlobalConfig | 组件级别的配置(覆盖全局配置) | | initialData | object | 初始化数据,会与 schema.data 合并 | | methods | object | 外部注入的方法,可在 script 动作中通过 $methods 访问 |

基础属性

| 属性 | 类型 | 说明 | |------|------|------| | com | string | 组件类型(HTML 标签或注册的组件名) | | props | object | 传递给组件的 props | | children | JsonNode[] \| string | 子节点或文本内容 | | events | object | 事件处理器 | | slots | object | 插槽定义 |

指令属性

| 属性 | 类型 | 说明 | |------|------|------| | if | string | 条件渲染(v-if) | | show | string | 显示/隐藏(v-show) | | for | string | 循环渲染,格式: "item in items""(item, index) in items" | | key | string | 循环项的 key | | model | string | 双向绑定(v-model) | | ref | string | 模板引用 |

数据和逻辑

| 属性 | 类型 | 说明 | |------|------|------| | data | object | 响应式数据定义(Schema 中声明) | | computed | object | 计算属性(值为表达式字符串) | | watch | object | 监听器 | | methods | object | 方法定义 |

💡 data vs state: data 用于 Schema 中声明初始数据,运行时通过 state 访问当前状态

生命周期钩子

| 属性 | 类型 | 说明 | |------|------|------| | onMounted | Action \| Action[] | 组件挂载后执行 | | onUnmounted | Action \| Action[] | 组件卸载前执行 | | onUpdated | Action \| Action[] | 组件更新后执行 |

API 配置

| 属性 | 类型 | 说明 | |------|------|------| | initApi | string \| ApiConfig | 组件挂载时请求 API,返回数据与 data 合并 | | uiApi | string \| ApiConfig | 组件挂载时请求 API,返回 JsonNode 替换 children |

🎬 动作类型 (Actions)

Set 动作 - 修改状态

{ "set": "count", "value": "{{ count + 1 }}" }

Call 动作 - 调用方法

{ "call": "methodName", "args": ["{{ arg1 }}", "{{ arg2 }}"] }

Emit 动作 - 触发事件

{ "emit": "eventName", "payload": "{{ data }}" }

Fetch 动作 - API 调用

{
  "fetch": "https://api.example.com/data",
  "method": "POST",
  "body": { "key": "{{ value }}" },
  "then": { "set": "result", "value": "{{ $response }}" },
  "catch": { "set": "error", "value": "{{ $error.message }}" }
}

Copy 动作 - 复制到剪贴板

{
  "copy": "{{ shareUrl }}",
  "then": { "set": "copied", "value": true },
  "catch": { "set": "error", "value": "{{ $error.message }}" }
}

兼容性:优先使用 Clipboard API,自动降级到 execCommand

WebSocket 动作 - 长连接

{
  "ws": "wss://example.com/socket",
  "op": "connect",
  "id": "main",
  "onMessage": { "set": "lastMessage", "value": "{{ $response }}" }
}

If 动作 - 条件执行

{
  "if": "count > 10",
  "then": { "set": "message", "value": "大于10" },
  "else": { "set": "message", "value": "小于等于10" }
}

Script 动作 - 自定义脚本

{
  "script": "await $methods.login(state.form.username, state.form.password);"
}

可用变量: state, computed, $event, $response, $error, $methods

📝 表达式语法

使用 {{ expression }} 语法在字符串中嵌入表达式:

{
  "children": "你好,{{ user.name }}!",
  "props": {
    "class": "{{ isActive ? 'active' : 'inactive' }}",
    "disabled": "{{ loading }}"
  }
}

🌐 initApi 和 uiApi

initApi - 初始化数据

{
  "data": { "title": "加载中..." },
  "initApi": "/api/posts",
  "com": "div",
  "children": "{{ title }}"
}

uiApi - 动态 UI 加载

{
  "uiApi": "/api/page/{{ pageId }}",
  "com": "div",
  "children": "加载中..."
}

🎰 插槽支持

简单插槽

{
  "com": "MyCard",
  "slots": {
    "default": [{ "com": "p", "children": "内容" }],
    "header": [{ "com": "h3", "children": "标题" }]
  }
}

作用域插槽

{
  "com": "MyList",
  "slots": {
    "item": {
      "content": [{ "com": "span", "children": "{{ slotProps.item.name }}" }],
      "slotProps": "slotProps"
    }
  }
}

🧩 注册自定义组件

import { useComponentRegistry } from 'vschema-ui';
import MyButton from './MyButton.vue';

const registry = useComponentRegistry();
registry.register('MyButton', MyButton);

⚙️ 全局配置

import { createApp } from 'vue';
import { createVSchemaPlugin } from 'vschema-ui';

const app = createApp(App);

app.use(createVSchemaPlugin({
  baseURL: 'https://api.example.com',
  defaultHeaders: {
    'Authorization': 'Bearer token'
  },
  responseDataPath: 'data',
  // API 响应格式配置
  responseFormat: {
    codeField: 'code',      // 业务状态码字段名,默认 'code'
    msgField: 'msg',        // 消息字段名,默认 'msg'
    dataField: 'data',      // 数据字段名,默认 'data'
    successCode: 200,       // 业务成功状态码,默认 200,支持数组如 [0, 200]
  },
  components: {
    MyButton: MyButtonComponent
  }
}));

响应格式配置

VSchema 支持自定义后端 API 返回格式,默认格式为 { code, msg, data }

// 自定义响应格式
responseFormat: {
  codeField: 'status',      // 后端使用 status 字段
  msgField: 'message',      // 后端使用 message 字段
  dataField: 'result',      // 后端使用 result 字段
  successCode: [0, 200],    // 0 和 200 都表示成功
}

当 API 返回的业务状态码不等于 successCode 时,会自动触发 catch 回调,错误信息从 msgField 字段提取。

🎹 事件修饰符

支持 Vue 事件修饰符语法:

{
  "events": {
    "click.prevent.stop": { "call": "handleClick" },
    "keyup.enter": { "call": "submit" },
    "keyup.ctrl.s": { "call": "save" }
  }
}

支持的修饰符:.prevent, .stop, .capture, .self, .once, .passive, .enter, .tab, .esc, .space, .up, .down, .left, .right, .ctrl, .alt, .shift, .meta

👁️ Watch 监听器

{
  "watch": {
    "searchText": { "call": "doSearch" },
    "user": {
      "handler": { "call": "onUserChange" },
      "immediate": true,
      "deep": true
    }
  }
}

🔐 安全特性

表达式求值器内置安全检查,禁止以下危险操作:

  • eval(), Function() 等代码执行
  • window, document, globalThis 等全局对象访问
  • constructor, __proto__ 等原型链操作
  • 直接的 fetch, XMLHttpRequest 调用(请使用 fetch 动作)

📚 完整示例

计数器

{
  "data": { "count": 0 },
  "computed": { "double": "count * 2" },
  "com": "div",
  "children": [
    { "com": "p", "children": "计数: {{ count }}, 双倍: {{ double }}" },
    {
      "com": "button",
      "events": { "click": { "set": "count", "value": "{{ count + 1 }}" } },
      "children": "增加"
    }
  ]
}

待办事项

{
  "data": { "todos": [], "newTodo": "" },
  "methods": {
    "addTodo": {
      "if": "newTodo.trim()",
      "then": [
        { "set": "todos", "value": "{{ [...todos, { id: Date.now(), text: newTodo }] }}" },
        { "set": "newTodo", "value": "" }
      ]
    }
  },
  "com": "div",
  "children": [
    {
      "com": "input",
      "model": "newTodo",
      "props": { "placeholder": "添加任务..." },
      "events": { "keyup.enter": { "call": "addTodo" } }
    },
    {
      "com": "ul",
      "children": [
        {
          "for": "todo in todos",
          "key": "{{ todo.id }}",
          "com": "li",
          "children": "{{ todo.text }}"
        }
      ]
    }
  ]
}

🛠️ 开发

# 安装依赖
pnpm install

# 启动开发服务器
pnpm dev

# 运行测试
pnpm test

# 构建
pnpm build

📄 License

MIT