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

App statistics: Help us make Preact even faster #2618

Open
3 tasks done
marvinhagemeister opened this issue Jul 11, 2020 · 26 comments
Open
3 tasks done

App statistics: Help us make Preact even faster #2618

marvinhagemeister opened this issue Jul 11, 2020 · 26 comments

Comments

@marvinhagemeister
Copy link
Member

marvinhagemeister commented Jul 11, 2020

🚀 Making Preact even faster

Good news! We're experimenting with various ways to speed up Preact even more 💯 To do so we have various enhancements and optimizations planned for our reconciler. So far we've come up with multiple different approaches that are battling against each other to claim the speed title or be less memory intensive.

Although we do have a small selection of apps to test the various optimizations, we want to make sure that we optimize the things that actually matter most to everyone. We some intuitions what that might be, but it's always better to have some solid data!

For this reason we've added a new "Statistics" panel to the Preact Devtools extensions. It allows you to measure statistics for our renderer and will help us get a picture of which optimizations will matter the most.

👋 How you can help

To start you need to have the Preact Devtools extension installed. Only the latest version (1.1.0) has the "Statistics"-tab. The extension is currently waiting approval and you can see the status for your browser below:

  • Chrome: Released
  • Edge: Released
  • Firefox: Released

Now we're ready to go!

  1. Open Preact Devtools and go to the "Statistics" tab
  2. Click the red "Record" button or the "Reload and record stats" button right next to it
  3. Navigate through your app like a typical user would
  4. Post a screenshot of the "Statistics"-Panel in this thread.

Here is an example of what it looks like after measuring preactjs.com for a while by clicking through various pages:

Screenshot from 2020-07-11 16-23-18

@marvinhagemeister
Copy link
Member Author

Here are some statistics for the "People browser" in our demo app:

Screenshot from 2020-07-11 17-19-16

@srdjan
Copy link

srdjan commented Jul 11, 2020

Screen Shot 2020-07-11 at 11 47 54 AM

@marvinhagemeister
Copy link
Member Author

Stats from the virtualized table from #2619 :

Screenshot from 2020-07-11 22-46-21

@hchokshi
Copy link

I have a toy CRUD-ish project that renders a large list of data using bvaughn/react-window for virtualization and material-ui for UI components.

It shows a side-by-side view on larger screens, and a tabbed view on mobile. Tried to do vaguely the same actions on both.

Also, I'm using Suspense/Lazy for a few larger components (like modals) but the Suspense render counts are showing up as zero - may be an issue with my setup, or with the stats counter for suspense.

Desktop:
image

Mobile:
image

@marvinhagemeister
Copy link
Member Author

marvinhagemeister commented Jul 12, 2020

@hchokshi That's awesome, thank you so much for sharing these 🙌 Regarding not detecting Suspense: That sure sounds like a bug. I'll take a look 👍

@hchokshi
Copy link

@marvinhagemeister In case it helps with repro/debugging, some of my suspense loaded components are coming from material-ui - in the form React.lazy(() => import("@material-ui/core/Snackbar")), and some are coming from my own project. These are exported as export const Something = props => <div /> rather than default exports, so they're loaded as React.lazy(() => import("./components/Something").then(m => ({ default: m.Something })))

@dvdzkwsk
Copy link

image

@gu-stav
Copy link

gu-stav commented Aug 28, 2020

A small Gatsby Site with a couple of images, but mostly static:

Screenshot from 2020-08-28 15-10-05

@ljosberinn
Copy link

image

personal site, index

@ConradSollitt
Copy link

Great job on the Preact DevTools @marvinhagemeister The statistics tab is very cool and I can see it being very helpful for future development when I'm working with Preact on advanced apps.

I've used React DevTools and was aware of the Preact DevTools but didn't properly try it till today after seeing this. I saw in the docs to use import for DevTools so I was curious if I could get it working from CDN using the UMD build and found a way. Granted I didn't find documentation if it's possible but I wanted to try because I developed a small JSX Compiler (5.7 kB) as a drop-in replacement for Babel Standalone so I can develop with JSX for both React and Preact using only HTML. So most of my Preact development is done without a build process.

// From the Docs for Preact DevTools
import "preact/debug";
import "preact/devtools";

Screenshots, additional info, and statistic details are below but here is the page I got working. It has several different options which I've documented in the comments.

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Preact Online Demo</title>
        <style>
            h1 { text-align:center; }
        </style>
    </head>
    <body>
        <script src="https://unpkg.com/[email protected]/dist/preact.umd.js"></script>
        <script src="https://unpkg.com/[email protected]/debug/dist/debug.umd.js"></script>
        <script src="https://unpkg.com/[email protected]/devtools/dist/devtools.umd.js"></script>

        <script>
            // Not yet needed, but keeping for reference in case it's needed later...
            /*
            'use strict';
            window.exports = window;
            window.require = function(module) {
                console.log('Called require() with: ' + module);
                switch (module) {
                    case 'preact':
                        return window.preact;
                    case 'preact/debug':
                        return window.preactDebug;
                    case 'preact/devtools':
                        return window.__PREACT_DEVTOOLS__;
                    default:
                        console.error('Unhandled module: ' + module);
                }
            };
            */
        </script>

        <!--
            Preact without JSX

            Preact DevTools shows "This page is using Preact"
            however Components do not appear on the Elements Tab.
        -->
        <!--
        <script type="module">
            const app = preact.h('h1', null, 'Hello World!');
            preact.render(app, document.body);
        </script>
        -->

        <!--
            Preact JSX (in a Browser)
            Choose one option (both work for this demo and both work with Preact DevTools)
            [babel.min.js]
                Full Babel Standalone and great for development if using React DevTools
                but very large so it typically delays page load by several seconds.
            [jsxLoader.min.js]
                Small 5.7 kB gzip and fast to compile and run, but only handles JSX.
                For IE or older browsers it automatically downloads Babel Standalone.
                https://github.com/dataformsjs/dataformsjs/blob/master/docs/jsx-loader.md
        -->
        <!-- <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script> -->
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/js/react/jsxLoader.min.js"></script>

        <!--
            Basic Hello World Demo
        -->
        <!--
        <script type="text/babel">
            // @jsx preact.h
            function Hello(props) {
                return <h1>Hello {props.name}</h1>
            }
            preact.render(<Hello name="World" />, document.body);
        </script>
        -->

        <!--
            Demo using Components to download a list of
            all Countries and display it in a table.
        -->
        <link rel="stylesheet" href="https://d2xbd92kui7v97.cloudfront.net/site/css/getting-started.css">
        <script>
            if (window.jsxLoader) {
                jsxLoader.usePreact();
            } else {
                // If using Babel Standalone make sure [React.Component] references
                // [preact.Component] before [DataFormsJS.min.js] is loaded.
                window.React = window.preact;
            }
        </script>
        <script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/js/react/es6/DataFormsJS.min.js"></script>
        <script nomodule src="https://cdn.jsdelivr.net/npm/[email protected]/js/react/es5/DataFormsJS.min.js"></script>
        <script type="text/babel">
            // The next two lines are needed if using [babel.min.js] or [jsxLoader] without calling `usePreact()`
            // @jsx preact.h
            // @jsxFrag preact.Fragment
            const format = new Format();

            function ShowLoading() {
                return <h3 className="loading">Loading...</h3>;
            }

            function ShowError(props) {
                return <p className="error">{props.error}</p>;
            }

            function ShowCountries(props) {
                return (
                    <>
                        <h1>Countries</h1>
                        <InputFilter
                            filter-selector="table"
                            filter-results-selector="h1"
                            filter-results-text-all="{totalCount} Countries"
                            filter-results-text-filtered="Showing {displayCount} of {totalCount} Countries"
                            placeholder="Enter filter, example 'North America'" />

                        <SortableTable
                            data-sort-class-odd="row-odd"
                            data-sort-class-even="row-even">
                            <thead>
                                <tr>
                                    <th>Code</th>
                                    <th>Name</th>
                                    <th>Size (KM)</th>
                                    <th>Population</th>
                                    <th>Continent</th>
                                </tr>
                            </thead>
                            <tbody>
                                {props.data && props.data.countries && props.data.countries.map(country => {
                                    return (
                                        <tr key={country.iso}>
                                            <td>{country.iso}</td>
                                            <td>{country.country}</td>
                                            <td className="text-right" data-value={country.area_km}>{format.number(country.area_km)}</td>
                                            <td className="text-right" data-value={country.population}>{format.number(country.population)}</td>
                                            <td>{country.continent}</td>
                                        </tr>
                                    )
                                })}
                            </tbody>
                        </SortableTable>
                    </>
                )
            }

            class App extends preact.Component {
                render() {
                    return (
                        <ErrorBoundary>
                            <main id="view" className="container">
                                <JsonData
                                    url="https://www.dataformsjs.com/data/geonames/countries"
                                    isLoading={<ShowLoading />}
                                    hasError={<ShowError />}
                                    isLoaded={<ShowCountries />}
                                    loadOnlyOnce={true} />
                            </main>
                        </ErrorBoundary>
                    )
                }
            }

            preact.render(
                <App />,
                document.body
            );
        </script>
    </body>
</html>

I found initially when using the *.umd.js builds with JS only (and no JSX) Preact DevTools detected the page was using Preact but didn't attach the elements.

image

After some more testing I found I could use JSX on the page with either Babel Standalone or the JSX Loader I wrote and then it worked. Here are several different random tests of changing state on props from the demo above.

image

image

This is not the best example for Preact DevTools because the page is a simple page and for the most part uses Preact in a templating type manner where state and props are not updated after the JSON service runs and mounts the data. However it shows Preact DevTools works using HTML and JS only! Example changes I made to generate the above statistics include changing display through fetchState or editing country data from the countries array.

Based on the demo page:

document.querySelectorAll('table tbody tr td').length === 1260

Additionally I found the Preact DevTools do not detect Preact if it runs from local file system so an actual host is required (without researching possibly https is required by Chrome). If you want to try it out I have a playground site https://www.dataformsjs.com/en/playground I used for this that allows temp pages/sites to be created for 1 hour.

image

@ConradSollitt
Copy link

After my comment earlier today I felt motivated to convert several React Demos I had published to Preact and try them with Preact DevTools and here is what I found. I've provided the links and currently the are setup to include needed scripts for Preact DevTools.

Additionally I found this setup also works when using Preact DevTools:

<script src="https://unpkg.com/[email protected]/dist/preact.min.js"></script>
<script src="https://unpkg.com/[email protected]/debug/dist/debug.umd.js"></script>
<script src="https://unpkg.com/[email protected]/devtools/dist/devtools.umd.js"></script>

Two Page SPA using Preact Router

This result occurs after clicking back and forth between the pages a number of times.

https://www.dataformsjs.com/getting-started/en/template-preact-router.htm

image

Same Two Page SPA but using React Router instead

Notice the large differences on the update operations.

https://www.dataformsjs.com/getting-started/en/template-preact-graphql.htm

image

Real World Demo App

This is also a SPA using React Router. It displays geographic data and lets the user quickly click between pages. In addition to Preact and displaying data from JSON services it lazy loads and uses Leaflet Map on the city page and on the search screen it uses jQuery Chosen (downloads on first access).

While this app is simple (mostly displays data from JSON services and doesn't use much VDOM other that route changes) I would describe it as a real world app for the fact that I've written a number of small apps like this at different companies to pull ERP or other data from various systems so users can view the data quickly and in my opinion stuff like this works very well with Preact.

https://www.dataformsjs.com/examples/places-demo-preact.htm#/en/

image

@marvinhagemeister
Copy link
Member Author

@ConradSollitt This is awesome! Thanks for sharing those numbers 👍

Devtools should work out of the box with unbundled sites. If there are any issues please let me know. So far none have been reported for that at https://github.com/preactjs/preact-devtools/issues

@ConradSollitt
Copy link

Your welcome @marvinhagemeister

Yep, you are correct DevTools works out of the box. I think my original version was too simple as it would have only rendered once and had no props or state so there was no need for DevTools to show anything other than connected.

<script type="module">
  const app = preact.h('h1', null, 'Hello World!');
  preact.render(app, document.body);
</script>

The below Hello World version works perfectly with DevTools and allows name and color to be changed from the DevTools and statistics to be tracked.

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Preact Online Demo</title>
        <style>
            h1 { text-align:center; }
        </style>
    </head>
    <body>
        <script src="https://unpkg.com/[email protected]/dist/preact.min.js"></script>
        <script src="https://unpkg.com/[email protected]/debug/dist/debug.umd.js"></script>
        <script src="https://unpkg.com/[email protected]/devtools/dist/devtools.umd.js"></script>
        <script type="module">
            function Hello(props) {
              return preact.h('h1', { style:{ color: props.color } }, 'Hello ', props.name, '!');
            }
            preact.render(preact.h(Hello, { name: 'World', color: 'blue' }), document.body);
        </script>
    </body>
</html>

@mzaien
Copy link

mzaien commented Dec 27, 2020

CleanShot 2020-12-27 at 12 06 07@2x

@austinjp
Copy link

austinjp commented Jan 6, 2021

image

If it's of use, here's a small screenshot of the UI that I'm building. It's a toy Bayesian network modeller. Everything is a Preact component. Data is adjusted using sliders which are drag-to-resize divs, which causes lots of re-rendering. I'll probably refactor that at some point. Probably 😄

image

@eliseumds
Copy link

eliseumds commented Mar 7, 2021

image

We use Preact for all of our embedded review widgets.

image

image

@fabiospampinato
Copy link

Happy to share some statistics from my app too:

image

@eliseumds
Copy link

An internal admin app (aliased React to Preact):

Screen Shot 2021-03-15 at 10 45 21 am

@lucacasonato
Copy link
Contributor

Browsing around on https://deno.land for a minute or so:
image

https://doc.deno.land also uses Preact if you want some more data :-)

@ShadiestGoat
Copy link

My Set Game recreation (it has a timer thing, like a live split rip off, updates once in like 59ms) (https://github.com/ShadiestGoat/set-game-site)

Hope it helps!

Image

@ntkoopman
Copy link

From our production website. Server rendered with some partially hydrated components.

Page 1:
image

Page 2:
image

@gavmor
Copy link

gavmor commented Dec 9, 2021

Simple usage of primary workflow in a very small app:

Screen Shot 2021-12-09 at 3 46 44 PM

Toying around with main interactive element as maybe an excitable user might:
Screen Shot 2021-12-09 at 3 51 32 PM

This was fun! What kinds of stories do they tell?

@rolftimmermans
Copy link
Contributor

Screenshot 2022-02-07 at 16 38 41

@kinglozzer
Copy link

We have an app that users can use to search for venues - with results rendered in a map view (lazy loaded) and a list view. Users can toggle to enable/disable the map, list view is always shown. Uses:

  • React + preact/compat
  • React.lazy() + Mapbox + @urbica/react-map-gl for the map
  • Apollo + GraphQL
  • react-router-dom

With the map view enabled:
Screenshot 2022-02-11 at 15 18 54

With the map view disabled:
Screenshot 2022-02-11 at 15 19 26

@BartmanAbyss
Copy link

@thegedge
Copy link

thegedge commented Sep 3, 2022

https://thegedge.github.io/muzart/. Heavy SVG usage, switching between a few tracks on a guitar tab. MobX for state, and not a lot of effort put into optimizing anything at the moment.

preact statistics for muzart

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