meteor / blaze

:fire: Meteor Blaze is a powerful library for creating live-updating user interfaces
http://blazejs.org/
Other
528 stars 115 forks source link

Template "groups" and code splitting idea #100

Closed msavin closed 6 years ago

msavin commented 8 years ago

Hey folks,

I'm popping in here after a brief chat in the Blaze slack. I really recommend going there. @mitar brought me up to speed on some issues and I thought I'd post my reaction to them.

My understanding is that we want to get away from the flat hierarchy into something that is more scalable when we have 1000 templates. I also suspect that Meteor will eventually integrate with Webpack, and with Webpack we get the big benefit of code splitting, etc. I also have an idea of how we can get that effect today. I thought I'd offer up my ideas on how to advance in this way while keeping Blaze simple and compatible.

Template Groups

The idea here is we have a templates that load on the first load, and then groups of templates that can be loaded on demand. Here's how that might look:

<template group="modal" name="edit">
    {{#form}}
        {{> input label="Name"}}
        {{> submit}}
    {{/form}}
</template>

<template name="anotherExample">
    {{> Template.dynamic group="modal" name=name data=data}}
    {{> modal.edit}}
</template>
Template.modal.edit.helpers({ ... })

With that, the developer has the option to group templates into a "mini tree". If they do not provide a group, it gets attached to the parent tree.

I suspect that would make it really easy for people to update their code incrementally and get all the benefits.

Considering Codesplitting

In consideration of code splitting:

Rule #1: All the templates that do not have a group specified load eagerly, unless overwritten.

Rule #2: All the templates with groups must be loaded by demand, unless overwritten.

// specify what groups load initially
Blaze.intial(['base','loaderScreen']);

// load a group of templates from the server
Blaze.load('modal');

I think the idea of loading "groups" instead of individual templates would be a much easier developer experience. We can also assume these templates would be chunked together in a reasoned about way.

(Additionally, maybe we could make Meteor pull in template groups in automatically.)

Enabling Code Splitting with-in Meteor's Build Tool

This would also open us up to what some may consider a hack, but I think makes sense, which is to retrieve these template objects via Meteor methods and then inject them into Blaze.

We can probably make a nice wrapper to get the objects from the server into the client. Someone already made a concept of this idea, cleverly named 'blazy'.

Disclaimer

Of course - this is coming from a very limited understanding of Webpack, React, etc. This perspective is coming from someone who see's the need to break up the template object and use code splitting but is content with Meteor and Blaze.

arist0tl3 commented 8 years ago

This immediately gets my head spinning with ideas, although they are probably unrealistically optimistic, haha.

In thinking about group definitions, one thing I loved from arunoda was grouping routes in flow-router and being able to apply rules and actions to a group of routes.

Currently, auth logic can be cumbersome, as it's either repeated on a multitude of similar templates, or contained in a "parent" template that is wrapped around the children templates. Maybe it's just me, but template-level auth is definitely a pain point for me in Blaze.

Another DRY pain point is using a set of helpers in a group of templates, but not wanting to introduce them into the "global" helper scope (registerHelper).

I'm wondering about the possibility of using groups to scope things like auth logic and helpers to a set of templates.

Feel free to bring me back to reality =)

mitar commented 8 years ago

I do not think we should be introducing a new concept like "group". I think it would be simpler to or reuse packages, or create a hierarchical templates. So we should simply allow names like "foo.bar" which would then be template "bar" under "foo".

About lazy loading of templates, templates are compiled to JavaScript. So once we have them represented as JavaScript modules, and Meteor has lazy loading of code, the rest comes for free out of it. So the idea would be that when you import at runtime some module (which has a Blaze template code) which is not yet loaded on client, it would load. You can of course preload some things.

So I think the lazy loading is orthogonal to what Blaze is and should be probably discussed inside Meteor. What Blaze should provide is to be compatible with various JavaScript loading mechanisms so that it works.

mitar commented 8 years ago

Also, I have been using the following pattern in my Meteor apps for quite some time now and it works very well. So once you have templates as objects, then I have:

You can see then how this can be loaded dynamically as well. So you can import edit view, which returns Foo.Edit and it also attaches itself to Foo namespace when it is first time loaded. So you can use both approaches you like: importing, or using namespaces to load things. In the template you could then do:

{{> require('./edit').Edit}}

Or:

<script>import './edit'</script>
{{> 'Foo.Edit'}}

Whichever you prefer. (Or you would have edit be preloaded as it is now, so you would just do:

{{> 'Foo.Edit'}}
msavin commented 8 years ago

I think there's two ways to look at it: the first way is, we are sort of able to achieve this functionality by prefixing our template names. the other way is, people are prefixing their template names, therefore grouping them, so we should build a stronger facility on top of this behavior.

The latter is how most products gain new features. For example, people on Twitter began to hashtag their tweets, and later, Twitter built official features around it.

For template groups, I see a number of things we can do to elevate the experience:

With Lazy Loading, I think it would be a unique, and far simpler experience to just import a group of templates instead of importing them one by one. There's also far less maintenance, as when you add or edit templates in a group you do not have to update the lazy loader.

As for imports/modules - I'm really not seeing what value they bring to Blaze aside from cluttering it and requiring us to write trivial code.

mitar commented 8 years ago

Yes, but grouping can be implicitly done through the filename using some delimiter. Like file paths are. I do not think we need an extra attribute. And you could still assign helpers to for example just Components.Post.One or Component.Post namespaces. In some way look at it as adding hierarchy of groups to your proposal.

mitar commented 8 years ago

But I am still not sure if we should provide Blaze-own type of namespace (it has one, we can extend it, we can add groups, hierarchy, whatever).

Or should we just piggy-back on top of JavaScript modules and then also get lazy-loading for free by using some lazy-loading of normal JavaScript modules as well. To me it feels like we are reinventing the wheel by doing our own namespaces.

I think we could still make some helpers or sugar to import stuff. So instead of having:

<script>import './edit'</script>

We could have:

<import>'./edit'</import>

Or something like that.

msavin commented 8 years ago

Well, the more I think about grouping, the more sense it makes to me. As from #126,

I'm big on grouping because we can import and export pieces of an application instead of focusing on a template at a time. I believe that's the right level of abstraction.

It would also require us less changes when we update parts of an app. For example:

Notifications = new Blaze("notifications");

Notifications.register({
    notifications: {
        // ...
    },
    notification: {
        // ...
    }
});

We can expect our application to render in "notifications" anywhere, and then we can trust that even if we rewrite that entire set of templates, it would work fine. We don't have to update 100 import/export statements all over the app.

Looking at it from a developer experience standpoint, I could see myself developing different parts of the UI as packages, which would contain the group of templates and the server code that supports it. That way we are splitting our app based on features. Then, on page load, I would load the essential views, and then would like to do Blaze.load("notifications") when I would need that view.

Perhaps a thing to consider is, if a Blaze developer wants or likes the import/export concept. I think the Blaze developer is more likely to be a designer and would probably prefer a simple way to do things.