-
Notifications
You must be signed in to change notification settings - Fork 597
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
v6.x.x / v7.0.0 - wish list / roadmap #563
Comments
I would love if we could figure out a way to get code-splitting to allow for code-split bundles to gain access to var app = typeof window.app === 'undefined' ? choo() : window.app |
It would be nice to use barracks seamlessly again... |
Point no. 1 - routes rendered async, you might want to check out universal-router, i think they got it quite right, I also implemented their router in many side projects of mine. Excerpt: const router = new UniversalRouter({
path: '/hello/:username',
async action({ params }) {
const resp = await fetch(`/api/users/${params.username}`);
const user = await resp.json();
if (user) return `Welcome, ${user.displayName}!`;
},
}); Of course, this is more verbose with additional property names since choo doesn't use these - its, however, trivial to omit :-) |
Some clever way to do page transitions/animations. There has been some discussion before in #487 I'm particulary interested in page transitions, because element component specific animations should be resolved particullary in views or nanocomponents IMO |
Extending router by a before hook would just do, wouldn't it? Its also non-breaking:
|
Here's a radical thought regarding async routes: if they were generator functions one could progressively render a view as data/resources become available. Also, I do miss a standard way of handling errors in choo. Wrapping the route handlers with a try/catch and emitting a standard error event upon a route crashing or a yielded Promise rejecting would be super! var html = require('choo/html')
var choo = require('choo')
var app = choo()
app.use(api)
app.route('/', mainView)
app.mount('body')
function * mainView(state, emit) {
if (state.error) {
return html`
<body>
<h1>Oops!</h1>
<p>This happened: ${state.error}</p>
<button onclick=${onclick}>Try again</button>
</body>
`
function onclick() {
emit('fetch')
}
}
if (!state.data) {
yield html`
<body>
<h1>Loading</h1>
</body>
`
yield emit('fetch')
}
return html`
<body>
<h1>${state.data}</h1>
</body>
`
}
function api (state, emitter) {
state.data = null
emitter.on(state.events.ERROR, function (err) {
state.error = err.message
emitter.emit(state.events.RENDER) // Override current render cycle
})
emitter.on('fetch', async function () {
var response = await fetch('/api')
state.data = await response.text()
state.error = null
emitter.emit(state.events.RENDER) // Ignored during async render cycle
})
} |
Can a build with I had some difficulty 'unassertifying' via webpack (got it working in the end) and the |
@chrisdwheatley nice one! - yeah was thinking we move to https://github.com/emilbayes/nanoassert using the |
I think no1 priority should be the apparent performance issue as of now, thats a rather big issue imo |
@graforlock Any particular examples? |
Not many real world ones (not in a simple SPA website) but creating, updating and appending thousands of divs obviously keep recreates the full DOM paint and maybe needs some good caching/memoizing mechanism (if possible). |
I started doing this for async routes, it's nothing fancy or revolutionary but I'm putting it here in case it might come in handy in figuring out a framework-level solution. // client side
app.use(function asyncInitialState () {
return function (state, emitter, app) {
const routeFn = app.route.bind(app)
const onCallback = initialState => Object.assign(state, initialState)
app._getInitialState = {}
app.route = function (routeName, fn, getInitialState) {
routeFn(routeName, fn)
app._getInitialState[routeName] = getInitialState
}
emitter.on(state.events.NAVIGATE, () => {
const getInitialState = app._getInitialState[state.route]
if (!getInitialState) return
getInitialState(onCallback)
})
}
})
app.route('/blog', layout(blogPage), async () => {
const initialState = {
blog: {
fetching: false,
posts: await fetch(endpoint).then(res => res.json())
}
}
return initialState
}) // server side
async function routeHandler (req, res, next) {
const pathname = url.parse(req.url).pathname
if (regex.test(pathname)) return next()
var initialState = {}
var getInitialState = client._getInitialState[pathname]
if (getInitialState) initialState = await getInitialState()
const html = client.toString(pathname, initialState)
pump(req, res)
res.end(html)
} |
I think we it's possible to rewrite the this.emitter.prependListener(self._events.RENDER, nanoraf(function () {
self.state.href = self._createLocation()
var res = self.router(self.state.href)
if (res instanceof HTMLElement) {
var newTree = res
nanomorph(self._tree, newTree)
} else if (res && res.then typeof === 'function') {
res.then(function (newTree) {
assert(newTree instanceof HTMLElement, '...')
nanomorph(self._tree, newTree)
})
}
})) This way the current sync routes operate in the same way as they do now, plus you can also pass in a route handler which returns a promise (or just an async function, since they're based on promises under the hood). It might be possible to support callbacks too, as a third argument for route handlers, i.e. this.emitter.prependListener(self._events.RENDER, nanoraf(function () {
self.state.href = self._createLocation()
var res = self.router(self.state.href, function (err, newTree) {
if (err) return //not exactly sure how to handle this yet, feedback welcome :p
assert(newTree instanceof HTMLElement, '...')
nanomorph(self._tree, newTree)
})
if (res instanceof HTMLElement) {
var newTree = res
nanomorph(self._tree, newTree)
} else if (res && res.then typeof === 'function') {
res.then(function (newTree) {
assert(newTree instanceof HTMLElement, '...')
nanomorph(self._tree, newTree)
})
} else {
//do nothing, should be handled by the callback above
//maybe some kind of assertion could be here added to be sure
}
})) |
I've experimented a bit with async routes on my own fork, and it seems the biggest challenge would be to keep supporting the Maybe an optional async resolve mechanism would be more appropriate. A possible API could be: app.route('/', mainView, {resolve: fetchThings})
function mainView (state, emit) {
return html`<body>
<ul>${state.things.map(listItem)}</ul>
</body>`
}
function fetchThings (state, done) {
api.get('/things', function (err, res) {
if (err) {
return done(err)
}
state.things = res.data
done()
}
} |
As described by choojs#563: > https://polyfill.io provides a fallback for browsers that don't support it, so for most people this will be a strict improvement.
As described by #563: > https://polyfill.io provides a fallback for browsers that don't support it, so for most people this will be a strict improvement.
I want to gather a bit of sentiment for what people might be missing / what you feel might improve. The same as in v6, it'd be nice if all that was needed to update the major was
npm install --save choo@7
, and that's it.So far my wish list is:
PerformanceObserver
like inchoo-log
.xtend
withObject.assign()
- https://polyfill.io provides a fallback for browsers that don't support it, so for most people this will be a strict improvement.choo/component
, usingnanocomponent
- Ideally it'd be nice if we could formalize tracing / an event system with this too. That way we keep blazing ahead in terms of debugging.window.initialState
) to provide out of the box "rehydration" of state that was passed on the server.It's still early, but I'd be interested in hearing input from people on what you find tricky, which things you love, and which things you struggle with! Thanks! ✨
The text was updated successfully, but these errors were encountered: