Skip to content
This repository has been archived by the owner on Jun 16, 2020. It is now read-only.

Latest commit

 

History

History
147 lines (101 loc) · 5.09 KB

life-cycles.md

File metadata and controls

147 lines (101 loc) · 5.09 KB

The life cycles of a mercury app

A well structure mercury app is reasonably deterministic about when certain code runs.

There are a few main "phases"

  • Reacting to browser events from the event loop
  • Running application update logic
  • Running the rendering cycle in requestAnimationFrame

Reacting to the event loop

The only time JavaScript get's executed is when the event loop tells your code that a new thing happened.

This includes the initial evaluation of your browser.js source code.

On first evaluation you setup your state, render your view and insert it into the DOM.

Reacting to DOM events

All your handling of DOM events (i.e. click, change, ...) should go through dom-delegator.

dom-delegator intercepts all events and sees if you have registered an event handler using the ev-{{name}} syntax in h() or have registered a global listener using delegator.addGlobalEventListener().

If it finds one it will invoke it.

If you suspect there might be an issue or bug with an event not getting fired you should go into node_modules/mercury/node_modules/dom-delegator and edit your copy of dom-delegator to add print statements.

One common issue might be that your event is not in the whitelist ( https://github.com/Raynos/dom-delegator/blob/master/index.js#L10-L16 ) you should call delegator.listenTo(eventName) to make sure the delegator registers a global event handler for it.

Reacting to other entries from the event loop

There are many other ways the event loop can notify you that something has changed.

It's highly recommended that you take all other effects & entries to the event loop and wrap them in a geval interface.

For example:

The benefit is that once you've done this, you can now trace or debug geval to check all new events entering the event loop.

The other benefit is isolating your actual core application logic that does not concern itself with effects from the code with side effects.

If you put all the IO in one bucket and create a "seperate" part of your app that is just "on a geval event, run business logic and update state" then that second part is really simple to reason about and unit test.

Running application logic

geval event listeners

Eventually a event from the event loop will trigger into a new discrete value being emitted on a geval Event instance.

This discrete value should be a application specific value, you shouldn't have any dom events or raw xhr objects being emitted through geval but instead have already converted them into something specific to your application.

Application logic

At this point the listener to the geval Event is generally one of your applications update functions that has access to either the top level state or one of the nested states.

This is your core application / business logic and you do some computation and update the state into a new state.

The request animation frame rendering loop

Scheduling a requestAnimationFrame

Any time you do state.set() or state.some.key.set() the main-loop module will check if we have scheduled a rerender yet and if not will schedule a render on the next frame.

If you suspect a requestAnimationFrame is not scheduled then add logging or tracing to main-loop.

Entering a requestAnimationFrame

The browser will enter the main-loop rendering phase on the next animation frame.

Rendering the view to create a new virtual tree.

main-loop will call the top level render function that you passed to mercury.app(state, render) and create a new virtual tree.

One of the tricks that makes this fast is the fact that mercury.partial is used to avoid evaluating subtrees in render unless needed

If you suspect this doesn't happen just add debug statements to your top level render function.

Calling diff on the trees

The next step is the diff phase, here vtree will diff the new and previous tree. At this point any partial's that have actually changed will be evaluated and their respective rendering functions will be called.

If you suspect anything is wrong here it's recommended you add a print to main-loop to inspect the patches returned by the diff() function and see if your expected changes are included.

Calling patch on the DOM

The final step is calling patch() on the actual DOM with your patches from diff.

This is the only place in the life cycle of a mercury app where actual DOM manipulation happens. it happens at the end of any raf we have scheduled. Here all patches are applied and all hooks get called

If you suspect something is wrong here it's recommended that you use DOM mutation observers with a helper like listenMutation ( https://github.com/Raynos/jsonml-stringify/blob/master/examples/lib/listen-mutation.js ) to inspect the actual mutations applied to the DOM and see if they are what you might expect.