Flowplayer and single-page applications
When designing Flowplayer Native, the rise of the SPAs (Single Page Application) had already occurred. There are hundreds of various front-end frameworks these days, but React is probably the leader for now.
SPAs have evolved drastically since their inception, originally designed around the idea of making the Web faster by needing less data, like an
PATCH operation, they now use a complex set of algorithms designed for the purpose of rendering a web page very quickly without needing to reload it.
Over the course of a lifecycle with a modern SPA the DOM can change drastically and quickly. It's possible for tens and maybe even hundreds of players to be created and destroyed, which means special care must be given to handling both creating and destroying an individual player.
Creating a player must be cheap
Creating a player must be as cheap as possible. This is accomplished through lazy instantiation as many complex objects, such as the
dash.js instances. We defer creation of these complex instances until it's likely the user wants to interact with the video. By defering this we can be sure that rendering results in snappy UI transitions and we can render player instances as quickly as possible without performance drawbacks.
Lazy evaluation was not without some sticking points, especially with video where there are often several logical branches that can lead to needing switch between eager and lazy. A prime example of this is
autoplay where we must eagerly create these objects to be sure that we minimize our time to first video frame.
Destroying a player must be safe
Internally we aggressively cache DOM lookups and store references to the resulting
HTMLElements because of how video works, there are a lot of events that can happen quite quickly. Reducing the load on the event loop helps to maintain a high UX for older and mobile devices.
Boolean are always considered leaf-nodes by the garbage collector.
This is very problematic in SPA's as each create/destroy cycle will result in more and more of these Objects to build up over time, this is sometimes known as a zombie reference, because it's not alive but it also cannot be killed (released) by the internal garbage collection engine.
This was initially a tricky problem, because we wanted a way to easily ensure that when
destroy() was called on a player, all of these unsafe cache references were destroyed. Using the properties of primitive types we were able to build an
Array<string> where each element is an Object path.
Internally this looks something like:
const reaper = video.reaper = new Reaper() reaper.put("some_dom_element_name", player.find(".an-unsafe-reference")) // video.some_dom_element == player.find(".an-unsafe-reference") // added an unsafe reference for caching reaper.reap() // video.some_dom_element == 0 // it's been pruned
How the pieces fit together
All decent frameworks provide a way to have lifecycle hooks around DOM manipulation, for instance in React there is the
componentWillUnmount() hook precisely for this problem. Our internal implementation details for each player do not matter at this point, all a developer needs to do is use the
player.destroy() method. This ensures your SPA can live a full and happy life without bursting at the memory seams, and making it easy to not have to think about all of these problems we have already solved for you.