Welcome to the JAM Stack with Angular and Scully by Natalia Venditto || Angular Conference
5K views
Nov 16, 2023
In this session of Angular Virtual Conference learn about Scully and Jam Stack with Angular from Natalia Venditto. C# Corner - Global Community for Software and Data Developers https://www.c-sharpcorner.com Conference Website: https://www.2020twenty.net/angular
View Video Transcript
0:00
Okay, so yes, I think there is nothing left for me to say to introduce myself because
0:08
that video that they put a lot of detail, a lot of attention
0:12
I thank you very much for having me here. Even though I'm on holidays, like Steven said, I think it's very important to share knowledge
0:22
and especially when we have new tools or new features in our Angular ecosystem
0:29
This is what I'm going to be talking to you about today
0:34
Usually when I speak about Scali connected to performance, I like to compare the metric result or results for static site rendering
0:44
which is what Scali does, with those of classic server rendering, right
0:49
Or client site rendering. That's what we have for Angular applications as default
0:55
I'm going to focus in the client-side rendering for now so we can explore a little bit the metrics we get
1:03
when it comes to performance. I don't know if every one of you is familiar with this
1:09
but if you're doing web development, for sure it's a good thing to be knowledgeable
1:16
on how performance is perceived by your users. So when we have a client-side..
1:23
I apologize. for the noise. When we have a client-side rendering strategy
1:33
what happens is that the client or the browser is super busy taking care of the business logic
1:40
of the routing, fetching of the data, fetching from APIs, doing all these logical operations
1:46
that it needs to finally render a page or a route, right
1:51
And because it's so busy with all this, Sometimes, and it happens at runtime, by the way
1:58
sometimes we get a fast first paint, we get a fast first contentful paint
2:03
I really advise you to go and learn what those metrics mean if you are not familiar with them
2:08
We also get a fast time to first bite or the first bite that gets ever painted on the page
2:15
But the time to interactive or the time when the applications becomes responsive
2:20
in the sense that it responds to user input, sometimes it gets really slow
2:29
And for me as a front-end architect, I am super concerned with performance, obviously
2:34
because how well our sites perform is crucial to guarantee return on investment
2:39
and fulfill our KPIs in the customer side and also our SLAs on us as providers of web services, right
2:51
So this is why I paid a lot of attention recently when Google advertised a new performance metrics
2:59
a new initiative they have called Core Web Vitals, consisting of LCP, I will tell you more about it
3:08
LCP, FIP, and CLS. And let's see what those acronyms mean. They mean LCP, the largest contentful paint
3:18
So it's when the largest piece of content above the fold gets finally rendered
3:25
and it's measured in seconds or milliseconds. And then we have the first input delay
3:32
which is also measured in time. It's when finally you can interact
3:40
with the server through this UI, Like you click on a button and then the browser finally gets some response or does something around it
3:48
Or maybe that happens entirely on the client. And finally, the cumulative layout shift
3:56
This is super important to us because in dynamic context, and that's mostly our Angular applications
4:03
things turn to shift around and there is a lot of sometimes unnecessary animation
4:10
and that plays against our perceived performance or our core web vitals
4:18
And that's not measured in time, but in a different kind of unit
4:25
But yes, having a static layout served for the first time would help in achieving really well scores
4:34
for core web vitals. And another typical concern when it comes to delivering sites is SEO, right
4:42
And in the case of the typical client-side rendering, like our Angular applications
4:48
dynamic content generation happens, and it implies that content is not available to be indexed
4:56
unless there is a request to a certain route, including the default one
4:59
So at least for some browsers, content is sort of obscure. They cannot reach it and they cannot index it
5:07
and that plays against our SEO rankings. So this makes client-side rendering
5:15
sort of problematic strategy for solutions that heavily rely on accurate content indexing
5:21
like marketing blogging, for example. And when it comes to Angular, we have a rendering strategy type
5:27
that sort of overcomes or takes care of a few of the mentioned issues
5:33
And what I'm talking about is Angular Universal. So let's explore the performance metrics for perceived speed for the server side rendering strategy like Angular Universal
5:44
It also happens at runtime, but on the server side. So the whole rendering process takes place on the server
5:55
It now takes care of all those heavy load operations. And the perceived performance metrics are quite similar to the ones of client-side rendering, but we may be facing new problems or additional problems that we will need to solve
6:12
So, first of all, development for server-side rendering with Angular Universal forces you to consider using dependencies that are ready to execute a lot of operations that we tend to perform on the client now on the server
6:27
And also you have to write your code specifically for server-side rendering because, well, for example, the DOM is not available on the server
6:38
So you need to write with an entirely different flavor your applications
6:44
You also need to use modules that are conceived for this technique and also maybe need a special infrastructure, more complex, probably more costly as well
6:55
And another downside of server-side rendering with Angular Universal, for example, used to be the duplication of state
7:07
Because when the application is bootstrapped in the client, since you both request state from the server and the client, it produces a certain flickering
7:17
because you have a certain state in the client and then the application gets bootstrapped
7:23
and there is like a certain time when things may not match This issue can be solved with the transfer state API that allows you to precisely serialize and deserialize state in a store
7:36
from the server side to the client side and avoid this application
7:41
But all those special requirements, the cost implied, all of that leaves a great portion
7:50
of dedicated solutions out of the Angular universal scheme, let's say. It's super simple to have an Angular application up and running, right
7:59
with the CLI. Right now you go and type a few commands and click a button
8:04
and you have a working application. But if we wanted to use Angular for a simple blog site, for example
8:12
or a documentation site, it is probably not the ideal solution. So needless to say, if you have a standard Angular application and you want to migrate
8:25
it to Angular Universal because of SEO reasons or because of performance reasons, it is quite
8:32
the effort. It's not that trivial and it may take a lot of pain and a lot of headache
8:40
So one second. So it's not only performance and SEO that we may be missing out
8:50
There is also the whole JAMstack offering that we may be missing out if we don't have the ability to think about rendering strategies other than server side and client side
9:08
And when I say the JAMstack, what do I exactly mean? The JAMstack has been around for five years, so it's very likely most of you, if not all of you, know about it
9:19
But it has gained real traction and has become a real successful methodology, I would say, for about two of those or for about the latest two years
9:31
And why is that? It's because it's a collection of best practices that ensure better performance, higher security, and also lower costs at the time of delivering websites
9:45
And together with the concept of everything lives in Git, like you don't have to do additional installations of databases or connect to foreign systems in very complex ways
9:59
The ploys are atomic inversions. Assets should be served from a CDN exclusively
10:09
Along with all those best practices, we also have the suggestion that the best way
10:15
to deliver sites is delivering pre-renders or what we are going to talk about
10:21
static generated pages. So like I mentioned, in the Angular ecosystem, we did not have a tool to help us with that. And this is why if other frameworks were really on
10:34
the train of the JAMstack, Angular was not yet there because it didn't have really the tool to
10:42
enable this. Until recently, until December 2019, to be more precise. This is when the hero devs
10:50
which are Aaron Frost, Juan Cano, and Sander Elias, they released Scully for the first time
10:56
And what is Scali? So like we mentioned before, it's a static site generation tool. It enables pre-rendering and it works on top of Puppeteer. And you're going to say, what's Puppeteer? Well, Puppeteer is basically an API to the Chrome developer tools protocol that you can use to render HTML on a headless Chromium browser instance
11:20
So it's important to keep in mind some of the requirements to use Scali
11:25
You cannot just use it for any application at the moment. You need to be on Angular 8 or 9 or above
11:31
and you need to also be using Node version 10 minimum. So installation is super simple with schematics
11:40
You use ngadd, and the full command is ngadd, out at scali.io slash forward init and boom that's it you have uh scali in your application and i i
11:54
have to say um that i migrated a standard application to scali um i first had to to migrate it or
12:04
upgrade it to version 9 because it was in version 7 and it was completely painless so
12:12
So now that you know what it is, what StoryScal is, and how you install it very easily with
12:21
schematics, let's see how it exactly works. So the important part is that it does its job at build time
12:28
We have seen other strategies. They all work at runtime, but this one is at build time
12:34
Instead of on demand or server side or runtime client side, it's while you do the build
12:44
So, Scali does the whole data fetching, the whole logical operations. All this business logic is interpreted
12:51
Everything is handled, including the routes at build time. And it outputs or produces this HTML in its static form
13:01
So it then places this HTML that has rendered in this Chromium headless browser in a folder
13:11
that by default is this slash static. But you can configure it to be anything else
13:18
And we're going to see that later. So, of course, it needs a router-enabled application because basically what Scali does is it says
13:27
okay, we have route one, two, and whatever you define, and it maps it or converts it to a normal
13:39
or standard HTML page. And how does it do that? Well, it has a process when you run Scully
13:47
you trigger a process that runs a bunch of plugins or inbuilt plugins by default. Those
13:55
plugins take care of different concerns through the process cycle. So you have the router handling
14:00
plugins that basically take care of handling the route discovery or finding out what routes you
14:06
have in your application. Then the render plugins that take care of transforming the HTML output
14:13
The file handler plugins used by the built-in content folder plugins that take care of different
14:20
file document extensions, for example, Markdown. And finally, you have the route discovery done and the alt done
14:28
that can trigger scripts or can run processes when the route discovery or everything else is completed
14:38
Apart from the built-in plugins, it is very, very easy to write your own custom plugins
14:43
to solve specific problems in your own implementation. So at the beginning of Scali, you had to write them in JavaScript, but now there is TypeScript
14:53
support and they are part of the build process And it simpler for TypeScript developers And um and basically for any other similar tool that we have in other frameworks and other communities
15:06
the community contributions of custom plugins is very, very important to expand the ecosystem
15:14
and make the tool even more powerful and cover more use cases
15:19
So we are hoping that in time we're going to have more and more community contributions to the plugins ecosystem and we're going to have Scali serve even more purposes as it does right now
15:32
So I'm going to demonstrate the use of a custom written plugin in a bit
15:38
But before we do that, let's talk about another very important feature of Scali
15:44
And it is that it allows us to start a server to serve these static files and test the HTML output at development time
15:54
So you have the ability of putting together both versions, the fully dynamic Angular distribution and the static pages to compare them and see how you are doing better at performance and SEO and even inspect your applications and see the conversion
16:16
So what is the gain? What is the gain of doing this, of installing Scully
16:21
The most important gain is obviously speed. Like we said, it happens at build time
16:27
Then you have a fast everything, the fast first pane, the fast first contentful pane, the fast first time to first byte
16:34
the fast time to interactive. And when we go back to this core web vitals
16:40
all of them are going to be winning scores because nothing will move around
16:47
you have now a static page, and the time to interact fully with the content
16:53
is going to be right away. So performance gets exponentially better and speed or, like Aaron says
17:07
wicked fast pages when it comes to loading them. So let's propose a problem
17:14
and let's imagine we want to have a documentation site. So it's just like a blog site
17:22
They are the perfect problem to solve with static prerendering because it is likely your data is stable
17:29
You may not have to connect to any other services for authentication or anything like that
17:35
So let me show you how I solved that problem with a little demo
17:40
To speed it up or for the sake of brevity, I have done already some of the work
17:47
I have already installed Scali. I have done it to an Angular 9 application with routing, of course
17:55
And then I created a module called Docs with schematics in the way you are seeing in the slide right now
18:03
And in the template for that module, and this is very important, you cannot miss that part if you do this manually
18:11
is I added the Scully content tags, and this is where the Scully static content gets injected, right, for each page
18:22
And finally, when you install Scully, one of the things that gets created is this configuration at the root of your project
18:32
And this is exactly where we can change some of the defaults
18:36
like I mentioned before, like the output directory, which is by default this slash static
18:44
but you can change it to something else if you want. You also have the project name, the project route
18:49
And in this case, what I did is I created this entry in the
18:56
or this property in the routes property that's called slash dots, slash two dots, Slack
19:04
And Slack can match a string that we're going to, or like we are going to see in a moment
19:13
And I defined that this route needs to be discovered as a content folder type
19:21
and the folder where the sources are at is docs. So we're going to also see how I created a site navigation
19:32
so we could access every one of these documents very easily and very simple
19:38
and also it automatically refreshes every time we have new content and we do a new build because it would be very nice
19:49
to be adding all those entries manually. So what I did is I made use of the Scali route service
19:56
The Scali route service reads the discovered routes from a JSON output
20:02
And then also I used an observable to continuously, like I said, or automate the process of getting new entries into this side navigation
20:17
Finally, yes, like to say, I put it in an order list, so I could give it some kind of
20:25
an order by number, in this case one, two, three, but you can in any case do it in the
20:32
way that is best to you. So let's change now to see some code and see some terminal work
20:44
Let me do this in a moment because I need to stop sharing my screen and I need to share
20:53
a different screen. Let's share the entire screen and find the right place
21:04
Let me clear this a bit because I was testing. I wanted to make sure that everything worked
21:11
The first thing that we're going to do is we're going to create a new entry. And you're going to see how I do it with schematics
21:18
because it would be very painful to do this or really tedious to do this manually
21:22
and be creating content in that folder. And by the way, this content is Markdown
21:29
So Scali is going to convert Markdown to HTML using an inbuilt plugin that it comes powered with
21:39
So what we're going to do is we're going to do an ng generate and we're going to say Scali.io
21:47
init post. So I could pass right now already the title for my post
21:54
And I say post because this is by default meant to create blog posts and this is why
22:02
they call it post but it could be entry as well. well, but you need to type it in this way because this is what the schematic is expecting
22:13
So in it, post and let's just do this. And then it's going to prompt me or it's going to ask me what title do you want to use for
22:24
this post And let create the homepage because we are going to want to have a default page also in our application What the target folder for this post
22:36
By default, like I explained, it's thinking it's going to be blogged
22:40
but we can pass whatever else we want. In this case, we're going to say docs
22:47
Boom, it creates already the markdown file in the docs folder at the root of our application
22:55
So now if we go to the editor, we see that we have a recently created docs folder
23:05
with a homepage and the anything. Take a look, we have a front matter YAML configuration here
23:12
that we can use to manipulate this file. So I'm going to say, okay, instead of homepage
23:18
because homepage is a little bit not too attractive or enticing. Let's call it awesome.site homepage
23:30
And yeah, it says a description, blog description, but we're going to say, yeah
23:36
this is the homepage for my Scully site. And as you can see, the initial state for published for any new page you generate via the schematic
23:56
is false. It doesn't mean that Scali won't discover it. It means that it will take it
24:02
as unpublished. So we're going to just set this to true. and also I'm going to add another property called flag
24:14
and I'm going to manipulate the path to this file so I can add it to a URL I want
24:22
because right now the URL would be homepage, but I want it to be something else
24:27
Let's call it ScullyDogs Homepage. And I choose this on purpose because I set this one to be the default route for our application
24:40
So now we have all these written. We may want to also change this
24:45
This is an awesome title. And yeah, Angular has static content generation now
24:58
Okay, now we have all these markdown written and the YAML configuration here
25:06
We are going to go back to the terminal and we're going to run Scully for the first time
25:11
And we're gonna cross our fingers that it goes fine. So we're going to do an NPM run Scully
25:22
and see what happens. So it tells me, the first thing it does is it tells me that to discover new routes, I
25:33
should be using this flag, dash, dash, dash, dash, dash, scan routes
25:38
But this is our first, our first content page. So we don't need to do this
25:44
But in the future, we will, you will see now. And it also tells me that it created the Scully routes that JSON, the one this service we mentioned before, it's going to be reading from to create the site navigation
25:58
And it also explains me that it created this docs, Kali docs homepage index.html
26:08
and rendered it into file static.slash index.html because I told it that this should be
26:16
the default page for our application. And what we want to do also is we want to launch the server
26:25
So let's do an NPM. run scolli serve, and this will start two servers
26:34
the angular distribution server on local host port 1864 and the static server on local host port 1668
26:44
So we're going to go here, we're gonna reload this. And as you can see, we have our doc site
26:54
I had already some CSS as well to make it a little bit nicer
27:00
And we have here design navigation with the awesome doc site homepage
27:04
Yay, Angular has static content generation now. And if we want to see this here, if we go to the distribution, static docs
27:16
we have it here. Scali docs homepage is an index.html. we will find the yay, Angular has static content
27:25
All this has been output as HTML with all the assets in line
27:31
because it's a static page. It, Scali also generated a 4.04 page for us
27:39
And let me show you as well, the assets Scali routes over here
27:48
this entry with the route pass. We provided also the title, the description, the state for
27:59
published and the Slack ScaliDocs homepage. Also, as you can see, because this is using the content folder plugin
28:07
it says the source file, which is homepage MD. Also, we have the default route
28:15
the home page over here. So, yes, we have now a route
28:22
but having only one route is not nearly as amazing. So what we are going to do is I have a little backup here
28:30
so I don't have to create one by one with a few lessons
28:34
And let's move these lessons. Let's not move it. Let's copy them to the docs folder. Okay
28:45
Let me clear this a bit so you can see. And now, yes, we're going to run Scully with the scan routes. Oops
29:02
Scan routes plug. And let's see what happens and what we get
29:09
should be or Scully should be discovering these new routes and it does in fact as you can see here
29:18
that's lesson two lesson one lesson three rendered into file and it tells you exactly where it's
29:28
rendering the new files. And if we launch the server again
29:41
go back here and refresh, and we have already all the new lessons
29:46
ordered over here and we can go and see them. And as you can see
29:54
if I go to Lesson 3, I have a guest here because who
29:58
Who would want to have a documentation site for developers and not have a GIST
30:03
or have some sort of code demonstration or example over there. But what happens is that if you, like me, add just a script
30:16
like we can see here in the test GIST, it's going to be in the middle of a template
30:23
And that's not possible. We cannot execute scripts like this in a template
30:28
in Angular for security reasons. So what we're going to do is we're going to use
30:33
a custom plugin written by Sam Plobergs. And I said yesterday, because I had a conference
30:40
where I spoke about this topic as well, that I was going to post in Twitter a series of links
30:46
So please stay tuned because I'm going to do it right after this session
30:52
where I'm going to link you to Sam's blog and also to my repository
30:58
So you can fork it and continue to play with this and maybe even improve it
31:03
So what we're going to do is we're going to go to the configuration
31:08
Don't pay attention to this for a minute that is commented because I didn't want to type all that later
31:14
I'm not sure I'm gonna be able because of time to do this
31:18
but maybe we get to see a different kind of data fetching for Scully
31:23
So for now, concentrate on this route. And what I'm going to do is
31:29
I already installed the plugin, again, for the sake of brevity and time
31:35
But what I'm going to do is I'm going to create a constant and I'm going to call it disable angular
31:47
And this object is going to require this Kali plugin disabled Angular
31:54
that I have already installed and saved as a developer dependency. And then I'm also going to create another content
32:01
This is going to be an array called post renderers. And I'm going to pass disabled Angular here
32:19
And I could potentially use this. I could disable Angular for all the routes in my static or
32:30
pre-rendered application. But let me show you. If I wanted to do it like this, I should call the
32:40
default post-rendered property and give it this value. But I'm going to do this only for the routes
32:48
that are matching this pattern, docs slash slug. And to do that, I just create a new property here
32:57
called post-renders, and I pass it this array. This array could contain potentially as many plugins as you want to execute post rendering But right now we only going to use this one
33:16
the one we have installed. And now what we're gonna do is we're going to
33:24
we're going to rebuild our application. We need to pass it for this time to work correctly
33:32
We need to, I never remembered this, stats.json. We need to read the stats.json in order to know
33:46
what dependencies from the graph we need to disable to disable Angular entirely
33:51
So this is why we need to pass this flag when we are building the application
33:56
So now hopefully, oh, I said pause. yes i am mixing things ng build prod we're gonna build the production
34:11
version and pass the flag stats jason thank you angular and cli for letting me know that was not an
34:21
option and let's hope it harries because i have i think five minutes
34:35
okay let's clear this let's um Well, I could do this here, so I don't have to type in
34:47
Type wrong. And now let's go back to the site. Let's reload
35:00
And sometimes it takes a bit anyways, but now you have the gist here in your documentation
35:10
site and you can put as many as you want and those scripts are going to be executed because there is
35:15
no longer Angular to be taking care of that security measure. Of course, use this with care
35:23
not maybe for this purpose, but this is an example of a use case on why you may want to disable
35:30
Angular. You may want to disable Angular because it won't do anything else after the site or the
35:36
page is rendered. And you don't need to have this dependency being requested when you load the page
35:43
because that's going to penalize you also in performance and you may not need it at all. So
35:49
it's going to do all its operations at field time, then generate the page, and then maybe you no
35:55
longer need it and you can disable it. So what I wanted to show you real quickly, I'm not sure I'm
36:01
going to have time, but let's still try to do this. It I want to generate another module that we going to call temporary and route route temporary
36:25
And module going to be in the up routing. And once we generate it, we're going to do a couple of changes here
36:38
maybe you say, hmm, but what if I want to read anyway all this content from a database or from
36:47
some, yeah, endpoint because it's going to be or it's not going to scale if I am creating markdown
36:54
files anyway. Or I want users or external users to be able to create this content and obviously
37:02
they cannot reach the file system and do it themselves. So what we can do is we can enable this
37:09
We can use the JSON plugin to read from an URL endpoint
37:16
and use a property as the title for each one of our entries
37:21
So let's toggle this. Let me disable this for a minute because I don't want it to..
37:32
It's problems. And we also have to go to this new module we created, the temporary one
37:44
We need to go to routing and let's copy this. Let's put title here
37:55
So we have a new route discovery pattern. It's asking me to log in, unfortunately. Maybe
38:40
Okay, no. Let's forget about that part. But you have to trust me on that
38:48
I have an endpoint with a series of entries, and what I want to do is I want to retrieve them
38:56
So let's do that. So let's run Scully again. scan routes and hopefully it will work right away
39:10
Yeah. So these, all these routes are now retrieved from this endpoint dynamically
39:19
and they are going to be created over here in the temporary folder which is probably down here I said module source oh I cannot find it
39:41
So maybe, temporary, oh, it didn't create it for some reason, maybe there is something missing
39:51
because I'm lack of time. Render it into file this static temporary
39:59
Oh, okay. So it's not creating the, sorry, it's not creating the folder
40:05
because it's not a content folder. It's a dynamic creation. So it's here
40:10
Temporary, each one of those posts in that endpoint is new dynamic post one, two, three, blah, blah, blah
40:16
And they are here generated. And then if we serve the application now
40:25
it's called as well. Hopefully we have all those new entries on the
40:42
well, they are not there. I mean, they are there, but they don't have the right data to read from
40:48
in order to create these items. So actually what we are getting, we're getting them
40:56
The routes are here, but they don't have a title right now. They don't have a description and published
41:02
And if you remember, these site navigation, what is reading is precisely the title
41:13
in order to create these renders. So this is why they are not rendered right there
41:20
You cannot see, but you can see that it does create it in the inspector
41:25
We are just missing a little bit of logic to be able to also add this title right here
41:37
So yes, this is it. I also, now that we are inspecting
41:42
we can see that every one of those router links are now a regular href property value or attribute value
41:51
So thank you very much. I hope you enjoyed it. Like I say, or I said
42:00
we'll post on Twitter, I'll post on Twitter, more information so you can fork this repository
42:07
You can play around with it. I also created a schematic to do precisely what I said
42:12
make sure that this is rendering over here. And yes, and I hope you adopt Scully
42:19
that you have fun with it and that you can use it to solve some use cases
42:24
where you need static probably more than something dynamic. Thank you very much
#Computer Education
#Flash-Based Entertainment
#Programming
#Web Design & Development