Deduplicate m.route and m.redraw logic (#2453)

- Remove appropriate route change subcriptions when a root is removed
  via `m.mount(root, null)`.
- Don't pollute `onpopstate` and friends - use standard event listeners
  instead.
- Simplify and streamline subscriptions, in preparation of adding a
  `remove` parameter to `m.mount`.
- Change the redraw internals to redraw immediately, with ability to
  cancel via returning a sentinel.
- Change `"bleeding-edge"` for `m.version` in `next` to instead just be
  the latest `m.version`. (If you're using `next`, you should know what
  you're in for.)
- Update tests to be aware of these changes. (Some were failing for
  subtle reasons.)
- Drive-by: remove some uses of `string.charAt(n)` and use `string[n]`
  instead.
This commit is contained in:
Isiah Meadows 2019-07-05 18:52:06 -04:00 committed by GitHub
parent 6c562d2b9b
commit 90bcff0fa7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 397 additions and 192 deletions

View file

@ -4,32 +4,38 @@ var Vnode = require("../render/vnode")
var Promise = require("../promise/promise")
var coreRouter = require("../router/router")
var sentinel = {}
module.exports = function($window, redrawService) {
var routeService = coreRouter($window)
var identity = function(v) {return v}
var render, component, attrs, currentPath, lastUpdate
var currentResolver = sentinel, component, attrs, currentPath, lastUpdate
var route = function(root, defaultRoute, routes) {
if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined")
function run() {
if (render != null) redrawService.render(root, render(Vnode(component, attrs.key, attrs)))
}
var redraw = function() {
run()
redraw = redrawService.redraw
}
redrawService.subscribe(root, run)
var init = false
var bail = function(path) {
if (path !== defaultRoute) routeService.setPath(defaultRoute, null, {replace: true})
else throw new Error("Could not resolve default route " + defaultRoute)
}
function run() {
init = true
if (sentinel !== currentResolver) {
var vnode = Vnode(component, attrs.key, attrs)
if (currentResolver) vnode = currentResolver.render(vnode)
return vnode
}
}
routeService.defineRoutes(routes, function(payload, params, path, route) {
var update = lastUpdate = function(routeResolver, comp) {
if (update !== lastUpdate) return
component = comp != null && (typeof comp.view === "function" || typeof comp === "function")? comp : "div"
attrs = params, currentPath = path, lastUpdate = null
render = (routeResolver.render || identity).bind(routeResolver)
redraw()
currentResolver = routeResolver.render ? routeResolver : null
if (init) redrawService.redraw()
else {
init = true
redrawService.redraw.sync()
}
}
if (payload.view || typeof payload === "function") update({}, payload)
else {
@ -40,7 +46,12 @@ module.exports = function($window, redrawService) {
}
else update(payload, "div")
}
}, bail, defaultRoute)
}, bail, defaultRoute, function (unsubscribe) {
redrawService.subscribe(root, function(sub) {
sub.c = run
return sub
}, unsubscribe)
})
}
route.set = function(path, data, options) {
if (lastUpdate != null) {