diff --git a/high-performance-react.org b/high-performance-react.org index 5e1ac60..7d5382d 100644 --- a/high-performance-react.org +++ b/high-performance-react.org @@ -1,3 +1,16 @@ +#+BEGIN_SRC emacs-lisp :exports results :results silent + (require 'ox-latex) + (add-to-list 'org-latex-packages-alist '("" "minted")) + (setq org-latex-listings 'minted) + (setq org-latex-pdf-process + '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f")) +#+END_SRC + +# #+latex_class: article +# #+latex_class_options: [a4paper,8pt] +# #+latex_header: \usepackage[a4paper,top=2.5cm,bottom=2.5cm,left=1.5cm,right=1.5cm]{geometry} +# #+latex_header:\renewcommand{\baselinestretch}{1.2} + #+TITLE: High-Performance React #+AUTHOR: Thomas Hintz @@ -87,7 +100,7 @@ Before baking is finished bread is a living organism. The way it grows and develops and flavors depend on what you feed it and how you feed it and massage it, care for it. If you have it grow and ferment at a higher temperature and more yeast it overdevelops producing too much -alcohol. If you give it too much time acidity will take over the +alcohol. If you give it too much time, acidity will take over the flavor. The recipes I used initially were missing a critical ingredient: the rising temperature. @@ -101,13 +114,14 @@ the other ingredients to complement the temperature. Now the bread can tell me what to do. While React isn't technically a living organism that can tell us what -to do it is, in its whole, a complex, abstract entity. We could learn basic -recipes for how to write high-performance React code but they wouldn't -apply in all cases and as React and things under it change our recipes -would fall out-of-date. So like the bread, to produce consistently -good results we need to understand how React does what it does. +to do, it is, in its whole, a complex, abstract entity. We could learn +basic recipes for how to write high-performance React code but they +wouldn't apply in all cases, and as React and things under it change +our recipes would fall out-of-date. So like the bread, to produce +consistently good results we need to understand how React does what it +does. -** React, made of +** Components of React Conceptually React is very simple. It starts by walking a tree of components and building up a tree of their output. Then it compares @@ -128,41 +142,43 @@ questions with solid answers. React is centered around the ~render~ method. The ~render~ method is what walks our trees, diffs them with the browser's DOM tree, and updates the DOM as needed. But before we can look at the ~render~ -method me have to understand its input. The input comes from +method we have to understand its input. The input comes from ~createElement~. While ~createElement~ itself is unlikely to be a -bottleneck it's a good to understand how it works so that we can have -a complete picture of the entire process. The more black-boxes we have +bottleneck it's good to understand how it works so that we can have a +complete picture of the entire process. The more black-boxes we have in our mental model the harder it will be for us to diagnose performance problems. -** Markup in Javascript: ~JSX~ +** Markup in JavaScript: ~JSX~ ~createElement~, however, takes as input something that is probably not familiar to us since we usually work in JSX, which is the last element of the chain in this puzzle and the first step in solving -it. While not strictly a part of React it is almost universally used +it. While not strictly a part of React, it is almost universally used with it. And if we understand it then ~createElement~ will be less of a mystery since we will be able to connect all the dots. -JSX is not valid HTML or Javascript but its own language compiled by a +JSX is not valid HTML or JavaScript but its own language compiled by a compiler, like Babel. The output of that compilation is valid -Javascript that represents the original markup. +JavaScript that represents the original markup. -Before JSX the normal way of injecting HTML into the DOM was via -directly utilizing the browser's DOM APIs. This was very cumbersome. -The code's structure did not match the structure of the HTML that it -output which made it hard to quickly understand what the output of -a piece of code would be. So naturally programmers have been endlessly -searching for better ways to mix HTML with Javascript. +Before JSX or similar compilers, the normal way of injecting HTML into +the DOM was via directly utilizing the browser's DOM APIs or by +setting ~innerHTML~. This was very cumbersome. The code's structure +did not match the structure of the HTML that it output which made it +hard to quickly understand what the output of a piece of code would +be. So naturally programmers have been endlessly searching for better +ways to mix HTML with JavaScript. And this brings us to JSX. It is nothing new; nothing complicated. Forms of it have been made and used long before React adopted it. Now let's see if we can discover JSX for ourselves. -To start with we need to create a data structure that both represents -a DOM tree and can also be used to insert one into the browser's -DOM. And to do that we need to understand what a tree of DOM nodes is -constructed of. What parts do you see here? +To start with, we need to create a data-structure -- let's call it +JavaScript Markup (JSM) -- that both represents a DOM tree and can +also be used to insert one into the browser's DOM. And to do that we +need to understand what a tree of DOM nodes is constructed of. What +parts do you see here? #+BEGIN_SRC html
@@ -174,26 +190,21 @@ constructed of. What parts do you see here? I see three parts: the name of the tag, the tag's properties, and its children. -|----------+-----------------------------| -| name | 'div', 'h1', 'input' | -| props | 'class', 'type', 'disabled' | -| children |

, , Hello | - -#+BEGIN_SRC -tag name: 'div' -tag prop: 'class' -children: h1..., 'Hello', input... -#+END_SRC +|-----------+-----------------------------| +| Name: | 'div', 'h1', 'input' | +| Props: | 'class', 'type', 'disabled' | +| Children: |

, , Hello | -Now how could we recreate that in Javascript? +Now how could we recreate that in JavaScript? -In Javascript we store lists of things in arrays and key/value -properties in objects. Luckily for us Javascript even gives us literal +In JavaScript, we store lists of things in arrays, and key/value +properties in objects. Luckily for us, JavaScript even gives us literal syntax for both so we can easily make a compact DOM tree with our own notation. This is what I'm thinking: +#+CAPTION: JSM - JavaScript Markup #+BEGIN_SRC javascript ['div', { 'className': 'header' }, [['h1', {}, ['Hello']], @@ -202,11 +213,11 @@ This is what I'm thinking: ] #+END_SRC -As you can see we have a clear mapping from our notation to the -original HTML. Our tree is made up of three element arrays. The first -item in the array is the tag, the second is an object containing the -tag's properties, and the third is an array of its children; which are -all made up of the same three element arrays. +As you can see in, we have a clear mapping from our notation, JSM, to +the original HTML. Our tree is made up of three element arrays. The +first item in the array is the tag, the second is an object containing +the tag's properties, and the third is an array of its children; which +are all made up of the same three element arrays. The truth is though, if you stare at it long enough, although the mapping is clear, how much fun would it be to read and write that on a @@ -224,7 +235,7 @@ And this is where our object of study enters the scene. JSX is just a notation that a compiler takes as input and outputs in its place a tree of nodes nearly identical to the notation we came up with! And if you look back to our notation you can see that you can easily embed -arbitrary Javascript expressions wherever you want in a node. As you +arbitrary JavaScript expressions wherever you want in a node. As you may have realized, that's exactly what the JSX compiler does when it sees curly braces! @@ -232,7 +243,7 @@ There are three main differences between our data structure and the real one that the JSX compiler outputs: it uses objects instead of arrays, it inserts calls to React.createElement on children, and spreads the children instead of containing them in an array. Here is -what "real" JSX compiler output looks like: +what real JSX compiler output looks like: # #+NAME: foo # #+CAPTION: foo bar @@ -248,10 +259,10 @@ React.createElement( ); #+END_SRC -As you can see it is very similar to our data-structure and for the -purposes of this book we will use our own simplified data-structure as -it's a bit easier to work with. A JSX compiler also does some -validation and escapes input to prevent cross-site scripting +As you can see, it is very similar to our markup data-structure and +for the purposes of this book we will use our own simplified +data-structure as it's a bit easier to work with. A JSX compiler also +does some validation and escapes input to prevent cross-site scripting attacks. In practice though they would behave the same in the ways that matter to us now. @@ -266,7 +277,7 @@ achieve that objective. ~createElement~ will take as input our JSX-like notation and output a tree of objects compatible with ~render~. -React expects nodes defined as Javascript objects that look like this: +React expects nodes defined as JavaScript objects that look like this: #+BEGIN_SRC javascript { diff --git a/manuscript/fundamentals--building-our-own-react.markua b/manuscript/fundamentals--building-our-own-react.markua index 38fd955..54bbb65 100644 --- a/manuscript/fundamentals--building-our-own-react.markua +++ b/manuscript/fundamentals--building-our-own-react.markua @@ -4,31 +4,31 @@ Baking bread. When I first began to learn how to bake bread the recipe told me w Understanding: that's what I was missing. The bread I make is now consistently good. The recipes I use are simpler and only give ratios and general recommendations for rests and waits. So why does the bread turn out better? -Before baking is finished bread is a living organism. The way it grows and develops and flavors depend on what you feed it and how you feed it and massage it, care for it. If you have it grow and ferment at a higher temperature and more yeast it overdevelops producing too much alcohol. If you give it too much time acidity will take over the flavor. The recipes I used initially were missing a critical ingredient: the rising temperature. +Before baking is finished bread is a living organism. The way it grows and develops and flavors depend on what you feed it and how you feed it and massage it, care for it. If you have it grow and ferment at a higher temperature and more yeast it overdevelops producing too much alcohol. If you give it too much time, acidity will take over the flavor. The recipes I used initially were missing a critical ingredient: the rising temperature. But unlike a lot of ingredients: temperature is hard to control for the home cook. So the recipe can't just tell you exactly what temperature to grow the bread at. My initial recipes just silently made assumptions for the temperature, which rarely turn out to be true. This means that the only way to consistently make good bread is to have an understanding of how bread develops so that you can adjust the other ingredients to complement the temperature. Now the bread can tell me what to do. -While React isn't technically a living organism that can tell us what to do it is, in its whole, a complex, abstract entity. We could learn basic recipes for how to write high-performance React code but they wouldn't apply in all cases and as React and things under it change our recipes would fall out-of-date. So like the bread, to produce consistently good results we need to understand how React does what it does. +While React isn't technically a living organism that can tell us what to do, it is, in its whole, a complex, abstract entity. We could learn basic recipes for how to write high-performance React code but they wouldn't apply in all cases, and as React and things under it change our recipes would fall out-of-date. So like the bread, to produce consistently good results we need to understand how React does what it does. -## React, made of +## Components of React Conceptually React is very simple. It starts by walking a tree of components and building up a tree of their output. Then it compares that tree to the tree currently in the browser's DOM to find any differences between them. When it finds differences it updates the browser's DOM to match its internal tree. But what does that actually look like? If your app is janky does that explanation point you towards what is wrong? No. It might make you wonder if maybe it is too expensive to re-render the tree or if maybe the diffing React does is slow but you won't really know. When I was initially testing out different bread recipes I had guesses at why it wasn't working but I didn't really figure it out until I had a deeper understanding of how making bread worked. It's time we build up our understanding of how React works so that we can start to answer our questions with solid answers. -React is centered around the `render` method. The `render` method is what walks our trees, diffs them with the browser's DOM tree, and updates the DOM as needed. But before we can look at the `render` method me have to understand its input. The input comes from `createElement`. While `createElement` itself is unlikely to be a bottleneck it's a good to understand how it works so that we can have a complete picture of the entire process. The more black-boxes we have in our mental model the harder it will be for us to diagnose performance problems. +React is centered around the `render` method. The `render` method is what walks our trees, diffs them with the browser's DOM tree, and updates the DOM as needed. But before we can look at the `render` method we have to understand its input. The input comes from `createElement`. While `createElement` itself is unlikely to be a bottleneck it's good to understand how it works so that we can have a complete picture of the entire process. The more black-boxes we have in our mental model the harder it will be for us to diagnose performance problems. -## Markup in Javascript: `JSX` +## Markup in JavaScript: `JSX` -`createElement`, however, takes as input something that is probably not familiar to us since we usually work in JSX, which is the last element of the chain in this puzzle and the first step in solving it. While not strictly a part of React it is almost universally used with it. And if we understand it then `createElement` will be less of a mystery since we will be able to connect all the dots. +`createElement`, however, takes as input something that is probably not familiar to us since we usually work in JSX, which is the last element of the chain in this puzzle and the first step in solving it. While not strictly a part of React, it is almost universally used with it. And if we understand it then `createElement` will be less of a mystery since we will be able to connect all the dots. -JSX is not valid HTML or Javascript but its own language compiled by a compiler, like Babel. The output of that compilation is valid Javascript that represents the original markup. +JSX is not valid HTML or JavaScript but its own language compiled by a compiler, like Babel. The output of that compilation is valid JavaScript that represents the original markup. -Before JSX the normal way of injecting HTML into the DOM was via directly utilizing the browser's DOM APIs. This was very cumbersome. The code's structure did not match the structure of the HTML that it output which made it hard to quickly understand what the output of a piece of code would be. So naturally programmers have been endlessly searching for better ways to mix HTML with Javascript. +Before JSX or similar compilers, the normal way of injecting HTML into the DOM was via directly utilizing the browser's DOM APIs or by setting `innerHTML`. This was very cumbersome. The code's structure did not match the structure of the HTML that it output which made it hard to quickly understand what the output of a piece of code would be. So naturally programmers have been endlessly searching for better ways to mix HTML with JavaScript. And this brings us to JSX. It is nothing new; nothing complicated. Forms of it have been made and used long before React adopted it. Now let's see if we can discover JSX for ourselves. -To start with we need to create a data structure that both represents a DOM tree and can also be used to insert one into the browser's DOM. And to do that we need to understand what a tree of DOM nodes is constructed of. What parts do you see here? +To start with, we need to create a data-structure -- let's call it JavaScript Markup (JSM) -- that both represents a DOM tree and can also be used to insert one into the browser's DOM. And to do that we need to understand what a tree of DOM nodes is constructed of. What parts do you see here? {format: "html"} ``` @@ -40,23 +40,17 @@ To start with we need to create a data structure that both represents a DOM tree I see three parts: the name of the tag, the tag's properties, and its children. -| name | 'div', 'h1', 'input' | -| props | 'class', 'type', 'disabled' | -| children |

, , Hello | +| Name: | 'div', 'h1', 'input' | +| Props: | 'class', 'type', 'disabled' | +| Children: |

, , Hello | -``` -tag name: 'div' -tag prop: 'class' -children: h1..., 'Hello', input... -``` +Now how could we recreate that in JavaScript? -Now how could we recreate that in Javascript? - -In Javascript we store lists of things in arrays and key/value properties in objects. Luckily for us Javascript even gives us literal syntax for both so we can easily make a compact DOM tree with our own notation. +In JavaScript, we store lists of things in arrays, and key/value properties in objects. Luckily for us, JavaScript even gives us literal syntax for both so we can easily make a compact DOM tree with our own notation. This is what I'm thinking: -{format: "javascript"} +{format: "javascript", caption: "JSM - JavaScript Markup"} ``` ['div', { 'className': 'header' }, [['h1', {}, ['Hello']], @@ -65,15 +59,15 @@ This is what I'm thinking: ] ``` -As you can see we have a clear mapping from our notation to the original HTML. Our tree is made up of three element arrays. The first item in the array is the tag, the second is an object containing the tag's properties, and the third is an array of its children; which are all made up of the same three element arrays. +As you can see in, we have a clear mapping from our notation, JSM, to the original HTML. Our tree is made up of three element arrays. The first item in the array is the tag, the second is an object containing the tag's properties, and the third is an array of its children; which are all made up of the same three element arrays. The truth is though, if you stare at it long enough, although the mapping is clear, how much fun would it be to read and write that on a consistent basis? I can assure you, it is rather not fun. But it has the advantage of being easy to insert into the DOM. All you need to do is write a simple recursive function that ingests our data structure and updates the DOM accordingly. We will get back to this. So now we have a way to represent a tree of nodes and we (theoretically) have a way to get those nodes into the DOM. But if we are being honest with ourselves, while functional, it isn't a pretty notation nor easy to work with. -And this is where our object of study enters the scene. JSX is just a notation that a compiler takes as input and outputs in its place a tree of nodes nearly identical to the notation we came up with! And if you look back to our notation you can see that you can easily embed arbitrary Javascript expressions wherever you want in a node. As you may have realized, that's exactly what the JSX compiler does when it sees curly braces! +And this is where our object of study enters the scene. JSX is just a notation that a compiler takes as input and outputs in its place a tree of nodes nearly identical to the notation we came up with! And if you look back to our notation you can see that you can easily embed arbitrary JavaScript expressions wherever you want in a node. As you may have realized, that's exactly what the JSX compiler does when it sees curly braces! -There are three main differences between our data structure and the real one that the JSX compiler outputs: it uses objects instead of arrays, it inserts calls to React.createElement on children, and spreads the children instead of containing them in an array. Here is what "real" JSX compiler output looks like: +There are three main differences between our data structure and the real one that the JSX compiler outputs: it uses objects instead of arrays, it inserts calls to React.createElement on children, and spreads the children instead of containing them in an array. Here is what real JSX compiler output looks like: {format: "javascript"} ``` @@ -87,7 +81,7 @@ React.createElement( ); ``` -As you can see it is very similar to our data-structure and for the purposes of this book we will use our own simplified data-structure as it's a bit easier to work with. A JSX compiler also does some validation and escapes input to prevent cross-site scripting attacks. In practice though they would behave the same in the ways that matter to us now. +As you can see, it is very similar to our markup data-structure and for the purposes of this book we will use our own simplified data-structure as it's a bit easier to work with. A JSX compiler also does some validation and escapes input to prevent cross-site scripting attacks. In practice though they would behave the same in the ways that matter to us now. So now that we've worked through JSX we're ready to tackle `createElement`, the next item on our way to building our own React. @@ -95,7 +89,7 @@ So now that we've worked through JSX we're ready to tackle `createElement`, the React's `render` expects to consume a tree of element objects in a specific, uniform format. `createElement` is the method by which we achieve that objective. `createElement` will take as input our JSX-like notation and output a tree of objects compatible with `render`. -React expects nodes defined as Javascript objects that look like this: +React expects nodes defined as JavaScript objects that look like this: {format: "javascript"} ```