I want svelte to handle simple stateless components better
I've always run into situations where I have lots of simple dumb components that don't have states but the compiler ends up generating a full blown svelte components instead of a simple JS function (a pure function somewhat similar to React's function component) that emits HTML string.
In other words, why can't the svelte compiler generate code for simple stateless components in SSR fashion for the front-end?
Current work-around
export function MyStatelessComponent(arg1, arg2) {
return `<div>Some HTML string from ${arg1}</div>`
}
import {MyStatelessComponent} from "MyStatelessComponent.js"
{@html MyStatelessComponent(arg1, arg2)}The solution I would like
An ideal implementation would be for svelte compiler to identify stateless components and emit an efficient code like the one above.
<script>
export let arg1;
export let arg2;
</script>
<div>
Some HTML string from {arg1}
</div>
import MyStatelessComponent from "MyStatelessComponent.svelte"
...
<MyStatelessComponent arg1={...} arg2={...} />
{@html MyStatelessComponent(arg1, arg2)} type of codeHow important is this feature to you?
Couple of important points:
.svelte file components with .js file components is giving us trouble when bundling (specially css purging). It would have been a lot easier if all of our components are ordinary .svelte components.Additional context
Demo for the above component in REPL: https://svelte.dev/repl/eb9e018b42574c43b788af809e3e8582?version=3.15.0
Look at the unnecessary JS output of the compiler in the above REPL code
Just a little warning here, your workaround should not be used as it because it introduces xss vulnerabilities.
(Another way of solving the above issue...)
Another thing I noticed (about simple stateless components) is that bundle size significantly increases when I extract portion of my svelte HTML into a component.
For example, if I had the following fragment:
my-page.svelte:
<div>
<i class="fas fa-{icon}"></i>
</div>
and I want to extract the <i> into its own component:
MyIcon.svelte:
<script>
export let icon;
</script>
<i class="fas fa-{icon}"></i>
and use it as:
my-page.svelte:
import MyIcon from "./MyIcon.svelte"
...
<div>
<MyIcon {icon} />
</div>
then the bundle size increases significantly only because I moved some HTML into a new component.
So, I feel like if the Svelte compiler can figure out that MyIcon component is totally stateless, it can avoid generating an entire Svelte component for it. But rather make the component inline
So, after compilation (or transpilation?)
my-page.svelte:
import MyIcon from "./MyIcon.svelte"
...
<div>
<MyIcon {icon} />
</div>
could become
my-page.svelte:
<div>
<i class="fas fa-{icon}"></i>
</div>
I have seen tremendous bundle size reductions when avoiding separate components (when used in lots of places in my project). But loose the benefits of making them in their own component files.
Issue #3898 directly relates to this.
I'm not so sure your solution of not using components has the benefits you think it does. Consider this REPL https://svelte.dev/repl/5a97ad55d3834ab595fdd4996c7f6fd6?version=3.15.0
In that REPL (which is a slightly modified version of your original one) you can see RepeatComponent generates 314 lines of code and no component generates 345. So even though the fairly simple stateless component itself is 59 lines of code, the more it is reused, the less other lines are generated.
There is also the obvious downside of, if not using a component, making any changes to the uses will be a nightmare. Like needing to find all uses of class="fas fa- and replace that with some other icon class library.
You should also probably consider minification and gzipping, where repeat uses of the same functions are trivialized by gzip. That isn't to say it can't be handled differently, but I don't believe the bundle size is significantly increased by making components, especially the more they are reused.
@vipero07 Run some tests using your unmodified code from the REPL you posted above
I downloaded it into a zip file and modified only App.svelte then run yarn build. Results:
NoComponent and removing RepeatComponent bundle size: 5.68 KBRepeatComponent and removing NoComponent bundle size: 6.44 KBNote that the build command produces a minified bundle.
Fair, however you are forgoing OOP principals like DRY in favor of 0.78 KB (in this case). I get that having a bunch of smaller components this may add up but I imagine the actual difference between many components and copy pasta is just as trivial. 1KB is nothing compared to something like the entire React library. I'm not railing against the idea of helping reduce the overall size. However personally I'd avoid copy pasta or any of the other solutions for that small a savings, and code with the expectation that a future release implements #3898 or something similar.
Consider that change occurs in the next release and now you have to refactor everything.
Overview
Svelte generates classes that are able to reconcile changes to data. However, quite often I find myself knowing that the data will change completely when updated and that there is no UI that can store user state like form fields. In this case, the reconciliation may be largely unnecessary and we would do just as well to blast away what's there and start anew.
As an example, on the homepage of hn.svelte.dev, if I hit "More..." to go to the next page then there's probably not a need to compare the new data to the old data. I don't need to individually check if item.domain, item.url, item.id, item.title, item.user, item.comments_count, etc. changed. If I got a new item I'm fine assuming they all changed. That allows the component to be much smaller and dumber
Benefits
This change would have two large benefits:
[page].js are the p methods and those could be removed. This would result in faster network transfer as well as reduced script parsing times.Drawback
In terms of costs, there is likely some savings we get today by reusing the existing DOM structure that we would lose. However, most of any savings could be gained back by simply working on optimizing fragment creation (https://github.com/sveltejs/svelte/issues/3898). E.g. by creating a template and cloning it instead of recreating the DOM structure for each instance.
Implementation
I'm thinking this would be specified in <svelte:options>. Perhaps something like <svelte:options reconcile=false />. There may be cases where you would want to call a component in a reconciled fashion and non-reconciled fashion. In that case you would simply use the standard reconciled component everywhere. Once you are including that extra code in your app in one place, there's not much need to do something different elsewhere
Though I wonder if there might be some other way to accomplish this as well. It almost feels like the combination of immutable and a keyed each block should give this to me
I implemented this for the page component of the hn.svelte.dev example just by editing the output of the compiler as can be seen below. create_fragment initialized some values that I had to update in p by duplicating the initialization code and that could be refactored out into a separate function to reduce the duplication if desired.
m: function mount(target, anchor) {
this.target = target;
...
},
p: function update(ctx, [dirty]) {
t0_value = /*item*/ ctx[0].title + "";
if_block0 = /*item*/ ctx[0].domain && create_if_block_1(ctx);
var anchor = article.nextElementSibling;
this.d(true);
this.c();
this.m(this.target, anchor);
},
Unanswered questions
Most helpful comment
(Another way of solving the above issue...)
Another thing I noticed (about simple stateless components) is that bundle size significantly increases when I extract portion of my svelte HTML into a component.
Initial Situation
For example, if I had the following fragment:
my-page.svelte:
and I want to extract the
<i>into its own component:MyIcon.svelte:
and use it as:
my-page.svelte:
Problem
then the bundle size increases significantly only because I moved some HTML into a new component.
Probable Solution
So, I feel like if the Svelte compiler can figure out that
MyIconcomponent is totally stateless, it can avoid generating an entire Svelte component for it. But rather make the component inlineSo, after compilation (or transpilation?)
my-page.svelte:
could become
my-page.svelte:
I have seen tremendous bundle size reductions when avoiding separate components (when used in lots of places in my project). But loose the benefits of making them in their own component files.