怎样实现自定义事件触发回调脚本

wen 实用脚本 23

本文目录导读:

怎样实现自定义事件触发回调脚本

  1. 浏览器环境 (JavaScript)
  2. Node.js 环境
  3. 基于配置文件的事件驱动系统
  4. WebSocket 方式(实时事件触发)
  5. 数据库驱动的事件系统
  6. 最佳实践建议

浏览器环境 (JavaScript)

使用 CustomEvent API

// 创建自定义事件
class EventManager {
  constructor() {
    this.events = {};
  }
  // 注册事件监听
  on(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
    // 返回取消订阅函数
    return () => {
      this.events[eventName] = this.events[eventName].filter(cb => cb !== callback);
    };
  }
  // 触发事件
  emit(eventName, data) {
    const callbacks = this.events[eventName];
    if (callbacks) {
      callbacks.forEach(callback => {
        callback(data);
      });
    }
  }
  // 移除特定事件的所有监听
  off(eventName) {
    delete this.events[eventName];
  }
}
// 使用示例
const eventBus = new EventManager();
// 注册回调
const unsubscribe = eventBus.on('userLogin', (userData) => {
  console.log('用户登录:', userData);
  // 执行自定义脚本
  executeCustomScript(userData);
});
// 触发事件
eventBus.emit('userLogin', { 
  id: 1, 
  name: '张三',
  timestamp: Date.now() 
});

使用浏览器原生 CustomEvent

// 创建自定义事件
function createCustomEvent(eventName, detail) {
  return new CustomEvent(eventName, {
    detail: detail,
    bubbles: true,
    cancelable: true
  });
}
// 监听事件
document.addEventListener('myCustomEvent', (event) => {
  console.log('自定义事件触发:', event.detail);
  executeScript(event.detail.script);
}, false);
// 触发事件
function triggerCustomEvent(scriptData) {
  const event = createCustomEvent('myCustomEvent', {
    source: 'user-action',
    script: scriptData,
    timestamp: Date.now()
  });
  document.dispatchEvent(event);
}
// 动态执行脚本
function executeScript(scriptContent) {
  try {
    // 方式1: 使用 eval
    eval(scriptContent);
    // 方式2: 创建 script 元素
    const script = document.createElement('script');
    script.textContent = scriptContent;
    document.body.appendChild(script);
    document.body.removeChild(script);
  } catch (error) {
    console.error('脚本执行失败:', error);
  }
}

Node.js 环境

使用 EventEmitter

const EventEmitter = require('events');
const { exec } = require('child_process');
const fs = require('fs');
class ScriptEventHandler extends EventEmitter {
  constructor() {
    super();
    this.scriptRegistry = new Map();
  }
  // 注册事件和对应的脚本
  registerScript(eventName, scriptPath) {
    if (!this.scriptRegistry.has(eventName)) {
      this.scriptRegistry.set(eventName, []);
    }
    this.scriptRegistry.get(eventName).push(scriptPath);
    // 自动绑定事件监听
    this.on(eventName, async (data) => {
      await this.executeScripts(eventName, data);
    });
  }
  // 执行注册的脚本
  async executeScripts(eventName, data) {
    const scripts = this.scriptRegistry.get(eventName) || [];
    for (const scriptPath of scripts) {
      try {
        if (scriptPath.endsWith('.js')) {
          // 执行 JavaScript 文件
          const script = require(scriptPath);
          if (typeof script === 'function') {
            await script(data);
          }
        } else if (scriptPath.endsWith('.sh') || scriptPath.endsWith('.bat')) {
          // 执行 shell 脚本
          await this.executeShellScript(scriptPath, data);
        }
      } catch (error) {
        console.error(`脚本 ${scriptPath} 执行失败:`, error);
      }
    }
  }
  // 执行 shell 脚本
  executeShellScript(scriptPath, data) {
    return new Promise((resolve, reject) => {
      const env = { ...process.env, EVENT_DATA: JSON.stringify(data) };
      const child = exec(scriptPath, { env }, (error, stdout, stderr) => {
        if (error) {
          reject(error);
          return;
        }
        console.log(`脚本输出: ${stdout}`);
        resolve(stdout);
      });
    });
  }
  // 触发事件
  trigger(eventName, data) {
    console.log(`事件 "${eventName}" 被触发`);
    this.emit(eventName, data);
  }
}
// 使用示例
const handler = new ScriptEventHandler();
// 注册脚本
handler.registerScript('fileChanged', './scripts/processFile.js');
handler.registerScript('fileChanged', './scripts/backup.sh');
handler.registerScript('error', './scripts/notifyAdmin.sh');
// 触发事件
handler.trigger('fileChanged', {
  path: '/path/to/file.txt',
  action: 'modified',
  timestamp: Date.now()
});

基于配置文件的事件驱动系统

config.json

{
  "events": [
    {
      "name": "user.register",
      "scripts": [
        {
          "type": "javascript",
          "path": "./scripts/welcomeEmail.js",
          "async": true
        },
        {
          "type": "shell",
          "path": "./scripts/logRegistration.sh",
          "params": ["username", "email"]
        },
        {
          "type": "python",
          "path": "./scripts/updateAnalytics.py",
          "params": ["user_id"]
        }
      ],
      "conditions": {
        "role": "user",
        "verified": true
      }
    },
    {
      "name": "file.upload",
      "scripts": [
        {
          "type": "javascript",
          "code": "console.log('文件上传完成:', data.filename);"
        },
        {
          "type": "shell",
          "path": "./scripts/virusScan.sh",
          "params": ["filepath"]
        }
      ]
    }
  ]
}

动态事件引擎实现

const fs = require('fs');
const path = require('path');
const { exec, spawn } = require('child_process');
const EventEmitter = require('events');
class DynamicEventEngine extends EventEmitter {
  constructor(configPath) {
    super();
    this.config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
    this.initialize();
  }
  initialize() {
    this.config.events.forEach(eventConfig => {
      this.on(eventConfig.name, async (data) => {
        // 检查条件
        if (this.checkConditions(eventConfig.conditions, data)) {
          await this.executeScripts(eventConfig.scripts, data);
        }
      });
    });
  }
  checkConditions(conditions, data) {
    if (!conditions) return true;
    return Object.entries(conditions).every(([key, value]) => {
      if (typeof value === 'function') {
        return value(data[key], data);
      }
      return data[key] === value;
    });
  }
  async executeScripts(scripts, data) {
    for (const scriptConfig of scripts) {
      try {
        await this.executeScript(scriptConfig, data);
      } catch (error) {
        console.error(`脚本执行失败:`, error);
        this.emit('script.error', { scriptConfig, error, data });
      }
    }
  }
  async executeScript(scriptConfig, data) {
    switch (scriptConfig.type) {
      case 'javascript':
        return this.executeJavaScript(scriptConfig, data);
      case 'shell':
        return this.executeShellScript(scriptConfig, data);
      case 'python':
        return this.executePythonScript(scriptConfig, data);
      default:
        throw new Error(`不支持的脚本类型: ${scriptConfig.type}`);
    }
  }
  executeJavaScript(scriptConfig, data) {
    return new Promise((resolve, reject) => {
      try {
        if (scriptConfig.path) {
          const scriptModule = require(path.resolve(scriptConfig.path));
          if (typeof scriptModule === 'function') {
            resolve(scriptModule(data));
          }
        } else if (scriptConfig.code) {
          const fn = new Function('data', scriptConfig.code);
          resolve(fn(data));
        }
      } catch (error) {
        reject(error);
      }
    });
  }
  executeShellScript(scriptConfig, data) {
    return new Promise((resolve, reject) => {
      const params = (scriptConfig.params || [])
        .map(param => data[param])
        .filter(p => p !== undefined);
      const child = spawn(scriptConfig.path, params, {
        env: { ...process.env, EVENT_DATA: JSON.stringify(data) }
      });
      let output = '';
      child.stdout.on('data', (chunk) => { output += chunk; });
      child.stderr.on('data', (chunk) => { output += chunk; });
      child.on('close', (code) => {
        if (code === 0) {
          resolve(output);
        } else {
          reject(new Error(`脚本退出码: ${code}`));
        }
      });
    });
  }
  executePythonScript(scriptConfig, data) {
    return new Promise((resolve, reject) => {
      const params = (scriptConfig.params || [])
        .map(param => JSON.stringify(data[param]))
        .filter(p => p !== undefined);
      const child = spawn('python3', [
        scriptConfig.path,
        ...params,
        '--data', JSON.stringify(data)
      ]);
      let output = '';
      child.stdout.on('data', (chunk) => { output += chunk; });
      child.on('close', (code) => {
        if (code === 0) {
          resolve(JSON.parse(output));
        } else {
          reject(new Error(`Python脚本退出码: ${code}`));
        }
      });
    });
  }
  // 触发事件方法
  trigger(eventName, data) {
    console.log(`触发事件: ${eventName}`);
    this.emit(eventName, data);
  }
}
// 使用示例
const engine = new DynamicEventEngine('./config.json');
// 触发用户注册事件
engine.trigger('user.register', {
  username: 'newuser',
  email: 'user@example.com',
  user_id: 12345,
  role: 'user',
  verified: true
});
// 触发文件上传事件
engine.trigger('file.upload', {
  filename: 'document.pdf',
  filepath: '/uploads/document.pdf',
  size: 1024576,
  type: 'application/pdf'
});

WebSocket 方式(实时事件触发)

// 服务器端 (Node.js)
const WebSocket = require('ws');
const http = require('http');
class WebSocketEventSystem {
  constructor(port = 8080) {
    this.server = http.createServer();
    this.wss = new WebSocket.Server({ server: this.server });
    this.clients = new Set();
    this.scripts = new Map();
    this.setupWebSocket();
    this.server.listen(port);
  }
  setupWebSocket() {
    this.wss.on('connection', (ws) => {
      this.clients.add(ws);
      ws.on('message', (message) => {
        const data = JSON.parse(message);
        this.handleMessage(ws, data);
      });
      ws.on('close', () => {
        this.clients.delete(ws);
      });
    });
  }
  handleMessage(ws, data) {
    switch (data.type) {
      case 'register_script':
        this.registerScript(data.event, data.script);
        break;
      case 'trigger_event':
        this.triggerEvent(data.event, data.payload);
        break;
      case 'execute_callback':
        this.executeCallback(data.callbackId, data.result);
        break;
    }
  }
  registerScript(eventName, script) {
    if (!this.scripts.has(eventName)) {
      this.scripts.set(eventName, []);
    }
    this.scripts.get(eventName).push(script);
  }
  async triggerEvent(eventName, payload) {
    const scripts = this.scripts.get(eventName) || [];
    // 广播事件给所有客户端
    this.broadcast({
      type: 'event_triggered',
      event: eventName,
      payload: payload
    });
    // 执行本地脚本
    for (const script of scripts) {
      await this.executeScript(script, payload);
    }
  }
  executeScript(script, payload) {
    return new Promise((resolve) => {
      if (typeof script === 'function') {
        resolve(script(payload));
      } else if (typeof script === 'string') {
        try {
          const fn = new Function('payload', 'resolve', script);
          fn(payload, resolve);
        } catch (error) {
          console.error('脚本执行错误:', error);
          resolve(null);
        }
      }
    });
  }
  broadcast(message) {
    const data = JSON.stringify(message);
    this.clients.forEach(client => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(data);
      }
    });
  }
}
// 客户端 (浏览器)
class EventClient {
  constructor(url = 'ws://localhost:8080') {
    this.ws = new WebSocket(url);
    this.callbacks = new Map();
    this.callbackId = 0;
    this.setup();
  }
  setup() {
    this.ws.onopen = () => {
      console.log('WebSocket 连接已建立');
    };
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.handleServerMessage(data);
    };
  }
  handleServerMessage(data) {
    switch (data.type) {
      case 'event_triggered':
        // 事件被触发,执行回调
        if (this.callbacks.has(data.event)) {
          this.callbacks.get(data.event).forEach(callback => {
            callback(data.payload);
          });
        }
        break;
    }
  }
  on(eventName, callback) {
    if (!this.callbacks.has(eventName)) {
      this.callbacks.set(eventName, []);
    }
    this.callbacks.get(eventName).push(callback);
    // 通知服务器注册监听
    this.send({
      type: 'register_listener',
      event: eventName
    });
  }
  trigger(eventName, payload) {
    this.send({
      type: 'trigger_event',
      event: eventName,
      payload: payload
    });
  }
  send(data) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    }
  }
}
// 使用示例
// 服务器端
const eventSystem = new WebSocketEventSystem(8080);
// 客户端
const client = new EventClient();
client.on('user_action', (data) => {
  console.log('用户操作:', data);
  // 执行自定义脚本
  if (data.script) {
    eval(data.script);
  }
});
// 触发事件
client.trigger('user_action', {
  type: 'click',
  element: '#button',
  script: 'alert("自定义脚本执行!")'
});

数据库驱动的事件系统

class DatabaseEventSystem {
  constructor(db) {
    this.db = db;
    this.listeners = new Map();
    this.setupDatabaseTriggers();
  }
  setupDatabaseTriggers() {
    // 监听数据库变更
    this.db.changes({
      since: 'now',
      live: true,
      include_docs: true
    }).on('change', (change) => {
      this.handleDatabaseChange(change);
    });
  }
  handleDatabaseChange(change) {
    const eventName = `db.${change.doc.type}.${change.doc.action || 'modified'}`;
    const data = change.doc;
    // 执行绑定的脚本
    this.executeEventScripts(eventName, data);
  }
  bindEvent(eventName, scriptConfig) {
    if (!this.listeners.has(eventName)) {
      this.listeners.set(eventName, []);
    }
    this.listeners.get(eventName).push(scriptConfig);
  }
  async executeEventScripts(eventName, data) {
    const scripts = this.listeners.get(eventName) || [];
    for (const script of scripts) {
      await this.runScript(script, data);
    }
  }
  async runScript(scriptConfig, data) {
    // 实现脚本执行逻辑
    // 可以支持不同的脚本类型
  }
}

最佳实践建议

  1. 异步处理:对于耗时的脚本,使用异步执行
  2. 错误处理:添加 try-catch 和错误日志
  3. 安全考虑
    • 限制可执行的脚本类型
    • 使用沙箱环境
    • 验证脚本来源
  4. 性能优化
    • 使用事件队列
    • 限制并发执行数
    • 实现熔断机制
  5. 调试支持
    • 添加日志记录
    • 实现事件追踪
    • 提供测试工具

选择合适的实现方式取决于你的具体需求:是浏览器环境、Node.js 后端、还是跨平台的实时系统。

抱歉,评论功能暂时关闭!