How Angular works | Kara Erickson | #AngularConnect

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Applause] hello everyone welcome to how angular works I guess I don't need as much of introductions I just got such a such a great one but I'm Cara Ericsson I'm the tech lead for anglers framework team and as she said I you know I work day to day on the Ivy runtime so I spend a lot of time thinking about how angular works and that's what we're talking about today so when you're building an application with the framework it can sometimes be kind of hard to imagine what the framework is doing under the hood but by the end of this talk I want you to be able to visualize kind of how your application code and the angular framework code are working together so just to jump right in angular broadly has two parts there's the angular compiler which is our build time utility and the angular runtime and that's the framework code that ships with your application and actually runs in the browser today we're going to focus on the runtime portion but if you're interested in how the compiler works I'd highly recommend checking out Alex's talk from yesterday it should be on YouTube soon and the slides are also linked in my presentation but for our purposes today all you need to know about the angular compiler is that it in addition to down leveling your typescript to JavaScript it also parses your angular decorators and your templates and it generates some code that the runtime can understand so if we take a look at this component example we have an act upon a decorator like you'd write in your application what the compiler would do is it would take this information and it turns it into something that we call a component definition or a component def and this contains all the same information but just transformed into something that the runtime can actually use so one way to think about it would be that the compiler generates you know these definitions and these instructions and the runtime actually implements those instructions to actually run as your application is running so this definition probably looks kind of intense right now there's a lot of stuff going on but don't worry about understanding what's in it right the second and throughout the course the talk I'll be explaining what these instructions are and how they work so hopefully by the end of this talk you can read these with confidence it's happiness okay so when an application bootstraps there's three main phases that it goes through there's module setup and this is where your or the framework is instantiating your modules it's setting up the module injector there is view creation when we're actually creating the Dom for your entire application instantiating directives that kind of thing and then there's a change detection and this is when we're checking your binding values and updating them if necessary the first part is really the module bootstrap part of application bootstrap and the second two parts are more related to the component tree and today we're going to talk only about the component bootstrap portion just for time purposes but know as answer questions about module bootstrap after so in the course of you know explaining how all this works we have an example application that we're gonna be going through and this is my cat's personal website or what I imagine it would look like and so it has you know an image of my cat on the Left there's a header there's an info card on the right that has all his information and then there's a footer component that's actually you know scrolled beneath this viewport that's his contact information so that's the app that we'll be using and you could imagine it's code might look something like this it has an app module it has a few components there's one for the you know the route component there's an info card component and then there's a footer component and then in the route component template you can see you know some of the things that I just showed you right there's the image of the cat there's the header there's info cards and there's the footer so when we bootstrap this application we're gonna start with whatever component you put in the bootstrap property of your ng module so the framework will start looking at app component and there's a few special things that we have to do at the very beginning to set up your root component the first thing that we need to do is locate your root element so when you wrote your index.html you added some tag right like a fruit and then it should match whatever selector that you have you component so the framework needs to figure out where this element is so it can you know bootstrap your angular application inside it and append all the elements to it so when you compile this component the selector gets compiled into the definition so we still have that information so the framework just takes this selector from your component definition and you know document query selector that selector and that's how we can locate through elements that's pretty straightforward next we want to actually create an instance of your root component and again this is something that the component definition makes pretty simple the you know the compiler generates a factory function that tells us how to correctly create this component with any dependencies that it may have so in this case there's no dependencies so it's pretty straightforward just doing it but we just call the factory function to get that started and then we're ready to actually start rendering the route component this is where it starts to get interesting so view creations that we're starting the process of you know creating the Dom and Stan shooting directives and this is where the template function comes in so you'll recall that your HTML template is turned into a function so all you need to do or all the framework needs to do is invoke this template function and give it a particular mode so in this case creation mode and as soon as that function is invoked it will create the Dom correctly for that component so if you imagine this as your component tree for our application right we have you know the app the info card and footer we can invoke the template function in creation mode to create the app component then we can invoke the template function for the info card to create the info card component and then do the same for the footer so really if we want to create an entire application or the Dom for an entire application all we have to do is call all of these template functions in our component tree so what is a template function atop the function is a function that is composed of a number of instructions and when I say instruction what I mean is it's like an angular it's a function that is implemented by the angular framework that does some specialized piece of work so you can roughly divided them into two camps there is creation instructions which you know you may have guessed it creates they create things and usually it's indicated by the name so the element instruction creates an element toxin instruction creates a text node the projection instruction creates an energy content you can kind of guess where this is going and the update instructions will take an existing you know element or component or something and it will update it so you know the property instruction updates a property of the attribute instruction updates an attribute once again it's pretty pretty self-explanatory in the name and what instructions are actually generated in your template function depend on what you've actually put in your HTML template that you've written so if we go to the app components template here that we were just looking at the compiler would generate something like this this equivalent template function and right now we're just kind of focused on the top part right because we're in creation mode so let's start by explaining how all of this works so the first thing I want to draw your attention to is that the structure of your template function is really mirroring the structure of your HTML template you see that we have an image tag on the right has the source attribute and then in the element instruction we're passing in that tag name for image we're passing in the source attribute so they're really similar and we do that on purpose that it's easy for us to create the exact Dom that you wrote in your template because we're not actually just taking the you know the template strengths not HTML we're not just inner HTML on it we're actually creating all of these elements one by one which is about these instructions do so if we took a closer look at the element in structure an instruction we would take the tag name that you give us and we use really recognizable Dom API is that you've probably used before like document.createelement passing the tag name get whatever parent is the right one for right now which would be the root element in this case set any hash we see set and then just append the element that we just created to its parent so it's all much more straightforward than I think you might expect from I'm what's inside this function we also do one more thing and that's we track all of the elements as we're creating them so we store them in our own internal data array and the reason that we do this is because later in change detection we're going to be updating these elements right and so we're going to need some way to get access to them so we could use Dom API is right like query selector to get a hold of them but that can be pretty expensive and pretty slow so instead we have our own way of keeping track of these elements that it's fast for us to access them later the internal data array where we store these elements is something that we call an L view it's also known as a logical view and we create an L view instance for every template instance or every component instance so every component has its own you know set of storage that has its Dom elements inside it so in addition to the Dom elements we also put things like binding values in here so we can check old binding values against new binding values we also put directive instances in here so that we can set inputs on them later so yeah every component has its own you know source of data and so for creation mode what we're doing is we're you know going through and we're adding each element as you create it into this array so if we go back to our template and we imagine that we're you know we're executing it line by line we'll create this image tag we append it in the correct place and then we'll store it in our L view here on the bottom right then we'll create the h1 will append it will store it in our L view and we'll do the same for the info card and the footer there's one more thing that these element and text or really just element instructions are doing and that's directive matching so at compile time the compiler will analyze your ng modules and your component decorator and it'll figure out based on your ng modules what directives and components are available to your component in its you know in its module scope and once it knows what all those directives are it'll print out that list of directives inside the component definition so here for the app component it has access to the info card directive components and the footer component and this makes it much easier for us because at runtime the runtime doesn't have to worry about you know module scope or any of that stuff we know what our list of available directives is from the component definition so if I put the registry up here on the right so you can kind of visualize it as we're creating all of these elements we're also matching directives against the elements that we're creating so for this image tag will we'll look at the registry look at the first item which is info card we'll grab its definition look at its selector right and then we'll try to see if the image tag matches the info card selector they don't match so we'll keep looking checking to see if the image tag matches the footer selector still won't match and we'll keep going through as we're creating until we actually get a match and at this point we need to actually instantiate the you know the directive or component that we found so your first thought might be oh you know the framework should just call new one whatever type it is and then you're done but that doesn't really take into account dependency injection any of these components or directives could have any number of parameters that could be anywhere in the tree and so what we do is we instead call the components factory this is the same factory function that we you know invoked at the very beginning so if you look at what info cards factory function would look like it'll call nuan info card as you might expect but it also has this this directive inject instruction and this is the instruction that powers dependency injection so this is the thing that's actually gonna go get this info token that you that you've asked for and you know dependency injection is kind of its own talk but the gist of it is that the directive inject instruction will take the token and it will first check the directive injector tree the directive injector is where we keep track of all of those providers that you added on directive definitions and component definitions as well as the directives and components themselves so every time you have a node that has directives on it we create a directive injector so it becomes this you know this big tree of directive injectors as you walk up kind of Saddam so we'll look at you know the directive injector for the node that's closest to where we're requesting it and then we'll keep walking up all the way to route and if it's not there then we fall back oops we fall back to the module injector module injector is where we keep track of providers that you set on energy modules if we don't find it in the module injector then you know you're out of luck and we'll return it all or something depending on if it's optional okay so for this info service provider in our example app let's say that we provided it on the app module so when we're creating the info card you know we call directive injects and directive inject will first check the node that we're currently on so it'll check the info card node you know it'll try to say okay is there a directive injector here yes or no is there and is there a directive that's registering info service as a provider no there isn't so then we'll move up to the next parent element that has a directive injector which is going to be the app root element but which the app component is on so the app component provide info service no it didn't so that's when we fall back to the module injector take a look to see if it's been provided and it has sort of very happy and we can correctly create the info card directive with the info card service or I think it's just called info so as soon as we create the info card instance we're gonna want to save it in that same L view array and like I mentioned earlier we need to keep track of directive instances that we can set inputs on them later and also so that we can call lifecycle hooks with the contacts there's a lot of things we do with directives so we have to keep track of them then of course since you can compose multiple directives on the same node we'll also check the rest of the directives in the registry after that as well so match the footer later and you know add it to the same array so one thing that you might be thinking is this directive matching process seems super expensive you know your directive registry is probably more than two directives right it's normally you know most of common module right you've got n GF energy four you have all your imported modules you might Tyrael and that's very true and so directed matching is not something that we want to do more than once per component we want to do it the very first time we see a component and then we never want to do it again so how do we make that work well essentially we make that work through shared data structures so I've already mentioned the l view and how we create one per component instance we also have a T view or a template view and we create one of these per template function or per component type but I won't think about it so for example if you have a component that has ten instances you would create ten l views and one t view and all of the instances would share the T view and the way that we make this work is that we store the T view on the component def so the next time we create this component instance we can look at the def lookup the T view and look at all the information that we stored the first time around and make use of that so in practice it might you know be something like this we have all of our elements in all of you we have our directive instances and as we're pushing directive instances into the L view we're also pushing their definitions into the T view at the same index similarly as we're creating Dom elements we're also pushing template nodes into the T view at the same index and template nodes or T nodes are essentially just kind of metadata about each Dom element so it'll create contain information like the tag name or more importantly in this case information about directive matching so on the T node we'll have a list of directive indices or a range of directive indices that matched this node so for info card for example the second time we go through this app component template we'll get to the info card element we'll check to see if there's a T note at that instance that we already created the T node will have information about where the directives are stored for this node and it'll point to the definition and then we can just go ahead and instantiate it immediately without having to do any matching so it makes it really quick for subsequent component instances okay so I mean that pretty much takes us through view creation for a single component but as we mentioned we're going to be doing this for every single Capone so just imagine the same process for info card and footer which brings us to change detection change' tection once again is the process where we check all of our binding values and if they've changed we rerun through the view to reflect the updated values as you may have guessed it's a it's very similar to view creation and that we can just go ahead and invoke all the template functions down the component tree but instead of using the creation flag this time we can just use the update flag so if you call all these template functions top to bottom then that's change detection essentially so now we're going to focus on this second half of the template function now that we've explained the first half and this is what we'll execute when you're in update mode so again the first thing I want to draw attention to is how similar these these two things are if you have your template on the right and your template function on the left there's you know the Hatter binding you can see that in both places and below that there's the name binding you kind of see see them one-to-one however we're no longer creating elements right so we're not necessarily gonna have a one-to-one mapping for every element there'll be some elements that have multiple bindings there'll be some elements that have no bindings so to keep track of which element were on when we're you know generating our binding instructions we also have these advanced instructions and these kind of tell us how to negotiate between nodes and bindings so this is much easier to visualize if you kind of can see the data structures so on the right here imagine this is the L view this is the array we're then storing our elements and these you know 0 through 6 or the in the index indexes or indices of the L view and we're gonna need to keep track of two things one is the current node so this is the node that we are currently on the node that we actually want to update the properties for and then there's the current binding the current binding keeps track of where we are in the L view so that it waits like that so basically we need to keep track of all of the old values of by that every time you run change detection we can tell whether it's changed right so the old binding values we will save in this section of the array after the Dom elements so we'll have kind of a counter that keeps track of where we are in that section of the of the array of mole as well and you'll see how that works in a second okay so let's say we execute the first advanced instruction all this does is tell us that the first binding is two nodes down so it'll say hey increment the current node counter until you get to the node that's two nodes below wherever I am so now we're on the text node then we run the text interpolate instruction and all this does basically is it checks the header variable that you passed in so context set header against whatever value you have at the current binding index which is right now it's empty so the first time we just assume that the values have changed so we're just going to go ahead and update that text binding in the DOM and we'll also cache the value for the next time that we run change detection and then we'll increment it so we don't keep overriding the same value so we're done with bindings on the current node right so we advanced one and that tells the framework that the next binding is on a node one slot down which takes us to the info card and then we execute the property binding instruction and that says hey for whatever node you're on right now you want to check the name property and you know use the value that I'm passing into the name value and again it's the first time so we assume it's changed so we'll want to update the binding value here but you'll notice that this is a directive input right this is not an element so we don't want to go and set the name property on the element we want to set it on the info card component but you'll remember that we have this beautiful mapping between a certain node and all the directives on the node so it's pretty simple for us to you know extrapolate which component this is and set the inputs on it directly from there so the next time that you go through change detection will be checking against these values that we just saved so whatever the new value of header is will be checking against Hermes the cat and then whatever the new value proper name is will be checking against Sherlock Holmes okay so one thing that you might have noticed is that change detection is going to be running within a view from top to bottom right because we're keeping the ordering as we you know mirror the template the component tree is check from top to bottom and all the nodes within a component view are also checks from top to bottom so it's pretty predictable so if you had this template you know with the div and the text binding you know look one two three four you can expect that it's just gonna be executed in the same order that you see one two three four that's the order like the bindings will actually be set before IV in view engined this was actually slightly different I don't know if you guys can guess what the order would be right now just think about it in your head okay is this what you thought three four one two okay so the reason for this is that pre IV we would do two separate passes we do one full pass for directive inputs and then another full pass for element and text bindings so in practice you know it makes sense from the implementation standpoint but from a user perspective that's super confusing right you wouldn't expect it to happen in a different order than you see so that's one thing that we've fixed Tenaya is we've made it a lot more predictable how your template function is actually going to run or how your templates going to be checked but one thing I want to call out is that while we would guarantee the binding order between nodes we don't guarantee binding order within a particular node and there's a really good reason for this and it's essentially to reserve the right to optimize bindings within within a node so for example let's say we have this component has a bunch of property bindings has a bunch of attributes we could just generate something like this in your template function right where we have an instruction for every binding in the same order that you wrote it just like as you might expect however it's pretty duplicate 'iv right you have two property instructions you have to attribute instructions is there any way that we could optimize this and we can using property chaining so if we can kind of fudge the order a little bit within a node and move them around we can take the property binding from below and we can chain it on the property binding at the top and we can do this because the property instruction returns itself so the function is returning an instance of itself and so we can keep chaining you know properties and attributes and save those extra function function names in the code size so this might seem like a micro optimization and it is but over large templates it actually makes a big difference so this is the kind of thing that we like to do okay okay so one thing I haven't talked about yet its lifecycle hooks you might be wondering when do these happen and they actually execute as part of change detection I just kind of omitted it for simplicity but now we're going to go back and kind of you know enrich what we had before so I you are probably familiar with lifecycle hooks but I'll do a quick review so the first three ng on changes ng on an init and ng you check these are all hooks that have to run after the inputs are set for a particular directive after unit and after view checks usually happen after the view or always have been after the view is checked and then after constant in it and after content checks happen after you know content children are checks but what you may not know is that they actually execute slightly differently so these first three hooks we execute node by node so we'll run them like one note at a time whereas the other ones we run in view by view so we'll run all of them at once for view instead of all of all of them at once for just a particular node and what that means is that all of the node by node ones that on change is the own in it and you check we have to actually run while we're executing the template function because that's when we're you know that's when we're actually evaluating the nodes so if we go back through this lovely process all the lifecycle hooks are being executed in this advanced instruction so in addition to changing the that you know the cursor for the current node we're also executing lifecycle hooks so here as soon as the advance instruction runs we know that we're done with all of the input all the bindings for that node right that's how we know that we can advance so for this one in advance we would say hey there's no more bindings for node 0 or node 1 that's why we're moving so we can execute all the lifecycle hooks for node 0 and 1 semi you know moved like move the cursor like we did before we check the binding for the text binding update the value like we did before and then we get to the next advanced instruction and at this point we're done with all the bindings for the text node obviously this doesn't work we're not gonna have any lifecycle hooks for the text node but if we if this had been like a normal element this is where we would you know flush any lifecycle hooks for that node so let me check the last instruction and then at the end there's no more advanced instructions because we're done right so after the template function is invoked we just execute any of the lifecycle hooks that remain for things that don't have bindings so after the template function runs we actually do a few more things one thing is that we refresh your embedded views these are all of your energy templates that are have been added through something like ng F for energy for I really wanted to talk about that today but I don't have time come find me then we flush all the content hooks so remember I said content hooks and view hooks happen node by net or happened view by view so this is where we flush all the content hooks at once for a particular view we set host bindings we check any of your child components once again we're calling a template function for any of the child components in the tree and afterwards is when we flush all of the view hooks and view hooks are bottom-up so we have to check the trunk opponents first so hopefully by now you have a better understanding of what you're seeing here right we've kind of gone through all of these instructions it's hopefully this isn't as scary I just I was like a quick review of what we've seen in view creation we created some elements we set up some attributes we didn't have any listeners with us where we would have registered them we match directives we instantiate of directives we perform Ti we created our child components in change detection we checked all of our binding as you called bicycle hooks but you're fresh and better views check their host bindings and we checked our child components so there's a lot going on but hopefully that's a little bit more digestible than it was before thanks [Applause] [Music] [Applause] [Music]
Info
Channel: AngularConnect
Views: 61,843
Rating: undefined out of 5
Keywords:
Id: S0o-4yc2n-8
Channel Id: undefined
Length: 29min 42sec (1782 seconds)
Published: Fri Sep 27 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.