Skip to content

Instantly share code, notes, and snippets.

@Rich-Harris
Last active November 1, 2018 21:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rich-Harris/5baca5f6c05b7adfd9f59fe3ebacb584 to your computer and use it in GitHub Desktop.
Save Rich-Harris/5baca5f6c05b7adfd9f59fe3ebacb584 to your computer and use it in GitHub Desktop.
πŸ€”πŸ€”πŸ€”
<div on:click="set({ count: count + 1 })">
{count} {double}
</div>
<script>
export default {
data() {
return { count: 0 };
},
computed: {
double: ({ count }) => count * 2
}
};
</script>
<script>
let count = 1;
const double = () => count * 2;
</script>
<div on:click="count += 1">
{count} {double()}
</div>
@Rich-Harris
Copy link
Author

Rich-Harris commented Oct 30, 2018

Twitter convo that inspired this: https://mobile.twitter.com/threepointone/status/1057228025740304384. Tracking this stuff over on sveltejs/svelte#1826

Other comparisons:

@Rich-Harris
Copy link
Author

Going to use this gist to think out loud in a way that doesn't disrupt the conversation happening on the GitHub issue.

Clearly, the code in the <script> block as currently imagined would need to run once per component, rather than just a single time. (Is that weird? To some people it might be, but then people are often surprised that the code in their existing Svelte components only runs once, and they try and put state in variables outside the default export, so ???)

We can potentially radically simplify the current model. If we did away with computed properties as a separate concept, and relied on people invoking functions instead (but tracking the dependencies of those function calls, to make them reactive), then we could have a single piece of compiler magic (as regards the JS code itself): assigning to a variable or member expression triggers a re-render.

Unsolved problems:

  • props
  • store
  • refs
  • ...

The props problem is particularly tricky I think. One idea, for a component that accepts foo and bar props:

<script>
  export let foo = 'default foo value';
  export let bar = 'default bar value';
</script>

<p>{foo} {bar}</p>

Under the hood, this compiles to something like the following:

function create_main_fragment(component, ctx) {
  // ...
}

const Component = createComponent((__update, __props) => {
  let foo = 'default foo value';
  let bar = 'default bar value';

  __props(({ changed, current }) => {
    foo = current.foo;
    bar = current.bar;
    __update(changed, { foo, bar });
  });
}, create_main_fragment);

Of course, this doesn't give you a way to set internal state based on those values:

<script>
  export let foo = 'default foo value';
  export let bar = 'default bar value';

  let baz = foo + bar;
</script>

<!-- baz will be based on the default values, not the actual ones! -->
<p>{foo} {bar} {baz}</p>

But maybe it's okay for that to be a thunk instead?

<script>
  export let foo = 'default foo value';
  export let bar = 'default bar value';

  let baz = () => foo + bar;
</script>

<!-- baz() will never be rendered with the default values -->
<p>{foo} {bar} {baz()}</p>

@Rich-Harris
Copy link
Author

Other things I'm thinking about:

  • namespace: 'svg' β€” perhaps this becomes <svelte:meta namespace="svg">?
  • tag β€” perhaps this becomes <svelte:meta tag="my-element">? (We no longer need props, since this is defined by export let xxx)
  • Firing events. The best thought I've had on this so far is
<script>
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();
</script>

<div on:click="dispatch({ type: 'foo', x: 1 })">...</div>
  • Speaking of events: would be cool to align these with CustomEvent and get things like bubbling.
  • Also speaking of events: does it now make sense to go with <button on:click={e => doThing(e)}>?
  • Component bindings
  • Component refs... do these still make sense?
  • Interacting with a component from JS. What is the external API?
  • The mechanism for responding to changes: At the moment, onstate fires for updated props or internal state (because there's no distinction between the two). This results in cyclicality (when you call this.set(...) inside onstate) which leads to confusion and bugginess. I'd propose that we make that completely impossible by only calling onstate (or more likely onchange) when props have changed. I can't think of anything that would become impossible as a result, except for things that we want to be impossible. Also, we'd get rid of the arguments to onstate β€” no longer necessary.

@Rich-Harris
Copy link
Author

Preload! We need to figure out how to support preload β€” doesn't need to be the exact same mechanism, but something like it. (We could abandon it in favour of doubling down on Suspense, but it wouldn't quite cover the same area.

@Rich-Harris
Copy link
Author

In fact SSR more generally is a big unknown. In this new world, the initial setup code (which is currently what we'd call oncreate) always runs, on both client and server. onchange and onrender (or whatever) would never run. ondestroy could either be a noop (in which case any mount code would have to be wrapped in if (process.browser) or something), or it could be called immediately following render.

It's possible that we'd lose the API distinction between client and server. We could also consider introducing async SSR rendering. Lot of possibilities, lot of stuff to figure out.

@Rich-Harris
Copy link
Author

Oh, and all this needs to work with standalone components as well as normal ones

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment