【问题标题】:How to create a plugin for an application written in svelte.js?如何为用 svelte.js 编写的应用程序创建插件?
【发布时间】:2020-04-03 14:53:28
【问题描述】:

如果我用纯 JS 编写应用程序,我会像这样进行插件连接:

App.js

var App = function(){ /* ... */ };
//...
App.prototype.regPlugin= function ( atr1, atr2, ... ) { /* ... */ };
//...
App.prototype.sendToEventBus = function ( atr1, ... ) { /* ... */ };
//...
var app = new App();
//...
var appModules = {};
//...
document.onreadystatechange = function () {
   if ( document.readyState === 'complete' ){
      for ( var module in AppModules ) {
        if ( AppModules[ module ] ) {
        try {
            AppModules[ module ].init( app );
        } catch(er) {
            //...
        }
      }
   }
}
//...

插件.js

var MyPlugin = function (){ /*...*/ };
//...
MyPlugin.prototype.init = function ( app ) {
    this.app = app;
    //...
    app.regPlugin( plugAtr0 );
    //...
};
//...
MyPlugin.prototype.handleAny = function(){
   this.app.sendToEventBus( /* my event */ );
};
//...
appModules.myPlugin = new MyPlugin();

如何在 svelte.js 上为应用程序类似地制作插件?
自定义元素不太适合这个。

【问题讨论】:

    标签: javascript plugins svelte svelte-3


    【解决方案1】:

    好吧,如果你愿意的话,你可以做一些非常相似的事情。 Svelte 只为您提供了一个 UI 组件,您可以在页面上的任何位置呈现。它不会接管你的整个 JS。

    一件事是您的 Svelte 应用程序很可能会使用 ES import 语句捆绑(Rollup 或 Webpack)。这意味着您的代码将存在于 ES 模块中,并且局部变量不会自动附加到 ES 模块中的 window 对象。所以你必须明确表示。所以你的代码会变成这样:

    App.js (大概是你的应用入口点)

    import App from './App.svelte'
    
    const app = new App({
      target: document.body,
      props: {
        name: 'world',
      },
    })
    
    const appModules = {}
    
    // expose appModules as a global variable
    window.appModules = appModules
    
    document.onreadystatechange = function() {
      if (document.readyState === 'complete') {
        debugger
        for (var module in appModules) {
          if (appModules[module]) {
            try {
              appModules[module].init(app)
            } catch (er) {
              //...
            }
          }
        }
      }
    }
    

    所以现在,app 是您的 Svelte 根组件。它将存在于App.svelte 文件中。 Svelte 允许您通过exporting const or function 向组件添加实例方法。

    App.svelte

    您可以导出 constfunction 以在 Svelte 组件上拥有实例方法。

    <script>
    
      export function regPlugin(...) { ... }
    
      // or
      export const sentToEventBus(...) { ... }
    
    </script>
    ...
    

    还有……瞧?您的代码中还有其他内容吗?

    上述代码的一个问题可能是App 组件将在您的插件有机会注册之前呈现。

    您可以使用 App 组件中的道具解决此问题。为了能够从您的“控制器代码”更改此道具的值,您可以使用组件的$set 方法。您还可以在组件上设置accessors 选项。您可以使用捆绑程序插件选项全局执行此操作,也可以使用 &lt;svelte:options&gt; 在单个组件上启用它。

    如果您需要一些仅在应用准备就绪后运行的自定义逻辑,您可以在"reactive statement" 中执行此操作。

    App.svelte

    <svelte:options accessors={true} />
    
    <script>
      export function regPlugin() {}
      export function sentToEventBus() {}
    
      export let ready = false
    
      $: if (ready) {
        // code to run when ready
      }
    </script>
    
    {#if ready}
      <!-- content to show when ready (all plugins initialized) -->
      <!-- most likely, you'd put other Svelte components in there -->
    {:else}
      <div>Loading...</div>
    {/if}
    

    然后,您可以在应用准备好启动时切换此道具:

    App.js

    document.onreadystatechange = function() {
      if (document.readyState === 'complete') {
        for (var module in appModules) {
          ...
        }
    
        app.$set({ ready: true })
        // or
        app.ready = true
      }
    }
    

    或者,您可能更愿意将插件初始化代码移动到您的 App 组件中。由于您在这里有一个“静态”状态,因此在 appModules 变量中,您必须将其放入组件的静态 &lt;script context="module"&gt; 部分:

    App.svelte

    <script context="module">
      // this block only runs once, when the module is loaded (same as 
      // if it was code in the root of a .js file)
    
      // this variable will be visible in all App instances
      const appModules = {}
    
      // make the appModules variable visible to the plugins
      window.appModules = appModules
    
      // you can also have static function here
      export function registerPlugin(name, plugin) {
        appModules[name] = plugin
      }
    </script>
    
    <script>
      // in contrast, this block will be run for each new instance of App
    
      ...
    
      let ready
    
      document.onreadystatechange = function() {
        if (document.readyState === 'complete') {
          // NOTE appModules bellow is the same as the one above
          for (var module in appModules) {
            // ...
          }
          ready = true
        }
      }
    </script>
    
    {#if ready}
      ...
    {/if}
    

    静态函数addPlugin 可以作为命名导出从其他模块访问:

    import { addPlugin } from './App.svelte'
    

    这可能更适合于带有模块的捆绑应用程序/应用程序的上下文,而不是将事物附加到window(因此会在全局命名空间中遇到冲突风险)。取决于你在做什么......

    【讨论】:

      【解决方案2】:

      这种类型的插件设置仍然有效,请查看Client-side component API

      使用 component.$set,您可以将插件中的 props 更改为 svelte 组件。

      当您从 svelte 内部向您的插件/应用程序添加侦听器时,您可能需要额外的分配 data = myPlugin.data 以便 svelte 能够对更改做出反应。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-10
        • 1970-01-01
        相关资源
        最近更新 更多