Preact: App statistics: Help us make Preact even faster

Created on 11 Jul 2020  ยท  13Comments  ยท  Source: preactjs/preact

:rocket: Making Preact even faster

Good news! We're experimenting with various ways to speed up Preact even more :100: 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:

  • [x] Chrome: Released
  • [x] Edge: Released
  • [x] 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

Most helpful comment

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

All 13 comments

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

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

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

Stats from the virtualized table from #2619 :

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

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

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

@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 })))

image

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

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

image

personal site, index

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

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:

~~~html

~~~

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

@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

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>
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jasongerbes picture jasongerbes  ยท  3Comments

paulkatich picture paulkatich  ยท  3Comments

mizchi picture mizchi  ยท  3Comments

Zashy picture Zashy  ยท  3Comments

marcosguti picture marcosguti  ยท  3Comments