Transpiler / compiler Babel

Najważniejszą ideą w technologii react jest możliwość tworzenia własnych znaczników (Tags) rozszerzających HTML. Osiągnięto to poprzez przetwarzanie takiego rozszerzonego kodu w JavaScript. Stosuje się w tym celu "transpiler" Babel, który został stworzony aby tłumaczyć standard JavaScript ES6 na wcześniejsze standardy (ES5), akceptowane przez przeglądarki. Ponieważ Babel obsługuje rozszerzenia (wtyczki) - nic prostszego, jak dodać wtyczki obsługujące rozszerzenia React.

Babel nie jest częścią projektu React, ale jest przez niego wykorzystywany.

Przykład:

Plik: helloworld.jsx

class HelloWorld extends React.Component {
  render() {
    return <h1>Hello World</h1>
  }
}
ReactDOM.render(
<HelloWorld />
, document.getElementById('example00')
);

Definiuje on nowy znacznik HelloWorld. Każdy taki znacznik odpowiada obiektowi zbudowanemu na bazie klasy React.Component.

Funkcja render() zwraca (return) rozszerzony kod html (musi być zawarty w obrębie jednego znacznika - tu h1).

Oczywiście można wykorzystywać wcześniej zdefiniowane znaczniki.

Funkcja ReactDOM.render wstawia wygenerowany znacznik <HelloWorld /> do kodu na stronie html.

Wykorzystany w tym celu zostanjeie element example00.

Przykład strony z takim elementem (index.html):

<!DOCTYPE html>
<html>
<head>
  <script src="node_modules/react/dist/react.js"></script>
  <script src="node_modules/react-dom/dist/react-dom.js"></script>
</head>
<body>
  <div id="example00"></div>
  <script src="helloworld.js"></script>
</body>
</html>

Trzeba zwrócić uwagę, że do strony dołączono plik helloworld.js, a nie helloworld.jsx.

Czyli plik po przetworzeniu kompilatorem babel.

Jak to się robi?

1) Najpierw musimy zainstalować kompilator i odpowiednie biblioteki rReacta.

Działają one w środowisku NodeJs.

# pusty package.json:
npm init
# react:
npm install --save-dev react
npm install --save-dev react-dom
# kompilator babel:
npm install --save-dev babel-cli babel-preset-es2015

2) Teraz musimy zainstalować wtyczki do kompilatora [Uwaga! Można ten zapis skrócić do npm i ….]:

# + pluginy:
npm install babel-plugin-transform-react-jsx

3) Tworzymy plik .babelrc

Zawiera on informację co ma być tłumaczone:

{  "presets" : [["es2015", "react"]] }

4) Uruchamiamy kompilator:

# windows: 
.\node_modules\.bin\babel --plugins transform-react-jsx index.jsx >  helloworld.js
# linux:
./node_modules/.bin/babel --plugins transform-react-jsx helloworld.jsx > helloworld.js

5) Testujemy otwierając w przeglądarce plik index.html

Babel możemy dołączyć w postaci biblioteki do naszej strony – wtedy można na stronie używać JSX.

Bibliotekę możemy pobrać na przykład ze stron CDN.

https://cdnjs.com/libraries/babel-core

Przykład:

<!DOCTYPE html>
<html>
<head>
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
</script>
</body>
</html>

Babel i Gulp

Gulp to popularny program do automatyzacji praz z projektami internetowymi. Wykorzystuje on ideę potoków (pipe). Kolejne etapy (elementy potoku) przetwarzają otrzymane dane i przekazuj ą dalej (tak jak w potokach systeów operacyjnych).

Z potoków implementuje się funkcję przetwarzania danych, używając języka JavaScript (działa to w środowisku NodeJS). Implementację tą zapisujemy w pliku gulpfile.js.

Przykład:

const { src, dest, parallel } = require('gulp');
const babel = require('gulp-babel');
const browserify = require('gulp-browserify');

const babelOptions = {
    presets: [
      "@babel/preset-env",
      "@babel/preset-react"
    ],
    plugins: [
      "@babel/plugin-transform-object-assign",
      "@babel/plugin-transform-react-jsx"
    ]
}


function build() {
  return src('./src/index.jsx')
    .pipe(babel(babelOptions))
    .pipe(browserify({
          insertGlobals : true,
          debug : true
        }))
    .pipe(dest('./public'));
}


exports.build=build;

W tym przykładzie mamy potok inicjowany przez odczyt pliku jsx (src). Następnie wywołujemy babel (tłumaczenie z JSX na JS) i browserify (ze środowiska NodeJS na środowisko przeglądarki). Na końcu dokonujemy zapisu do katalogu public (dest).

Aby to zadziałało, musimy zainstalować użyte paczki. Przykładowa konfiguracja aplikacji (fragment package.json):

  "scripts": {
    "build": "gulp build"
  },
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0"
  },
  "devDependencies": {
    "@babel/core": "^7.5.5",
    "@babel/preset-env": "^7.5.5",
    "@babel/preset-react": "^7.0.0",
    "@babel/plugin-transform-object-assign": "^7.2.0",
    "@babel/plugin-transform-react-jsx": "^7.3.0",
    "gulp": "^4.0.2",
    "gulp-babel": "^8.0.0",
    "gulp-browserify": "^0.5.1"
  },

Poza zdefiniowanymi paczkami, mamy tu definicję skryptu build.

Aby zbudować aplikację wykonujemy:

yarn install
yarn build

webpack

Użyty w poprzednim przykładzie pakiet browserify umożliwia uruchamianie programów NodeJS w przeglądarce poprzez dodanie przeglądarkowych odpowiedników brakujących funkcji. Nieco inną filozofię zastosowano w pakiecie webpack [http://blog.namangoel.com/browserify-vs-webpack-js-drama\]. Te identyfikatory zastosowane w NodeJS mogą po przetworzeniu przez webpack mieć odmienne znaczenie. Dzięki temu aplikacje są zdecydowanie mniejsze. Dla prostego komponentu React przy użyciu webpack uzyskujemy nawet 4 razy mniejszy kod niż po zastosowaniu browserify i uglify (kompresja).

Powyższy przykłąd z wykorzystaniem webpack będzie wyglądał następująco:


const { src, dest, parallel } = require('gulp');
const babel = require('gulp-babel');

const webpack = require('webpack');
const webpackStream = require('webpack-stream');

// zakładamy pomocniczy katalog tmp na wynik tłumaczenia babel

const webpackConfig = { // minimalna konfiguracja
  entry: './tmp/index.js',
  output: {
    filename: 'index.js'
  },
  mode: 'production'  // lub:  mode: 'development'
};

const babelOptions = {
    presets: [
      "@babel/preset-env",
      "@babel/preset-react"
    ],
    plugins: [
      "@babel/plugin-transform-object-assign",
      "@babel/plugin-transform-react-jsx"
    ]
}


function build() {
  return src('./src/index.jsx')
    .pipe(babel(babelOptions)).pipe(dest('./tmp'))
    .pipe(webpackStream(webpackConfig), webpack)
    .pipe(dest('./public'))
}


exports.build=build;

react-scripts

Skrypt react-scripts jest częścią create-react-app - choć można go instalować niezależnie.

Używając create-react-app możesz zapomnieć o webpack, gulp etc.... ;).

Minimalna konfiguracja projektu ztym skryptem:

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build"
  },
  "dependencies": {
    "react": "^16.7.0",
    "react-dom": "^16.7.0"
  },
  "devDependencies": {
    "react-scripts": "^3.1.1",
    "yarn": "^1.16.0"
  }

Przy powyższej konfiguracji wystarczy wykonać:

yarn install
yarn build

Na podstawie dwóch plików: src/index.jsx oraz public/index.html, zostanie w katalogu build utworzona strona statyczna wykorzystjąca komponenty React. Najprostszy przykład:

src/index.jsx:

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(<div>Hello World</div>, document.getElementById('root'));

public/index.html

<html>
<body>
  <div id="root"></div>
</body>
</html>