Let's Build a Virtual DOM from Scratch

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi I'm Kevin in this video I'm going to talk about how to build a simple virtual Dom library from scratch so what is a virtual Dom virtual Dom is basically a JavaScript representation of real Dom nodes and the reason why you use a virtual Dom is so that you can quickly compare virtual Dom's and make the minimal number of changes to the real DOM necessary each time you want to make an update so why are we doing this basically it comes down to understanding how our tools work internally help us to avoid bugs and take better advantage of their characteristics but also to help demystify some of what what's happening underneath so an overview of kind of what I'm going to be covering in this video I'm going to start with just an overview of the project and kind of how its set up it's pretty simple not to it and I'm going to start with JSX because that's what most people are familiar with and I'll talk about how to use babel to transpile that to virtual hyper script which is just a function representation and then we can take that and turn it into an actual virtual Dom representation for today it's just going to be plain objects and arrays nothing too fancy then once we've gone through that I'll show you how to create real Dom nodes based off of virtual Dom nodes so that's gonna be like the first half and then the second half is talking about diff and patch so how do we update Dom nodes so diff is kind of like let's figure out what changes we need to make and then patches the process of applying those changes to the real DOM and then there's basically three kinds of things we'll need to update the type of the tag so like ul versus div any child so like allies of U ELLs and and properties things like IDs and class names for the purposes of this video I'm going to use the word property and attribute interchangeably I know that there are two different things but just for the purpose of this video just kind of considering them to kind of be the same thing so just a representation of kind of what we're gonna do here so in our first pass through we're gonna start with JSX and then that's gonna transpile down to virtual hyper script before the user even shows up on the page and then when we call the virtual hyper script function we're gonna end up with virtual Dom nodes and then we're going to use a create element function to turn that virtual Dom list into our first set of real Dom nodes on the second pass through and every successive pass through basically every time we have a state change we're going to create a new virtual hypeeee we're going to call the virtual hyper script again to create a new set of virtual Dom nodes then we're going to compare the new set of virtual Dom nodes with the previous set of virtual Dom nodes and that's going to give us a list of patches that we need to make and then we're going to call our patch function to actually apply those changes to the real Dom nodes and the end result will be and efficiently updated real Dom some of the things that I'm not going to cover like events like on click and on change and that stuff they're a little bit more complicated and there's other videos out there covering that we're not going to go into like Reax components and life cycle methods those are just a little bit more complicated than the virtual Dom itself we're not going to do format SVG elements mostly for the sake of just kind of keeping the scope reasonable they're not too hard to add but it again takes some extra tweaks data attributes kind of have their own set of rules around them as well we're not going to do much in terms of error checking and handling that's like if you were using like a real library you would want you know a good amount of error checking and handling to make it easier to use and then caching and performance is a pretty big part of making this work efficiently particularly if you have like a really large graph I won't be talking about that but it's definitely necessary if you want to build like a really big website with a lot of different nodes on it so let's go ahead and get started I'm going to just kind of talk about what I have set up here so you'll see here I have an index.html my body tag is just like a little basic style and just said annoys me less and then I've got three classes so we're gonna switch at one point between those three so you can see that that's updating it's pulling in a compiled j/s file so i have like an index that I'm writing and I'm gonna compile it to compile j/s I've got my main tag and this is kind of gonna be where everything's gonna render too and then script wise I'm just grabbing that main tag then calling the render function so render is gonna be like our main function that kind of starts the chain it starts the process so I can kind of show you in the browser I've got it open over here so you can see it's you know we'll be working with that then in package.json most of this is pretty standard for you know any kind of node project here we have a Babel we're using Babel CLI and then we're also using just the transform react JSX plugin so I'm not using anything else from it I'm just using that one particular plugin the most important line though is here on seven where I'm basically transpiling from my index J asked to compile J s so every time run npm run compile essentially my index j s will compile to compile j s I have a Babel RC file and I'll talk about that in just a few minutes here and then in my index J s I've already got the functions outlined and like with arguments they take so during the course of this video I'm just going to be going through and filling these in and explaining what each of these little parts of the code do so that's an outline of the project so I'm going to start off with JSX transpiling so if I go to this view function here I'm just gonna write some really simple JSX nothing too fancy just to give a demonstration of you know transpiling so when I when I have my JSX representation and I run my compile when we look at the compiled version we'll see that it's changed to this function representation so this is working with this plugin react JSX transform it really has nothing to do with react specifically I'm guessing they named it that mostly for SEO purposes but you can see here it's basically transforming JSX into just JavaScript function calls the default is react create element but you can customize it to whatever you want you can see the first argument is the tag name and the second argument is you know like properties and attributes that can be either an object or null and then any arguments after that are the children so there's there's those three kinds of things again so this representation is often called virtual hyper script some people use that name some dough we have this virtual Dom representation now in virtual hyper script I'm just using the the letter H that comes from this babel RC I'm just telling it to call it H it's just a really common name that I've seen in different virtual dom libraries so that's the one I'm using here so we'll need to essentially write a way to handle those age functions so for today I'm going to keep it pretty simple and really just used like playing objects and arrays now what I have almost works but we need to kind of take it two steps further the first is that if you remember props can be either null or an object so I need to handle that case because I just want to be able to easily assume that it's always an object the other thing is that sometimes children will come in just as a list of arguments but then sometimes it'll come in as an array so we'll want to flatten that down a level and my flatten function just kind of borrowed this one from Stack Overflow it looks a little crazy but the magic here is with apply taking an array of arguments instead of just you know the arguments list it out so that's just gonna flatten anything that I have you know one level deep so let's go ahead and compile this and then you can see that my compiled file has been updated and now if I go to my browser and I call that you can see now I have a virtual Dom representation I'll make this a little bigger so the type of the tag as you well the props have oops I think I messed up it should be an ID should refresh to okay so now that's an ID and then children is just an array and the nodes underneath of that follow the same pattern and then here you can see that the nodes are actually strings so it can basically be either in an object or just a string so that's the representation that we're gonna be working with as far as when we get to like creating elements and dipping them we'll be using this particular format so we can now go ahead and start to actually create real element that's going to be the next step is from that virtual Dom representation to then actually create real elements so if you remember render is gonna be kind of our input into our application so I'm gonna start by just giving it a view function the zero you don't need to worry about right now later on we're gonna have a state that's just a count when we do like updates so this view call is just gonna give us you know our virtual Dom nodes and then I'm gonna wrap that with create element so I'm going to pass those virtual Dom nodes and to create element which will give me a collection of real Dom nodes and then I'm just going to append that to that element which if you remember is just mean so right now create element hasn't been written yet so it's not really gonna do very much event thing so we need to go in and actually write our view element function now or create element so create element for just the tag is pretty easy so if you remember there's objects and strings so if it's a string then we're just going to return a text node otherwise we'll just recreate you know just a regular element so I'll go ahead and compile that and hopefully we'll see you know in our in our main tag here we'll see a ul element okay so that's getting the tag now if we wanted to do the children it's just a little bit more complicated than that so I'm going to set the L to a variable and I'm just gonna go ahead and get those children and then I'm gonna map over them and I want to call this recursively so that you know creating elements successively and then on all of those I'm just going to append that to whatever element is the parent and here I'm using bind because with the for each you would lose that context so now if this works you should see two UL eyes and some text so here you can see now we have the UL and we've got two lis and we've got some text on our page as well the third thing would be to add properties to this so I'm just going to add another stage to this so I'm going to farm this out to another function just because I always have a bit of a tough time with longer functions and reading them so set properties we're just going to iterate over all of those properties and then I'm just going to farm that out to a set prop function and then this one's pretty simple we're just going to use the built in set attribute name value so I'll go ahead and run that and show what that looks like oh we got an error so miss the letter S I always do that so now we can see we have some properties rendering you'll notice though that a they were lowercase so set attribute will lowercase your your attribute names and then also it's not handling class name correctly so we'll need to update that and just handle that case and hopefully that's all it takes to get that class name correct so cool that's pretty much the first half of this is really just you know creating you know you know going from the JSX to virtual hyper script getting a virtual dom representation and then writing this function that then will actually create real elements in the real dom so the second half of this is the diff and patch so I'll bring that up again real quick so we'll start with the state change right in this case it's just gonna be a counter that happens goes up increments by one every 500 milliseconds we're going to then feed that into all of our virtual hyper script functions and create a virtual Dom representation then we're going to compare the the current virtual Dom implement representation with the previous one from that we're going to get a list of changes and then we're going to apply those changes to the real Dom so we end up with an updated Dom so I'm gonna start this off by you know making it so that we have a good working example so that as we're iterating through this we can actually see the different things functioning so I'm basically just going to have it update every 500 milliseconds there's really nothing specific about this implementation to that it's on a timer you could use this with redux or you know any kind of state management system you wanted to so again we're just gonna update it basically every 500 milliseconds and we're going to just need a comma there we're going to update it you know by one each time and then this wound up driving me crazy so we'll have an out basically so that it doesn't just keep going infinitely so we said I said before so we have to compare the previous and the new virtual Dom and then we'll get a list of changes from that and then we need to apply that to the real Dom so it's just get a list of the changes from diff and here I'm gonna be a little bit inefficient and just recalculate at each time I don't really care for this example so that's going to give us our list of changes and you'll notice we have new and then old that's just the way that the real Dom API is also set up so I'm following that pattern there and then we're gonna patch those changes onto the the real Dom in this case I'll being that me element and then for today so that we can also see those patches I'm just gonna lock those out each time just so that we can see it what the changes are so hopefully if I've written this correctly we'll start to see it log every 500 milliseconds here in my console so that looks pretty good the other thing is that this view isn't really going to show us much because there's nothing using that count here so just bear with me I'm gonna write and something a little bit more complicated for our view this looks a little crazy but basically it's just a way of getting a range so I'll show you that real quick so if count is like 10 and we do this then you can see that gives us an array of like 0 through 9 and then I'm just going to again using you well and I'm going to bring in a couple of different things one is that this class is going to change basically each time so that we can see that updating when we get there and then each time we're going to have an you know just an additional Li and then so that you can see some stuff updating in there you know we're gonna do a little bit of multiplication so you'll see you know things within each of these Li is updating as well so we're kind of creating a bit of a worst case scenario for our rendering engine here and I'm just gonna compile that real quick just to make sure I don't have yep any errors looks like I've got a syntax error in here somewhere the easiest code to write so I'll just make sure I didn't crash anything here all right so we'll go back to you know now we need to do the the actual diff and patch algorithms and so the first thing I want to do is show the the type of the tag so we'll we'll diff the type of the tag and then we'll patch the type of the tag then we'll do children so we'll diff the children and then we'll patch the children and then the last thing we'll do is properties so we'll diff the properties and then we'll patch the properties so our dip function is pretty simple if there's basically four kinds of changes that we could potentially need to make create which means like hey we didn't have it in the old but we do have it in the new so we need to make a new note remove which means hey we didn't have this in the old one or excuse me we had it in the old one but we don't have it in the new one so then we need to remove it replace means like the type of the thing has changed like the tag name or it's gone from being an object to a string and so we need to do a replacement and the last one is update and then update is kind of a continue it means hey there may be children or properties to look at that need to be updated so it's kind of our default mode so I'll just go ahead and write pretty much exactly what I just described so if we don't have an old note but we you know now have a new note I'm just gonna tell it that hey we need to do a create operation here and I'll give it the new node so it knows what to create and then if we don't have a new note but then you know we would obviously have a an old node because we've ruled that out then we just need to tell it to remove it and then if there's been a significant change you know like the type of the tag has change or it's gone from an object to a string then we'll need to handle that and tell it to do a replacement and replace is going to be very similar to create so it's also going to just need that new node and then I'm just assuming that we have a type right so it's not a string we're just going to tell it to always do an update here and so if this works we should pretty much just see update each time because the UL tag is that that parent so I'm going to go ahead and run that and here we can see now it's just saying to update each time so nothing too fancy so now we'll do a patch on the the tag the type of the tag so the first thing is that if we don't have any patches if we don't have any changes to make we're just gonna exit we don't want to do a bunch of work if we don't have to I love early return by the way it's probably my favorite programming construct I love doing the least amount of work possible and then the element that's going to be what we're gonna be working on it's basically going to be whatever the the top-level one is so right so the first iteration parent will be main and so this will end up being ul right so it's just walking down one step and then we're gonna switch over essentially what type of change we've got right so we had four different cases we had create remove replace and update so remove is the easiest case we'll just tell hey parent now go ahead and remove that child and then for create we need to actually create those elements so I'm gonna grab my new node out of my patch and then I'm going to create a new element from that just using our very trusty create element function and then I'm just going to append that to my parent and then replace is almost identical so we're going to create a new node we're gonna create new elements and then we're going to instead of appending it we're going to replace it and we provide a sacrament argument so it's new old and then we don't need to do anything with update yet well we will when we do properties and children so I'm going to go ahead and compile this and then there really shouldn't be any change on the page yet because you know every time it's just the UL so you can see my properties aren't updated yet and I don't have any children so we need to work on both of those things next so one function that we also need to write is changed this will come into effect a little bit later but basically if we've gone from like you know an object to a string or a string to an object or if you know they're both strings and just remember and always takes precedence over or so this would all evaluate is like one block and then the last thing is just literally comparing the types that I've assigned so if we go from like you know a ul to like a div tag then we would need to do that the changed operation so that we would replace that node will just compile it real quick just to make sure my codes still good all right so diffing killed Rijn so again i'm not really good at long functions so I'm just gonna farm that out to a second function and again we go new then old and we're gonna end up with a list of patches so that's gonna be our end result and then with all these operations we're going to essentially want the worst case scenario so we want the most number of children regardless if that's the new state or the old state and now I'm going to use a good old for loop I know they're a little out of fashion these days but sometimes you know they can be pretty convenient for certain things and I'm just gonna set it you could also push if you prefer push and then I'm gonna essentially do a diff on each of these child nodes so this ends up being recursive right because diff would called if children and then on each child you would end up calling diff so it ends up just kind of walking that whole tree so I'm gonna go ahead and run that and then hopefully we'll start to see a little bit of a change here so now you can see as expected the number of children is going up each time and then each child is basically its own you know patch basically so now we need to actually apply those changes so I'm going to go ahead and grab that out of my patch and again with my good old for loop friend and this time I'm just gonna do straight-up recursion so you can see it's it's going to just go through each child and patch it and we're going to keep track of that index too so hopefully if this works which it doesn't be helpful if I could type will actually start to see some some children on the page here so cool you can see that it's adding a new one each time and then the the contents is changing and then if you also look in here while this is running you'll see that it's actually doing this pretty efficiently so it's only updating the parts that need to be updated each time so I promise that we were in do tags children and then properties so properties is the last thing that we have to diff and patch and then we're done so I know sometimes I can get kind of antsy too so I promise that we're getting near the end here so if we go back to our diff again I'm just gonna farm out all the changes to my properties and again we're just gonna have a list of patches so we're essentially going to have a patch for each property and then I'm gonna do something a little funky but basically I'm just trying to get all the different keys that could be in play again getting that worst case scenario so this isn't the object design isn't that useful I'm really just doing it to get all the different keys that could come into play and then I'm actually going to loop over every key and then the values really come into play quite a bit here so I'm gonna grab both of those and then if we don't have a new value then I need to remove that property and then if I don't have a previous property or if it's just that that value has changed then I'm just gonna set it so these properties don't really have like a way to diff them you just replace the old one each time and that's really all there is to diffing on the properties and I'll go ahead and show that so now you can see here we've got props added and then each time it's telling it to hey set the property at this name to this value so when we actually do our patch will actually start to see the background color change here because this number will start updating so for patching the properties again I don't really like long functions so I'm going to farm that out to a separate function and then we're just gonna iterate essentially over all of the different patches that have come in for the properties again using my old-school for loop and I'm just gonna grab out that name type and value that we had set up above and then pretty straightforward here nothing surprising if it says hey to set the property then we're just going to well set that property the other case is that if it's you know to remove that property then we're just going to remove the property and remove property is pretty much identical to our set property function the only difference here is that instead of set attribute will use remove and we don't need the value to do that now you'll notice that we're not really using the value argument in this function if you wanted to do form elements you would likely want this value property of particularly for like boolean properties so it would be useful if you were to do forms or potentially on SVG elements too so I'm going to go ahead and compile that and now hopefully we'll start to see some color changes in here so now you can see that each time my class property is updating and it's doing what we expect it's cycling through 0 1 & 2 and you can see the background color changing each time so that's really all there is to it really not that difficult totally accessible and I definitely hope that you know this has got you a little bit more curious about some of the tools that you're using on the front end and thank you for watching this video
Info
Channel: Kevin Heis
Views: 14,070
Rating: 4.9586563 out of 5
Keywords:
Id: l2Tu0NqH0qU
Channel Id: undefined
Length: 38min 57sec (2337 seconds)
Published: Sun Mar 12 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.