r/webdev - Bundle your React app with Rollup.js
Strategy

Bundle your React app with Rollup.js : webdev


r/webdev - Bundle your React app with Rollup.js

Rollup & React

According to Tooling.Report maintained by GoogleChromeLabs , Rollup.js performs better than other bundlers.

r/webdev - Bundle your React app with Rollup.js

bundlers

In this blog post, we will see how to bundle a React.js app with rollup.js.

Let’s start

Make sure that you have installed a node.js version in your machine. Nvm makes things easier and allows us to have multiple versions of node.js in our machine.

Let’s install the latest stable nodejs version:

nvm install stable 
Now using node v15.3.0 (npm v7.0.14)

Cool! We’re ready to go.

Setup your project:

mkdir react-rollup-app 
cd react-rollup-app 
npm init

The above command will create the initial package.json. We’re going to enhance this file later.

package.json

{
  "name": "react-rollup-app",
  "version": "1.0.0",
  "description": "A react app bundled with rollup.js",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "John Dusaitis",
  "license": "MIT"
}

We’re going to enhance this file later.

Create the html file which will host your React.js app.

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Your React app bundled with Rollup.js</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

Open it with your browser:

r/webdev - Bundle your React app with Rollup.js

loads the page

That’s a good start, but soon or later you’ll need a web server to load other static files such as javascript and css files.

How rollup.js works?

It’s pretty simple:

  1. It takes an input file with your code

  2. It performs some actions to your code based on the provided plugins you gave

  3. It produces one or more output files

Give me a dead simple example

OK, fair! Let’s change the body background colour of our page.

src/index.js

document.body.style.backgroundColor = "yellow";

Install rollup and some plugins for development

Rollup.js comes with a variety of plugins and one of them can play as web server to host all your static files. We will also need another one which reloads the page after each edit of our javascript files.

Install rollup and the above 2 plugins:

npm i rollup rollup-plugin-serve rollup-plugin-livereload –save-dev

Create the rollup configuration file:

rollup.config.dev.js

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
export default {
  input: 'src/index.js',
  plugins: [
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

In our case the output file dist/bunlde.js will be used by our index.html to run the javascript code.

Include it to your index.html file:

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Your React app bundled with Rollup.js</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/dist/bundle.js"></script>
  </body>
</html>

Add the build:dev script to package.json

package.json

{
  ...
  "scripts": {
    "build:dev": "rollup -c rollup.config.dev.js -w"
  }
  ...
}

and run the command npm run build:dev.

This command, will load our yellow page to localhost:3000.

r/webdev - Bundle your React app with Rollup.js

Loads the page to localhost:3000

So far, so good. Now it’s time to run some React code.

Setup your React code

Install the react and react-dom libs:

npm i react react-dom

Create your first React component:

src/App.jsx

import React, { useState } from 'react';

const App = () => {
  const [counter, setCounter] = useState(0)
  return (
    <div>
      <h4>A react page bundled with Rollup.js</h4>
      <br />
      <h1>Counter: {counter}</h1>
      <br />
      <button onClick={()=>{setCounter(counter+1)}}>+ Increase</button>
      <br />
      <button onClick={()=>{setCounter(counter-1)}}>- Decrease</button>
    </div>
  );
};


export default App;

Edit the src/index.js to run the react code.

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const root = document.querySelector('#app');

ReactDOM.render(<App />, root);

Run the build script again:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

error #1

Hmmm, error No #1. <App />Unexpected token..

This happens because JSX is not valid JavaScript and must be transpiled to valid JavaScript. Babel is one of the most popular JavaScript tranpilers out there.

Babel also allow us to write modern JavaScript. It converts ES6/ES7 to older versions such as ES5 which are compatible with old browsers.

Install babel and the related react-preset.

npm i babel @babel/preset-react @rollup/plugin-babel --save-dev

Edit the rollup.config.dev.js

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import babel from '@rollup/plugin-babel';

export default {
  input: 'src/index.js',
  plugins: [
    babel({
      presets: ['@babel/preset-react']
    }),
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

Run again the build script:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

error #2

Error No #2. (!) Unresolved dependencies . The packages react and react-dom cannot be found.

To overcome this issue, we need another rollup plugin. The @rollup/plugin-node-resolve.

npm i @rollup/plugin-node-resolve --save-dev

add it to rollup.config.dev.js:

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import babel from '@rollup/plugin-babel';
import { nodeResolve } from '@rollup/plugin-node-resolve';

export default {
  input: 'src/index.js',
  plugins: [
    nodeResolve({
       extensions: ['.js', '.jsx']
    }),
    babel({
      presets: ['@babel/preset-react']
    }),
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

Run again the build script:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

error #3

Error No #3, name exports, this time. Let’s fix it.

For this we need the @rollup/plugin-commonjs plugin which converts CommonJS modules to ES6.

npm i @rollup/plugin-commonjs --save-dev

add it to rollup.config.dev.js:

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import babel from '@rollup/plugin-babel';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

export default {
  input: 'src/index.js',
  plugins: [
    nodeResolve({
       extensions: ['.js', '.jsx']
    }),
    babel({
      presets: ['@babel/preset-react']
    }),
    commonjs({
      include: ['node_modules/**']
    }),
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

Let’s run the build script again:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

created the bundle.js successfully

The build was successful. But, there’s a last error in the page.

r/webdev - Bundle your React app with Rollup.js

error #4

Error No #4, Uncaught ReferenceError: process is not defined. In order to fix this issue, we need a last plugin. The @rollup/plugin-replace which replaces strings in files while bundling.

npm i @rollup/plugin-replace --save-dev

Include it to the rollup config file:

rollup.config.dev.js

import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import babel from '@rollup/plugin-babel';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';

export default {
  input: 'src/index.js',
  plugins: [
    replace({
      'process.env.NODE_ENV': JSON.stringify('development')
    }),
    nodeResolve({
       extensions: ['.js', '.jsx']
    }),
    babel({
      presets: ['@babel/preset-react']
    }),
    commonjs({
      include: ['node_modules/**']
    }),
    serve({
      open: true,
      verbose: true,
      contentBase: ['', 'dist'],
      historyApiFallback: true,
      host: 'localhost',
      port: 3000
    }),
    livereload({ watch: 'dist' })
  ],
  output: {
    file: 'dist/bundle.js',
    format: 'iife',
    sourcemap: true
  }
};

Let’s run the build script again:

npm run build:dev

r/webdev - Bundle your React app with Rollup.js

the react app ran successfully

? Congrats! You have your first React & Rollup.js app is up and running. ?

Find the full source code at my github repo.

And the live working example at https://dusaitis.co/react-rollup-app/index.html

Thanks for reading.

John Dusaitis

P.S. Original blog post: https://dusaitis.com/bundle-your-react-app-with-rollup-js



Source link

Three Things You Didn’t Know About AVIF
Strategy

Three Things You Didn’t Know About AVIF


AVIF, the file format based on the AV1 video codec, is the latest addition to the next-gen image formats. Early reports and comparisons show good results compared to JPEG and WebP. However, even if browser support is good, AVIF is still on the bleeding edge in regards to encoding and decoding. Encoding, decoding, settings and parameters has been well discussed elsewhere. 

No doubt, AVIF images generate a smaller payload and are nice looking. In this post, we’ll take a closer look at issues to be aware or before you go all in on AVIF. 

1. WebP is Better for Thumbnails

One interesting observation is that for small dimension images, WebP will produce lighter payload images than AVIF.

It’s probably possible to explain why, and tune the encoder to address this case. However, that is not an option for most people. Most people would probably rely on an image optimizer like squoosh.app or an image CDN like ImageEngine. The below comparison uses exactly these two alternatives for AVIF conversion. 

We see that WebP will generally produce images with a higher file size than AVIF. On larger dimension images, ImageEngine performs significantly better than squoosh.app.

Now, to the interesting observation. On images around 100px × 100px squoosh.app passes ImageEngine on effectiveness, but then also WebP catches up and for a 80px x 80px image. WebP is actually the most effective image measured in file size. 

The test performs relatively consistently on different types of images. For this illustration, this image from Picsum is used.

Pixels Original JPEG (bytes) Optimized WebP (bytes) ImageEngine AVIF (bytes) squoosh.app AVIF (bytes)
50 1,475 598 888 687
80 2,090 1,076 1,234 1,070
110 3,022 1,716 1,592 1,580
150 4,457 2,808 2,153 2,275
170 5,300 3,224 2,450 2,670
230 7,792 4,886 3,189 3,900
290 10,895 6,774 4,056 5,130

2. AVIF Might Not Be the Best for Product Images with High Entropy

Typically, a product page consists of images of the product, and when a user’s mouse hovers over or clicks on the product image, it zooms in to offer a closer look at the details.

It is worth noting that AVIF will in certain cases reduce the level of detail, or perceived sharpness, of the image when zoomed in. Especially on a typical product image where the background is blurred or has low entropy while foreground, and product, has more detail and possibly higher entropy.

Below is a zoomed in portion of a bigger product image (JPEG, AVIF) which clearly illustrates the difference between a regularly optimized JPEG versus an AVIF image optimized by squoosh.app.

The AVIF is indeed much lighter than the JPEG, but in this case the trade off between visual quality and lower file size has gone too far. This effect will not be as perceptible for all types of images, and therefore will be difficult to proactively troubleshoot in an automated build process that relies on responsive images syntax for format selection.

Moreover, unlike JPEG, AVIF does not support progressive rendering. For a typical product detail page, progressive rendering might provide a killer feature to improve key metrics like Largest Contentful Paint and other Core Web Vitals metrics. Even if a JPEG takes a little bit longer time to download due to its larger file size compared to AVIF, chances are that it will start rendering sooner than an AVIF thanks to its progressive rendering mechanism. This case is well illustrated by this video from Jake Achibald.

3. JPEG 2000 is Giving AVIF Tough Competition

The key selling point of AVIF is its extremely low file size relative to an acceptable visual image quality. Early blogs and reports have been focusing on this. However, JPEG2000 (or JP2) may in some cases be a better tool for the job. JPEG2000 is a relatively old file format and does not get the same level of attention as AVIF, even if the Apple side of the universe already supports JPEG2000.

To illustrate, let’s look at this adorable puppy. The AVIF file size optimized by squoosh.app is 27.9 KB with default settings. Converting the image to JPEG2000, again using ImageEngine, the file size is 26.7 KB. Not much difference, but enough to illustrate the case.

What about the visual quality? DSSIM is a popular way to compare how visually similar an image is to the original image. The DSSIM metric compares the original image to a converted file, with a lower value indicating better quality. Losslessly converting the AVIF and JPEG2000 version to PNG, the DSSIM score is like this:

DSSIM (0 = equal to original) Bytes
JPEG2000 0.019 26.7 KB
AVIF 0.012 27.9 KB

AVIF has slightly better DSSIM but hardly visible to the human eye.

Right Tool for the Job

The key takeaway from this article is that AVIF is hardly the “silver bullet,” or the one image format to rule them all. First of all, it is still very early in the development of both encoders and decoders. In addition, AVIF is yet another format to manage. Like Jake Archibald also concludes in his article, offering 3+ versions of each image on your webpage is a bit of a pain unless the entire workflow (resize, compress, convert, select, deliver) is all automated.

Also, like we’ve seen, just because a browser supports AVIF, it doesn’t mean that it is the best choice for your users.

Using responsive images and adding AVIF to the list of image formats to pre-create is better than not considering AVIF at all. A potential challenge is that the browser will then pick AVIF if it’s supported regardless of whether AVIF is the right tool or not.

However, using an image CDN like ImageEngine, will to a greater extent be able to dynamically choose between supported formats and make a qualified guess whether WEBP, JPEG2000 or AVIF will give the best user experience. Using an image CDN to automate the image optimization process will take into account browser compatibility, image payload size and visual quality.





Source link