Understanding JSX Compilation in VueJS

In the JavaScript World, you may be seeing a lot of frameworks coming in, each of them has their own syntax in building web applications which some of the browser vendors have not implemented yet.

Still, the code that we write using these frameworks works fine. It all happens through parsing of our code and generating render functions which are served to virtual DOM libraries like snabbdom.

During the generation of render function there are three important phases

  • Parse
  • Transform
  • Generate
JSX to Render Function

Before understanding how these three phases really work, we need to understand why AST is required? & What is AST?.

Why AST?

Generally, when we write JavaScript programs, for example, we use import statements for modularizing our code to make our programs more maintainable.

However, this is not the case with computers. For them, each of these characters is just a numeric value in memory. Hence we need to find a way to turn our code into something that computers can understand.

What is AST?

AST Stands for Abstract Syntax tree which is a tree representation of the abstract syntactic structure of the source code written in a programming language. Abstract syntax trees are data structures widely used in compilers to represent the structure of program code.

Parse:

The parsing stage takes the code as an input and outputs an AST.

There are two phases of parsing in Babel:

  • Lexical Analysis
  • Syntactic Analysis

Lexical Analysis takes a string of code and turns into a stream of tokens.

Syntactic Analysis will take a stream of tokens and turn it into an AST representation.

Transform:

In transform phase, we need to traverse the AST recursively to add, update or remove a node in the tree.

Generate:

It takes the transformed AST and generates the render function. You traverse through the AST depth-first, building a string that represents the transformed code.

Compilation of JSX code in to render function:

let us understand how the below snippet of code compiles.

How the compilation happens in each and every phase.

JSX syntax in Vue

Babel is the one which compiles the above code, babel-transform-vue-jsx is the plugin which is used in the creation of render functions.

Parse:

Babylon is the parser which takes the JavaScript code and turns into AST.

In Lexical Analysis Phase for each and every character that we write in the code, there will be a token getting generated.

Let’s have a look at the token that is generated for span tag.

{
type: {
label: 'jsxName',
keyword: undefined,
beforeExpr: false,
startsExpr: false,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null
},
value : 'span'
}

If you see in the above token the type of the label is ‘jsxName’, this label name varies from token to token. Suppose if it is text node then the type of the label in the token would be ‘jsxText’.

Now Syntactic Analyser will take these stream of tokens generated in lexical analysis and turn them into an AST representation. Using the information in the tokens, this phase will reformat them as an AST.

At this point, you may be thinking why is the transform phase required if we have AST. Don’t worry read the below few lines and it will be clear.

Transform:

Do you remember that I said babel-transform-vue-jsx is the plugin used in this compilation process?

Now let us see when this plugin comes in to picture and what is the difference in the AST generated after parsing phase and transform phase.

Before Transform phase AST for the span tag looks like as below.

{
type : 'JSXElement',
openingElement : {..},
closingElement : {...},
children : [{
type : 'JSXText',
value : 'Hello'
}]
}

parsing phase cannot create proper AST for the HTML syntax that we pass as an argument to return statement in JSX. if you see the above AST this is not in a proper format that can be used to generate render function.

Babel-transform-vue-jsx is the plugin which does this job for us. After transform phase AST for the HTML that we wrote will be :

{
type : 'Call Expression',
arguments : [{
type: "StringLiteral", value: "div"
},{
...
type : 'ObjectProperty',
key : {
name : 'attrs',
...
}
},{
...
[
{
type : 'StringLiteral',
value : 'span'
},{
type : 'StringLiteral',
value : 'Hello'
}
]
}]
}

Let me give some more information on how the hierarchy of the nodes will be for the above snippet of code.

Creation of Nodes in AST happens with respect to the type of Node. For example, if it comes across something like import statement then it will create a node of type import declaration.

The Program is the top level node while creating AST for JSX, the constructor function which is of type expression statement will be the child to program node which is taking an object as an argument which has again two property nodes.
First property node “el” is of type string literal, Second property render is of type Function Expression, inside the body of the function again there is a node which is of type return statement which also takes an argument, it has sub-node which is of type Array Expression which will contain the elements that are inside the main div.
Since there is only one element inside div, first child of div tag will be span tag which is a node of type Array Expression since there is a text node inside the span tag. This text node is of type string literal. This is how AST will be created in case of JSX

Generate:

Code Generation is the process in which we convert some intermediate representation of code into the one that can be readily executed by a machine.

It takes the final AST and turns it back into a string of code in our case this string of code is nothing but render function.

Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.

In this phase first, it creates a buffer which contains all the strings that need to be combined to generate render function. This render function is then given to snabbdom to create virtual DOM.

Let us have a look at the render function.

Render Function

References:


Simultaneously published at blog.imaginea.com