nandorojo / reanimated-tree-shaking

A Next.js playground to test Reanimated bundle size.
24 stars 1 forks source link

Results #1

Open nandorojo opened 2 years ago

nandorojo commented 2 years ago
Screen Shot 2022-05-12 at 9 40 54 AM

Here are the preliminary results for an empty page. Compare / to /reanimated. They are both identical, but Reanimated has 80kb greater.

Some questions to answer:

  1. Is this because Reanimated itself is 80 kb? Or, is it because Reanimated is importing all of react-native-web, breaking its babel plugin that tree shakes imports from react-native directly?

    a. If it is indeed breaking because it's importing all of RNW, this is an easy solution. We just need to split the createAnimatedComponent calls of Text, View, etc. into their own files. See Dripsy for an example.

  2. What does adding sideEffects: true to Reanimated change?

RazaShehryar commented 2 years ago

I suspect this might be due to the second one where it is importing all of the RNW.

nandorojo commented 2 years ago

After upgrading to Reanimated v3, which has lower size since it removes all of Reanimated v1 code, the bundle size of the page already decreased by like 20kb (~25%)!

Screen Shot 2022-05-12 at 10 01 39 AM

So it may still be due to importing all of RNW, but it seems like the Reanimated size certainly has an impact. Going to investigate further.

nandorojo commented 2 years ago

I commented out ScrollView, Image and FlatList:

Screen Shot 2022-05-12 at 10 19 47 AM Screen Shot 2022-05-12 at 10 19 43 AM

This dropped the page size to 45kb (down another 20kb):

Screen Shot 2022-05-12 at 10 20 37 AM

So there is certainly something to breaking RNW's tree shaking.

nandorojo commented 2 years ago

The FlatList is the greatest culprit: importing that adds 11kb. It looks like that's the size of the actual FlatList from react-native-web (it's huge). I thought this might be from reanimated's layout animations, but importing FlatList directly resulted in the same bundle size.

Screen Shot 2022-05-12 at 10 23 42 AM

Overall, this is good news. If this import size is only due to the components, that's likely easy to optimize. However, I don't think that's the full story...

Here is the size with all components, minus FlatList (53kb):

Screen Shot 2022-05-12 at 10 27 21 AM
RazaShehryar commented 2 years ago

Interesting. What's the next step? Create an issue on RNW? 😢

nandorojo commented 2 years ago

I still want to find where the remaining bloat from reanimated is coming from. As for FlatList, probably best to not use on Web. It doesn't offer any benefits besides code sharing anyway, since it doesn't optimize performance on Web or do any virtualization.

RazaShehryar commented 2 years ago

So you suggest to completely get rid of the FlatList components on the web and instead replace it with a ScrollView?

nandorojo commented 2 years ago

It's your call, but if you want to avoid the 10kb of FlatList on web, you could do that yeah.

nandorojo commented 2 years ago
image

This is the bundle size with zero components. So reanimated itself is taking up 47kb out of the 63kb total.

However, it also has a massive JS load to the right. What is the difference between size and first load JS?

nandorojo commented 2 years ago

Adding sideEffects: false significantly decreased the size too. However, I'm unsure if this is safe or not.

Does reanimated Web have side effects, such as initializing global variables? If so, we should put those all into one file.

nandorojo commented 2 years ago

sideEffects: false resulted in 79.3kb first load (with other components commented out)

image

Without sideEffects: false, it is 116kb:

Screen Shot 2022-05-12 at 4 13 53 PM

These are relative numbers not absolutes, since I have other code commented out. The actual bundle size is larger.

nandorojo commented 2 years ago

I only found 2 files that edited the global scope and that were used by Web. I found them by searching global. in VSCode.

Adding these to sideEffects in Reanimated's package.json had the same effect as adding sideEffects: false – a roughly 38kb decrease in bundle size.

image

This is very promising.

nandorojo commented 2 years ago

With zero optimizations:

65kb side, 168kb first load

Screen Shot 2022-05-12 at 5 17 41 PM

Add sideEffects:

37kb size (45% lower), 130kb first load (23% lower)

Screen Shot 2022-05-12 at 5 18 23 PM

Separate components into their own files:

No impact. I think it's because they're all getting added as Animated.View on that object. I'll have to see if there is a way to optimize this type of variable or not...

image

Removing all components

Removing all components brings it down to 70kb, so we are getting an additional 60kb from adding them to Animated.

image

Named export of components

What if we remove the components from Animated, put them into their own files, and then export them one-by-one as named exports?

image

The bundle size is now 110kb first load, down from 130kb if we put them directly on Animated. This is using only a View, meaning it is now successfully tree shaking per-component.

image

It's worth noting that this is importing Animated from react-native-reanimated, but it is not importing any components. As a result, components are tree-shaking when they aren't used.

However, this is now completely acceptable. Notice that the page size is only 17kb – 16kb lower than /, which has no reanimated components.

Before starting, using Reanimated on a page added 81kb. Now, it's only 17kb.

The only missing piece is trying to get backwards compatibility for Animated.View usage...

nandorojo commented 2 years ago

Success!

image

Doing this results in no additional bundle size, meaning it is tree shaking the variables at least. The only missing piece is, can we add Animated.View to Animated without importing all components? Can PURE be used for this?

nandorojo commented 2 years ago

I think I have the solution for named exports: do both named exports and default one. Now that the default one can be tree shaken, as long as you don't import it, then it won't affect bundle size.

Instead, if we use star imports, it will import a smaller bundle:

import * as Animated from 'react-native-reanimated'

With this method, we can have no breaking changes and still preserve Animated.View usage for both default imports and star imports.

nandorojo commented 2 years ago

Default import has been solved. Opening a PR on react-native-reanimated.

nandorojo commented 2 years ago

I opened a PR at https://github.com/software-mansion/react-native-reanimated/pull/3278