[js] 不用插件:绑定思源事件总线(eventBus)

不多说了,用的人都懂。

简洁版 (不推荐,有风险)

优点:代码简洁。

缺点:要求用户至少安装一个插件。

⚠️ 风险警告:当用户关闭第一个插件时,之前的绑定会失效。

function eventBusOn(eventName, callback) {
    const plugin = window.siyuan.ws.app.plugins[0];
    if(!plugin) {
        console.log('绑定事件'+eventName+'失败,请至少安装一个插件');
        return false;
    }
    plugin.eventBus.on(eventName, callback);
    return true;
}
function eventBusOff(eventName, callback) {
    const plugin = window.siyuan.ws.app.plugins[0];
    if(!plugin) {
        console.log('解绑事件'+eventName+'失败,请至少安装一个插件');
        return false;
    }
    plugin.eventBus.off(eventName, callback);
    return true;
}

自定义 Plugin 版

优点:兼容性好。

缺点:代码量大。

// 参考 https://github.com/siyuan-note/siyuan/blob/f660b2ddfa98f93f778bbbbe5abc3bfaa5a932b6/app/src/plugin/index.ts
function eventBusOn(eventName, callback) {
    const plugin = getMyPlugin();
    plugin.eventBus.on(eventName, callback);
}
function eventBusOff(eventName, callback) {
    const plugin = getMyPlugin();
    plugin.eventBus.off(eventName, callback);
}
function getMyPlugin(pluginName = "my-custom-plugin") {
    let myPlugin = window.siyuan.ws.app.plugins.find(item=>item.name === pluginName);
    if(myPlugin) return myPlugin;
    class EventBus {
        constructor(name = "") {
            this.eventTarget = document.createComment(name);
            document.appendChild(this.eventTarget);
        }
        on(type, listener) {
            this.eventTarget.addEventListener(type, listener);
        }
        once(type, listener) {
            this.eventTarget.addEventListener(type, listener, { once: true });
        }
        off(type, listener) {
            this.eventTarget.removeEventListener(type, listener);
        }
        emit(type, detail) {
            return this.eventTarget.dispatchEvent(new CustomEvent(type, { detail, cancelable: true }));
        }
    }
    class Plugin {
        constructor(options) {
            this.app = options.app||window.siyuan.ws.app.appId;
            this.i18n = options.i18n;
            this.displayName = options.displayName||options.name;
            this.name = options.name;
            this.eventBus = new EventBus(options.name);
            this.protyleSlash = [];
            this.customBlockRenders = {};
            this.topBarIcons = [];
            this.statusBarIcons = [];
            this.commands = [];
            this.models = {};
            this.docks = {};
            this.data = {};
            this.protyleOptionsValue = null;
        }
        onload() {}
        onunload() {}
        uninstall() {}
        async updateCards(options) { return options; } // 返回选项本身
        onLayoutReady() {}
        addCommand(command) {}
        addIcons(svg) {}
        addTopBar(options) { return null; } // 模拟返回null
        addStatusBar(options) { return null; } // 模拟返回null
        // 去掉设置,参考 https://github.com/siyuan-note/siyuan/blob/dae6158860cc704e353454565c96e874278c6f47/app/src/plugin/openTopBarMenu.ts#L25
        //openSetting() {}
        loadData(storageName) { return Promise.resolve(null); }
        saveData(storageName, data) { return Promise.resolve(); }
        removeData(storageName) { return Promise.resolve(); }
        getOpenedTab() { return {}; } // 返回空对象
        addTab(options) { return () => {}; } // 返回空函数模拟模型
        addDock(options) { return {}; } // 返回空对象模拟 dock
        addFloatLayer(options) {}
        updateProtyleToolbar(toolbar) { return toolbar; } // 返回toolbar本身
        set protyleOptions(options) {}
        get protyleOptions() { return this.protyleOptionsValue; }
    }
    myPlugin = new Plugin({name:pluginName});
    window.siyuan.ws.app.plugins.push(myPlugin);
    return myPlugin;
}

已封装到 openAny 中。

[js] 连续点击 openAny,小代码,大作用,让一切触手可达

在 openAny 中调用方法示例:

const handler = (args)=>console.log(args);

openAny.on('open-menu-link', handler);

openAny.once('open-menu-link', handler);

openAny.off('open-menu-link', handler);

openAny.emit('your event name', data);

折中版(推荐)

优点:代码简洁和稳定的结合。同时,该功能更强大,还可以通过这种继承实现无插件的方式对插件函数调用等。

缺点:至少得安装一个插件才行。

function eventBusOn(eventName, callback) {
    const pluginName = 'my-custom-plugin';
    if(window.siyuan.ws.app.plugins?.length === 0) {
        console.log('绑定事件'+eventName+'失败,请至少安装一个插件');
        return false;
    }
    let myPlugin = window.siyuan.ws.app.plugins.find(item=>item.name === pluginName);
    if(!myPlugin) {
        const Plguin = Object.getPrototypeOf(window.siyuan.ws.app.plugins[0].constructor);
        const MyPlugin = class extends Plguin{};
        myPlugin = new MyPlugin({app:window.siyuan.ws.app.appId, name:pluginName, displayName:pluginName});
        myPlugin.openSetting = null; // 防止顶部插件按钮添加设置菜单
        window.siyuan.ws.app.plugins.push(myPlugin);
    }
    myPlugin.eventBus.on(eventName, callback);
    return true;
}
function eventBusOff(eventName, callback) {
    const pluginName = 'my-custom-plugin';
    if(window.siyuan.ws.app.plugins?.length === 0) {
        console.log('绑定事件'+eventName+'失败,请至少安装一个插件');
        return false;
    }
    let myPlugin = window.siyuan.ws.app.plugins.find(item=>item.name === pluginName);
    if(!myPlugin) {
        console.log('解绑事件'+eventName+'失败,未找到名为'+pluginName+'的插件');
        return false;
    }
    myPlugin.eventBus.off(eventName, callback);
    return true;
}
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    26401 引用 • 109793 回帖 • 2 关注
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    204 引用 • 1482 回帖 • 2 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
wilsons
正在努力开发 wilsons 工具箱中 🛠️ 目前已正式入驻爱发电啦!💖 想催更、提需求?欢迎访问 👉 https://afdian.com/a/wilsons