VRath – z Reactem w parze cz. 2

Marzec już za pasem a mój projekt w dalszym ciągu nie ma solidnych fundamentów. W tym wpisie ostatecznie rozprawię się z kwestią frameworków aby wreszcie móc pobawić się technologią VR.

W pierwszej części postu zakończyłem wstępną konfigurację webpacka i udało mi się doprowadzić do sytuacji w której mój kod pisany w reakcie i ES6, jest transformowany do VanillaJS w standardzie ES5. Może to nie dużo, ale dla laika brzmi kosmicznie nieprawdaż? Pora sprawdzić jak React poradzi sobie z A-Frame’m.



Pierwszy komponent

Nie wiem jak Wy, ale ja najlepiej wiedzę przyswajam poprzez praktykę, dlatego też bez zbędnego wstępu rozpoczynam zabawę z pierwszym komponentem, który będzie sceną. Na początek utworzę folder components w katalogu js, który będzie przechowywał wszystkie komponenty aplikacji. Pozbywam się też testowego pliku modules.js, który miał na celu sprawdzenie czy wynikowa paczka jest poprawnie transformowana. Poprawiam także webpack.config.js, ponieważ nie uwzględniłem w nim reguły do parsowania plików z rozszerzeniem .jsx, a będę je stosował do plików komponentów. Fragment rules będzie wyglądał następująco:

rules: [{
	test: /\.(js|jsx)$/,
	exclude: [/node_modules/],
	use: [{
		loader: "babel-loader",
		options: {
			presets: [
				["env", {
					"targets": {
						"browsers": ["last 2 versions", "ie >= 11", "Android >= 5"]
					}
				}],
				["react"]
			] 
		}
	}]
}]

Czas na komponent sceny. Nazwę go scene.component.jsx i podobny schemat zachowam dla przyszłych komponentów. O to jego zawartość:

import AFrame from "aframe";
import React from "react";

const SceneComponent = () => (
    <a-scene>
    </a-scene>
);

export default SceneComponent;

Wykorzystałem tzw. stateless function, czyli funkcję bezstanową do zwrócenia pustego komponentu sceny. Na uwagę zasługuje linijka import AFrame from "aframe";, która jest wymagana, ponieważ A-Frame musi zostać wczytany przed użyciem komponentu (zaleceniem jest umieszczenie skryptu w sekcji <head>, ale to na razie pomijam). Jeszcze tylko drobna modyfikacja pliku app.js pod kątem nazewnictwa i jesteśmy w domu:

import React from "react";
import ReactDOM from "react-dom";
import SceneComponent from "./components/scene.component.jsx";

ReactDOM.render(<SceneComponent />, document.getElementById("vrath-scene"));

A-Frame „kompiluje” znacznik <a-scene> do canvasu, a więc do uruchomienia przykładu będę potrzebował serwera. Na szczęście także i w tym przypadku mogę liczyć na webpacka. Twórcy tego narzędzia przygotowali pakiet webpack-dev-server, który uruchamia serwer deweloperski i jednocześnie „śledzi” zmiany w projekcie, dzięki czemu jest w stanie pokazać zmiany bez odświeżania strony. Dodam więc go do projektu:

yarn add webpack-dev-server --dev

Następnie muszę zmodyfikować package.json o wpis, który pozwoli mi uruchomić serwer. W sekcji scripts uzupełniam o skrypt server:

"scripts": {
    "dev": "webpack --config webpack.config.js",
    "production": "webpack -p --config webpack.config.js",
    "server": "webpack-dev-server --config webpack.config.js --open"
}

Teraz wystarczy uruchomić npm run server aby cieszyć się serwerem webpacka. Po wpisaniu w przeglądarce adresu http://localhost:8080 widać, że element sceny został dołączony do drzewa DOM.

Więcej komponentów!

Zdecydowanie za mało atrakcyjny to przykład, dlatego uzupełnię go o elementy z wpisu wstępnego o A-Frame, czyli o kamerę i obiekt kuli. I będzie to pełnopoprawny komponent React’a:

import AFrame from "aframe";
import React from "react";

const SceneComponent = () => (
    <a-scene>
    	<a-sphere radius="100" color="#258bd6"></a-sphere>
        <a-camera position="0 0 500"></a-camera>
    </a-scene>
);

export default SceneComponent;

Niebieska kula jak się patrzy! Nie chciałbym jednak aby cały projekt był w jednym pliku, więc pora na mały refaktor. Wydzielę komponent kuli i kamery do osobnych plików:

sphere.component.jsx

import React from "react";

export default () => <a-sphere radius="100" color="#258bd6"></a-sphere>;

camera.component.jsx

import React from "react";

export default () => <a-camera position="0 0 500"></a-camera>;

Z komponentu sceny wynoszę też importowanie A-Frame do app.js, ponieważ nie ma takiej potrzeby aby robić inicjalizację tego frameworka w każdym komponencie (na pewno zbyt mądre by to nie było). Natomiast sama plik sceny wygląda teraz tak:

import React from "react";
import SphereComponent from "./sphere.component.jsx";
import CameraComponent from "./camera.component.jsx";

const SceneComponent = () => (
    <a-scene>
    	<SphereComponent />
    	<CameraComponent />
    </a-scene>
);

export default SceneComponent;

Zarówno kula jak i kamera mają zaszyte właściwości (ang. properties) bezpośrednio w komponentach, co nie jest zbyt elastycznym rozwiązaniem, ponieważ nie jesteśmy w stanie tym sterować. Trzeba się tym zająć!

CDN…

VRath – z Reactem w parze cz. 3