You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hello, it's me again. I'm here to help giving some Common Performance Optimization Instruction. Some of you might recognize me, I think "You again? Why don't you just create a PR yourself??".
Well... I'm a bit too lazy...
to handle Enterprise-scale project, yet... beside I'm learning some Node.js backend stack which took a long way off. So, I didn't have enough time to learn the whole project and create a PR. So I'll be giving an instruction instead so you could implement it for future use.
First of all, you did a great job optimize common thing. Beside Next.js usually optimize most thing out of the box which you don't have to handle it yourself.
Migrate useCallback, useMemo.
Even those who experience with React missed this step, so I'll start with it. useCallback and useMemo is introduced to optimized React Function Component (a function which used hooks). Which it's a bit confuse to read, so I'll leave a quick guide here.
React Function Component will re-create itself when a state is updated.
(For more information and reason behind this search about "How React Hooks work").
The thing is, nothing is persisted... which means when you declare a function, it'll just re-create itself each time the state is updated which wasted the computation time and resources.
(I believe developer which has a lot of experience with Data-Structure and Algorithms knows why and might scream it happen).
Good thing, React team introduced useCallback which will help persist these function after state update. Under the hood, React kinda 'cache' your function. So when it re-render it just put from cache prevent a function re-creation.
constComponent=()=>{let[counter,updateCounter]=useState(0)// This function will recreate itself everytime when state is updatedletupdateAndDispatchCounter=()=>{dispatchCounter(counter+1)updateCounter(counter+1)// Do not mutate state.}return<buttononClick={()=>updateCounter()}>{counter}</button>}
That's waste a lot if there're a lot of state change happening in there, beside in React, we usually use a lot of component.
I have recommended to do this.
constComponent=()=>{let[counter,updateCounter]=useState(0)// This function will recreate itself when counter is updated.letupdateAndDispatchCounter=useCallback(()=>{dispatchCounter(counter+1)updateCounter(counter+1)// Do not mutate state.},[counter])return<buttononClick={()=>updateCounter()}>{counter}</button>}
Just like useEffect, the internal function will update when [dependencies] is changed.
It kinda useless for a single state to have a useCallback but it just an example, I'd recommended using this in multiple state changing happening in there.
Also for a function which doesn't depend on state, I'd recommended adding a blank dependencies, so React can just 'remember' it.
constComponent=()=>{let[counter,updateCounter]=useState(0)// This function will be created only on initialization.letdoSomething=useCallback(()=>{// Do something that doesn't depend on state.},[])return<buttononClick={()=>doSomething()}>Click me</button>}
useMemo is recommended for 'remembering' heavy computation but usually used with return.
It kinda implement usage from useCallback plus the syntax itself is just like useCallback.
In your case, you could use useMemo with component which is responsible for displaying user information which retrieve from Firebase. Since most of that usually don't update much.
Migrate memo and PureComponent
I don't know if you knew about memo. It's a PureComponent version of Function Component.
Basically it implement a shouldComponentUpdate which determined if the component should be updated based on props. It compare current props with new props. If it's the same, it'll just skip other life-cycle and return the component's view.
It's not a silver-bullet since if you implement it in component which isn't often follow these rule would just add a O(shallowCompare(props.length)) to the component.
Also, it's not recommended to use this memo and PureComponent in component which is not pure (has state). You can read more about PureComponent here
Use Dynamic Import
I recognize that you use Next.js to build this project. I'd recommended considering about putting a Component which isn't require in First Contentful Paint to next/dynamic
By doing this, you would get a lot of benefit including:
Decreasing total file-size for first pain.
Better download time.
Well, lazyload (If you know what I'm talking about)
Code splitting (I'll explained this on next topic)
Better SEO (based on Google's Pagespeed Insight)
It also support SSR out of the box.
Dynamic import is good.
You can also use useLazy and Suspense for React Component on client-side which require an interaction eg: Usage with IntersectionObserver which basically, a lazy load component.
Chunk Splitting
I recognize that you're using Zeit Now as a server. By default, Now's server use HTTP/2 Server Push which handle a better job for caching, by overall splitting code into a smaller chunk would performance a better when using with HTTP/2 Server Push and Cache.
I'd recommended that you should splitting a JavaScript and CSS chunk into a smaller piece. Beside, a CSS which generated from StyledComponent with multiple-page app by default doesn't perform tree-shaking as good as it should. I'd recommended checking about that too, otherwise there would be some CSS which doesn't require when using with some pages.
Font display=swap and Google Fonts CDN.
Google Fonts is magic. Adding that ?display=swap property and Google Fonts CDN will send only letter which you used in the page. I don't know what witchery is this and how does Google does it, but it really just working, eg:
I don't know if one of you have ever inspect the production code but, it doesn't pre-render.
This is critical
I believe Mark already how to fix it. In Next 9.1.7, getStaticProps and getServerProps is introduced for generating a getInitialProps in different usage.
In your src/pages/_document.tsx, you're using getInitialProps for injecting a CSS which generated by StyledComponent into global view which is good.
But... it break pre-rendering which is really bad since React will just created everything on client which require a lot of computation time and memory usage. (If you put an animation on first page it wouldn't even get 60fps) plus it put a great amount of time to render all of it.
I'm not quite sure if it will work but, I'd recommended switching from getInitialProps into getStaticProps so it could just pre-render the page. Even if it getStaticProps doesn't working, I'd recommended to put your best effort to pre-render the page. You could just use React Snap in case getStaticProps doesn't working or just use prerender.io
In case you're wondering why, you might want to read how React construct its component and perform a CPU throttle testing.
Cache Header
Since you're using a Zeit Now server (in fact, any other server should applied this too). You should just add Cache-Control header for any static content which is public and static.
I guess you know why but I'll just explain it again, WebP is a new format for image which is exclusively built for web.
Not THAT much exclusive but still
It reduced a lot of spaces like how gZip helps reduced resources' size.
Enterprise-scale Optimization
An above topic is basically a SHOULD do form most React app. Below this is optional but since you're building an Enterprise-scale app, you should consider applied these technique. (PS. it might be hard tho)
Error Boundary.
I don't know how or if you even trace error in your application but for best-practice you should.
Using React's Error Boundary would help you trace a lot of error which you could fixed it later. You could also use (Sentry)[http://sentry.io/] to help tracing errors which occurs.
Compile to static where possible.
I noticed that you rarely use getInitialProps which is only available in SSR. In-case you didn't know, Static Content perform better than SSR does in most way including performance. So, if most of your site are not using a benefit of SSR, I'd strongly recommended you to switch to CSR and use Progressive Hydration where you necessary.
Plus by default Zeit's Now server doesn't support running Nextjs with custom server which you would lose a benefit of Message Queue and custom cache which is REALLY important for Enterprise-scale app.
In case you're wondering why I've never compiled my app to static, I does but never put it on Git. Since I've never deployed (but I practiced it) any Enterprise-scale app publicly, that's why all of my project never compile to static since I'm using Zeit Now, a serverless which doesn't allowed Next.js to run with custom server plus I'm too lazy to do it since I build side-project focusing on practicing Frontend Development.
Plus, I'm too lazy to do it.
Message Queue
I'm too lazy to explain this one, please Google it yourself. It's a topic you should consider.
Cloudflare worker.
I bet you knew Cloudflare so I wouldn't explained much. I'd recommended caching your site with Cloudflare worker if possible, that's all.
Add test.
Too lazy, but you could Google it. I'll leave some important topic.
Unit Test
Integration Test.
Code-coverage.
Stress Test.
You might somehow want to use for visual test percy.io.
About Zeit Now
It's perfectly fine running your server on Zeit Now for now...
Just an opinion but I think Zeit Now isn't really suitable for an Enterprise-Scale app. (You should asked Mark about it).
I'd recommended adding CI/CD and just use other provided like Google Cloud or deploys.app and implement your own Kubernetes model. (Mark knows Kubernetes asked him for a help)
Don't ask me, I don't know how to setup a Kubernetes Engine.
That's quite it.
That's quite a common thing you should optimize. Yep...
I might forget something which I might open an issues if I somehow remember it.
And believe me, I will.
So, I'll go to sleep and being lazy until then.
The text was updated successfully, but these errors were encountered:
Common Performance Optimization
Hello, it's me again. I'm here to help giving some Common Performance Optimization Instruction. Some of you might recognize me, I think "You again? Why don't you just create a PR yourself??".
Well... I'm a bit too lazy...
to handle Enterprise-scale project, yet... beside I'm learning some Node.js backend stack which took a long way off. So, I didn't have enough time to learn the whole project and create a PR. So I'll be giving an instruction instead so you could implement it for future use.
First of all, you did a great job optimize common thing. Beside Next.js usually optimize most thing out of the box which you don't have to handle it yourself.
Migrate
useCallback
,useMemo
.Even those who experience with React missed this step, so I'll start with it.
useCallback
anduseMemo
is introduced to optimized React Function Component (a function which used hooks). Which it's a bit confuse to read, so I'll leave a quick guide here.React Function Component will re-create itself when a state is updated.
(For more information and reason behind this search about "How React Hooks work").
The thing is, nothing is persisted... which means when you declare a function, it'll just re-create itself each time the state is updated which wasted the computation time and resources.
(I believe developer which has a lot of experience with Data-Structure and Algorithms knows why and might scream it happen).
Good thing, React team introduced
useCallback
which will help persist these function after state update. Under the hood, React kinda 'cache' your function. So when it re-render it just put from cache prevent a function re-creation.That's waste a lot if there're a lot of state change happening in there, beside in React, we usually use a lot of component.
I have recommended to do this.
Just like
useEffect
, the internal function will update when[dependencies]
is changed.It kinda useless for a single state to have a
useCallback
but it just an example, I'd recommended using this in multiple state changing happening in there.Also for a function which doesn't depend on state, I'd recommended adding a blank dependencies, so React can just 'remember' it.
useMemo is recommended for 'remembering' heavy computation but usually used with
return
.It kinda implement usage from useCallback plus the syntax itself is just like useCallback.
In your case, you could use
useMemo
with component which is responsible for displaying user information which retrieve from Firebase. Since most of that usually don't update much.Migrate memo and PureComponent
I don't know if you knew about
memo
. It's aPureComponent
version of Function Component.Basically it implement a
shouldComponentUpdate
which determined if the component should be updated based on props. It compare current props with new props. If it's the same, it'll just skip other life-cycle and return the component's view.It's not a silver-bullet since if you implement it in component which isn't often follow these rule would just add a O(shallowCompare(props.length)) to the component.
Also, it's not recommended to use this
memo
andPureComponent
in component which is not pure (has state).You can read more about PureComponent here
Use Dynamic Import
I recognize that you use Next.js to build this project. I'd recommended considering about putting a Component which isn't require in First Contentful Paint to next/dynamic
By doing this, you would get a lot of benefit including:
It also support SSR out of the box.
Dynamic import is good.
You can also use
useLazy
andSuspense
for React Component on client-side which require an interaction eg: Usage with IntersectionObserver which basically, a lazy load component.Chunk Splitting
I recognize that you're using Zeit Now as a server. By default, Now's server use HTTP/2 Server Push which handle a better job for caching, by overall splitting code into a smaller chunk would performance a better when using with HTTP/2 Server Push and Cache.
I'd recommended that you should splitting a JavaScript and CSS chunk into a smaller piece. Beside, a CSS which generated from StyledComponent with multiple-page app by default doesn't perform tree-shaking as good as it should. I'd recommended checking about that too, otherwise there would be some CSS which doesn't require when using with some pages.
Font display=swap and Google Fonts CDN.
Google Fonts is magic. Adding that ?display=swap property and Google Fonts CDN will send only letter which you used in the page. I don't know what witchery is this and how does Google does it, but it really just working, eg:
Pre-render
I don't know if one of you have ever inspect the production code but, it doesn't pre-render.
This is critical
I believe Mark already how to fix it. In Next 9.1.7,
getStaticProps
andgetServerProps
is introduced for generating agetInitialProps
in different usage.In your
src/pages/_document.tsx
, you're usinggetInitialProps
for injecting a CSS which generated by StyledComponent into global view which is good.But... it break pre-rendering which is really bad since React will just created everything on client which require a lot of computation time and memory usage. (If you put an animation on first page it wouldn't even get 60fps) plus it put a great amount of time to render all of it.
I'm not quite sure if it will work but, I'd recommended switching from
getInitialProps
intogetStaticProps
so it could just pre-render the page. Even if itgetStaticProps
doesn't working, I'd recommended to put your best effort to pre-render the page. You could just use React Snap in casegetStaticProps
doesn't working or just use prerender.ioIn case you're wondering why, you might want to read how React construct its component and perform a CPU throttle testing.
Cache Header
Since you're using a Zeit Now server (in fact, any other server should applied this too). You should just add
Cache-Control
header for any static content which is public and static.Use WebP instead of JPG and PNG.
I guess you know why but I'll just explain it again, WebP is a new format for image which is exclusively built for web.
Not THAT much exclusive but still
It reduced a lot of spaces like how gZip helps reduced resources' size.
Enterprise-scale Optimization
An above topic is basically a SHOULD do form most React app. Below this is optional but since you're building an Enterprise-scale app, you should consider applied these technique. (PS. it might be hard tho)
Error Boundary.
I don't know how or if you even trace error in your application but for best-practice you should.
Using React's Error Boundary would help you trace a lot of error which you could fixed it later. You could also use (Sentry)[http://sentry.io/] to help tracing errors which occurs.
Compile to static where possible.
I noticed that you rarely use
getInitialProps
which is only available in SSR. In-case you didn't know, Static Content perform better than SSR does in most way including performance. So, if most of your site are not using a benefit of SSR, I'd strongly recommended you to switch to CSR and use Progressive Hydration where you necessary.Plus by default Zeit's Now server doesn't support running Nextjs with custom server which you would lose a benefit of Message Queue and custom cache which is REALLY important for Enterprise-scale app.
In case you're wondering why I've never compiled my app to static, I does but never put it on Git. Since I've never deployed (but I practiced it) any Enterprise-scale app publicly, that's why all of my project never compile to static since I'm using Zeit Now, a serverless which doesn't allowed Next.js to run with custom server plus I'm too lazy to do it since I build side-project focusing on practicing Frontend Development.
Plus, I'm too lazy to do it.
Message Queue
I'm too lazy to explain this one, please Google it yourself. It's a topic you should consider.
Cloudflare worker.
I bet you knew Cloudflare so I wouldn't explained much. I'd recommended caching your site with Cloudflare worker if possible, that's all.
Add test.
Too lazy, but you could Google it. I'll leave some important topic.
You might somehow want to use for visual test percy.io.
About Zeit Now
It's perfectly fine running your server on Zeit Now for now...
Just an opinion but I think Zeit Now isn't really suitable for an Enterprise-Scale app. (You should asked Mark about it).
I'd recommended adding CI/CD and just use other provided like Google Cloud or deploys.app and implement your own Kubernetes model. (Mark knows Kubernetes asked him for a help)
Don't ask me, I don't know how to setup a Kubernetes Engine.
That's quite it.
That's quite a common thing you should optimize. Yep...
I might forget something which I might open an issues if I somehow remember it.
And believe me, I will.
So, I'll go to sleep and being lazy until then.
The text was updated successfully, but these errors were encountered: