Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider extract the routing code from core #47

Closed
danigb opened this issue Feb 6, 2017 · 20 comments
Closed

Consider extract the routing code from core #47

danigb opened this issue Feb 6, 2017 · 20 comments

Comments

@danigb
Copy link
Contributor

danigb commented Feb 6, 2017

Hi @jbucaran,

I felt in love with hyperapp because is almost the perfect essence of the Elm application architecture. All the code is clean, simple and functional 👍 The only part that looks a little bit hacky is the routing code. But there are other reasons to extract it:

  • Not all applications need routing. That would help to reduce the size of the core.
  • Even if they use routing, is not always to change the view.

I don't know the best solution (that's why I'm opening this issue), but I think the API could be something like:

import { html, routes, app } from 'hyperapp'

const model = {}
const view = routes({
     "*": (model, msg) => {},
     "/": (model, msg) => {},
     "/:slug": (model, msg, params) => {}
    }
})

app({ model, view })

The new routes function should create a view (function) that selects the route proper view function using the browser history, and previously it registers the required event handlers, probably using the lifecycle events (more or less, the same code now it's inside the app.js file)

What do you think?

Anyway, congrats for this nice library

@FlorianWendelborn
Copy link

FlorianWendelborn commented Feb 6, 2017

@danigb I'm generally in favor of extracting it, but note that this will lead to people using different routing solutions that may not interoperate well with each other.

That in itself is not a problem, but the thing I like about hyperapp is that it's basically React+Redux+React-Router without any problems (React-Redux, Redux-Router) when trying to combine all of those (and of course the file-size).

@tunnckoCore
Copy link

tunnckoCore commented Feb 6, 2017

@danigb I've done it in 600 bytes :) last night and it is available at https://github.com/tunnckoCore/gibon and https://unpkg.com/gibon (i'm writing docs now). It is pretty customizable and works well with nanomorph, bel or any other.

edit: I'm in process to extract the "diffing" thing, so we can see if we could build hyperapp from smaller parts and remain the same size.

@tunnckoCore
Copy link

But yea, in anyway it should be in the core. Because the purpose of the hyperapp is to be kinda "complete" solution for building apps.

@itrelease
Copy link
Contributor

@jbucaran @maraisr it's also would be nice to move helpers/utils function in their own modules so they can be reused as I did in #28

@jorgebucaran
Copy link
Owner

@itrelease Yeah, but this would create a slightly larger bundle.

@itrelease
Copy link
Contributor

@jbucaran I think that 1kb shouldn't be a selling point of hyperapp. I'm with you on keeping it as small as possible but not paying for this by poor code base

@tunnckoCore
Copy link

@jbucaran I believe @itrelease means to separate helpers into new local file. It won't add additional bytes, because Rollup is smart enough.

@tunnckoCore
Copy link

tunnckoCore commented Feb 7, 2017

@itrelease i'm 👍 here. Also it is not really 1kb, fully functional is ~3.5kb - meh... don't know.

@jorgebucaran
Copy link
Owner

@tunnckoCore What your users will consume is what matters? And what they will consume ~1.6ish kb.

If you mean the codepen examples, etc., sure it's 3.1.

screen shot 2017-02-07 at 22 28 05

@itrelease I think that 1kb shouldn't be a selling point of hyperapp.

Why not?

@itrelease
Copy link
Contributor

@jbucaran because at some point I think you would need to add some feature or fix bug that would lead to bigger bundle size but I guess it would be more important than keeping 1kb?

@jorgebucaran
Copy link
Owner

@itrelease Yeah, that's not unlikely. I'll see if rollup is smarter than browserify here as @tunnckoCore said.

@tunnckoCore
Copy link

@jbucaran it is the father and mother of the tree shaking, it always will be better then the others.

As about the size... 1.6kb gzip is for JSX users

@jorgebucaran
Copy link
Owner

@tunnckoCore Yes, and also for Hyperx users alike, if they bundle with hyperxify.

browserify -t hyperxify index.js > bundle.js

@SkaterDad
Copy link
Contributor

That is one of Rollups big selling points: It merges all of the modules together, as if you had hand-crafted a single file.

@jorgebucaran
Copy link
Owner

jorgebucaran commented Feb 7, 2017

@SkaterDad In that case I apologize for my impatience. Browserify didn't do this for me, but rather created small little UMD modules all over the place. Maybe my setup was iffy.

@jorgebucaran
Copy link
Owner

jorgebucaran commented Feb 10, 2017

@danigb You still want to get rid of the router?

Should we get rid of the router?

Please 👍 👎 .

@FlorianWendelborn
Copy link

@jbucaran I'm against "getting rid of" it, but making it an optional import would be nice.

@danigb
Copy link
Contributor Author

danigb commented Feb 10, 2017 via email

@jorgebucaran
Copy link
Owner

jorgebucaran commented Feb 11, 2017

tl;dr

  • Let's remove the router. Why?
    • Not all applications need routing.
    • Reduce size of app.js
    • Too coupled with app.js, makes it difficult to test.
    • Router implementation is trivial, no query string handling.
    • Even if you use routing, it's not always about changing the view.
    • No support for hash-based routing
  • Solution involves having app return a render(view: View) function.
  • No API changes in app.
  • Router API would be one of the following:
    1. Router.app({...})
    2. Router(app({...}))
    3. Router.start({...})
    4. Router.start( app({...}) )

I've considered this issue again and I'd like to proceed with removing the router from app.js and also from this repository.

The current router implementation is "okay" for simple usage, but it's not fair to limit users from using a great router implementation due to HyperApp size goals, etc.

I've been fiddling with how the API would look like and I've come up with two approaches. There's actually a third one that looks similar to what @danigb proposed, but it requires app.js to know about pushState and that there is a router, etc.

I used the name Router in the examples, but this is also open to discussion. Router probably has the best search-ability, but Navigator was also an eye-candy.

A

Router.app({
    model: 1985,
    view: {
        "/": (model, msg, params, setLocation) => {
            console.log("!!!!", setLocation)
            return (
                <div>
                    <h1>Home</h1>
                    <button onclick={_ => setLocation("/about")}>About</button>
                </div>
            )
        },
        "/about": (model, msg, params, setLocation) =>
            <div>
                <h1>About</h1>
                <button onclick={_ => setLocation("/")}>Home</button>
            </div>
    }
})

B

Router.start(app({
    model: 1985,
    view: {
        "/": (model, msg, params, setLocation) => {
            console.log("!!!!", setLocation)
            return (
                <div>
                    <h1>Home</h1>
                    <button onclick={_ => setLocation("/about")}>About</button>
                </div>
            )
        },
        "/about": (model, msg, params, setLocation) =>
            <div>
                <h1>About</h1>
                <button onclick={_ => setLocation("/")}>Home</button>
            </div>
    }
}))

C

app({
    view: Router({
        "/": (model, msg, params, setLocation) =>
            <div>
                <h1>Home</h1>
                <button onclick={_ => msg.setLocation("/about")}>About</button>
            </div>,
        "/about": (model, msg, params, setLocation) =>
            <div>
                <h1>About</h1>
                <button onclick={_ => msg.setLocation("/")}>Home</button>
            </div>
    })
})

/cc @dodekeract

@danigb
Copy link
Contributor Author

danigb commented Feb 11, 2017 via email

jorgebucaran pushed a commit that referenced this issue Feb 11, 2017
* Extract router from app.js.
* Add documentation.
* Have app(...) return { ...options, render(view) {...}  } object
  which can be used to render a different view.
jorgebucaran added a commit that referenced this issue Jan 7, 2018
* Support nodes whose tag is a function.

This makes it possible to create jsx components
in this way: <MyComponent props=...></MyComponent>

* Remove router. Close #47.

* Extract router from app.js.
* Add documentation.
* Have app(...) return { ...options, render(view) {...}  } object
  which can be used to render a different view.

* Remove routing tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants