Reputation: 67
If I were writing an application in pure JS, I would make a plugin connection like this:
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) {
//...
}
}
}
}
//...
plugin.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();
How to similarly make a plugin to an application on svelte.js?
Custom Element is not very suitable for this.
Upvotes: 2
Views: 1372
Reputation: 29897
That type of plugin setup would still work, look into the Client-side component API
With component.$set
you can change props from your plugin to the svelte component.
When you adding listeners to you plugin/app from inside svelte you might need additional assignments data = myPlugin.data
for svelte to be able to react to changes.
Upvotes: 1
Reputation: 25001
Well, you can do something very similar if you're so inclined. Svelte only provides you with an UI component you can render anywhere you want on your page. It doesn't take over your whole JS.
One thing is that your Svelte application will most probably be bundled (Rollup or Webpack) using ES import
statement. That means your code will live in ES modules, and local variables are not automatically attached to the window
object in ES modules. So you've got to make that explicit. So your code would become something like this:
App.js (presumably your application entry point)
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) {
//...
}
}
}
}
}
So now, app
is your root Svelte component. It would lives in an App.svelte
file. Svelte lets you add instance methods to components by exporting const
or function
.
App.svelte
You can export const
or function
to have instance methods on a Svelte component.
<script>
export function regPlugin(...) { ... }
// or
export const sentToEventBus(...) { ... }
</script>
...
And... Voilà? Is there anything more in your code?
One issue, maybe, with the above code is that the App
component will be rendered before your plugins had a chance to register.
You can fix this with a prop in your App
component. In order to be able to change the value of this prop from your "controller code", you can use the $set
method of the component. You can also set the accessors
option on your component. You can do this globally with a bundler plugin option, or you can enable it on individual components with <svelte:options>
.
If you need to have some custom logic that run only once app is ready, you can do so in a "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}
You can then toggle this prop when the app is ready to start:
App.js
document.onreadystatechange = function() {
if (document.readyState === 'complete') {
for (var module in appModules) {
...
}
app.$set({ ready: true })
// or
app.ready = true
}
}
Alternatively, you may prefer to move the plugin init code in your App component. Since you have a "static" piece of state here, in the appModules
variable, you'd have to put it into the static <script context="module">
part of your component:
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}
The static function addPlugin
would be accessible as a named export from other modules:
import { addPlugin } from './App.svelte'
This would probably be more suited in the context of a bundled app / app with modules, than attaching things to window
(hence running into risks of conflict in the global namespace). Depends on what you're doing...
Upvotes: 2