Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Results #1

Open
nandorojo opened this issue May 12, 2022 · 17 comments
Open

Results #1

nandorojo opened this issue May 12, 2022 · 17 comments

Comments

@nandorojo
Copy link
Owner

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.

  1. What does adding sideEffects: true to Reanimated change?
@RazaShehryar
Copy link

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

@nandorojo
Copy link
Owner Author

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
Copy link
Owner Author

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
Copy link
Owner Author

nandorojo commented May 12, 2022

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
Copy link

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

@nandorojo
Copy link
Owner Author

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
Copy link

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

@nandorojo
Copy link
Owner Author

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

@nandorojo
Copy link
Owner Author

nandorojo commented May 12, 2022

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
Copy link
Owner Author

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
Copy link
Owner Author

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
Copy link
Owner Author

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
Copy link
Owner Author

nandorojo commented May 12, 2022

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
Copy link
Owner Author

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
Copy link
Owner Author

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
Copy link
Owner Author

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

@nandorojo
Copy link
Owner Author

I opened a PR at software-mansion/react-native-reanimated#3278

piaskowyk pushed a commit to software-mansion/react-native-reanimated that referenced this issue Jun 28, 2022
## Description

<!--
Description and motivation for this PR.

Inlude Fixes #<number> if this is fixing some issue.

Fixes # .
-->

Fixes #2843.

Reduces Reanimated's bundle size on Web from `85kb` to `17kb`.

## Changes

Zero user-facing changes are in this PR. It is strictly file organization changes to optimize Reanimated for tree shaking. After extensive testing, this PR reduced Reanimated's bundle size on Web by almost 80%.

In `package.json`, you'll notice that there is a new `sideEffects` array. This is a way to tell Webpack which files need to run global code rather than as standalone modules. I looked through every file that runs on Web which edits `global` at the top-level and found 2 files. Ideally, we could move all side effects into a single file rather than colocating them with other code that may go unused. But for now, this works. (Update: I moved some into their own file)

More on Webpack tree shaking and the `sideEffects` field can be found here: https://webpack.js.org/guides/tree-shaking/


## Screenshots / GIFs

I created a fresh [repo](https://github.com/nandorojo/reanimated-tree-shaking) to fix tree shaking for Reanimated. The playground tests it by building in a Next.js app (the most common framework for Web).

Prior to this PR, this imported _all_ code from Reanimated:

```ts
import Animated from 'react-native-reanimated'
```

This resulted in an absolutely massive impact on the Web bundle size, approximately 85kb, including tons of unused code (such as layout animations).

This PR reduces the Reanimated overhead down to about `17kb`. In the future, I may be able to investigate further improvements by seeing where that size is actually coming from. But this PR is a massive step forward, since it provides no changes to the user.

I wrote about my findings [here](nandorojo/reanimated-tree-shaking#1). There are plenty of detailed screenshots there.

### Before

Massive bundle size on Web, nearly unusable.

### After

It's now very optimized for tree shaking.

## Checklist

- [x] Included code example that can be used to test this change
- [x] Updated TS types
- [ ] Added TS types tests
- [ ] Added unit / integration tests
- [ ] Updated documentation
- [x] Ensured that CI passes
fluiddot pushed a commit to wordpress-mobile/react-native-reanimated that referenced this issue Jun 5, 2023
## Description

<!--
Description and motivation for this PR.

Inlude Fixes #<number> if this is fixing some issue.

Fixes # .
-->

Fixes software-mansion#2843.

Reduces Reanimated's bundle size on Web from `85kb` to `17kb`.

## Changes

Zero user-facing changes are in this PR. It is strictly file organization changes to optimize Reanimated for tree shaking. After extensive testing, this PR reduced Reanimated's bundle size on Web by almost 80%.

In `package.json`, you'll notice that there is a new `sideEffects` array. This is a way to tell Webpack which files need to run global code rather than as standalone modules. I looked through every file that runs on Web which edits `global` at the top-level and found 2 files. Ideally, we could move all side effects into a single file rather than colocating them with other code that may go unused. But for now, this works. (Update: I moved some into their own file)

More on Webpack tree shaking and the `sideEffects` field can be found here: https://webpack.js.org/guides/tree-shaking/

## Screenshots / GIFs

I created a fresh [repo](https://github.com/nandorojo/reanimated-tree-shaking) to fix tree shaking for Reanimated. The playground tests it by building in a Next.js app (the most common framework for Web).

Prior to this PR, this imported _all_ code from Reanimated:

```ts
import Animated from 'react-native-reanimated'
```

This resulted in an absolutely massive impact on the Web bundle size, approximately 85kb, including tons of unused code (such as layout animations).

This PR reduces the Reanimated overhead down to about `17kb`. In the future, I may be able to investigate further improvements by seeing where that size is actually coming from. But this PR is a massive step forward, since it provides no changes to the user.

I wrote about my findings [here](nandorojo/reanimated-tree-shaking#1). There are plenty of detailed screenshots there.

### Before

Massive bundle size on Web, nearly unusable.

### After

It's now very optimized for tree shaking.

## Checklist

- [x] Included code example that can be used to test this change
- [x] Updated TS types
- [ ] Added TS types tests
- [ ] Added unit / integration tests
- [ ] Updated documentation
- [x] Ensured that CI passes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants