Bisher habe ich fast auschliesslich im sogenannten backend gearbeitet. Es handelt sich dabei um den Teil einer Webseite, der für Endbenutzer nicht sichtbar ist: also die Datenbank, die business logic und so weiter. In meinem Betrieb soll ich aber in Zukunft an allen Bereichen der Webseite arbeiten können. Deshalb habe ich mich ein wenig in das front end development vertieft, also das Programmieren jenes Teils der Webseite, der von Endbenutzern tatsächlich gesehen und direkt verwendet wird. Es gibt verschiedene Framworks und Techniken, um ein Frontend zu entwickeln. Wir haben uns allerdings auf react.js konzentriert.
react.js ist eine Frontend-Library, die von Facebook und weiteren entwickelt wird. Es basiert auf JavaScript und ermöglicht. Im weitesten Sinne ist es dafür verantwortlich, die Benutzeroberfläche zu rendern. Dafür bietet es eine ganze Reihe an Funktionen, wie etwa die automatische Generierung von HTML und CSS auf Grundlage von JavaScript-Code, state management, die Kapselung von Komponenten und einiges mehr. Darüber hinaus lässt es sich mit anderen Frameworks wie z.B. bootstrap verwenden.
Im Folgenden wird anhand einer einfachen To Do-Applikation aufgezeigt, welche Schritte notwendig sind, um den Server bzw. den Container zu konfigurieren und eine funktionierende Applikation zu schreiben.
name: 'todo',
entry: {
main: Path.resolve(__dirname, 'app/client/src/todo/bundle.jsx')
},
output: {
path: Path.resolve(__dirname, 'app/client/dist/todo'),
filename: 'bundle.js',
},
devtool: (ENV !== 'production') ? 'source-map' : '',
resolve: {
...resolves(ENV, PATHS),
alias: {
...resolves(ENV, PATHS).alias,
// We resolve root fo quick access to scss
'Root': Path.resolve(__dirname, 'app/client/src/react/'),
},
extensions: [
...resolves(ENV, PATHS).extensions,
'.json', '.js', '.jsx', '.gql', '.graphql'
],
},
module: {
...modules(ENV, PATHS),
rules: [
...modules(ENV, PATHS).rules,
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader',
}
]
},
plugins: plugins(ENV, PATHS),
Es wird für Silverstripe ein Controller TodoController angelegt, der sicherstellt, dass eine gewisse URL mit einem passenden Template gerendert wird, welche unseren Code einbindet. Ebenfalls erstellen bzw. anpassen müssen wir also das Routing in routes.yml sowie ein Template, hier TaskPage.ss.
Das Ergebnis ist, dass beim Aufrufen der Seite localhost/todo der TaskController das Template rendert. Das von Webpack generierte bundle.js wird vom Browser dann heruntergeladen und ausgeführt. Ausserdem generieren wir im TaskController ein CSRF-Token, das via TaskPage.ss an React weitergegeben wird. Dieses ist ein wichtiges Sicherheitsfeature, doch sprengt es den Rahmen, seine Funktion hier zu erklären. Im Template stecken wir die Applikation in einen Container und zentrieren sie horizontal.
Nach all diesen Schritten können wir uns der Applikation in /app/client/src zuwenden.
// TaskController.php
namespace {
use SilverStripe\Control\Controller;
use SilverStripe\View\Requirements;
use SilverStripe\Security\SecurityToken;
/**
* @config
* @var array
*/
class TaskController extends Controller
{
private static $allowed_actions = [
'index' => true
];
public function getSecurityID() {
return SecurityToken::inst()->getValue();
}
/**
* index - default action; render with TaskPage.ss template
*
* @return DBHTMLText the render-able page
*/
public function index() {
Requirements::themedCSS('client/dist/global/bundle.css');
return $this->renderWith('TaskPage');
}
}
}
# routes.yml
SilverStripe\Control\Director:
rules:
'todo/': 'TaskController'
<!-- TaskPage.ss -->
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="root" class="container-fluid" style="justify-content: center" data-secid="$getSecurityID">
<% require themedJavascript("client/dist/todo/bundle.js") %>
</div>
</body>
</html>
Nach unserer Konfiguration befindet sich die Applikation im Ordner /app/client/src/react. Hierhin gehören noch einige Konfigurationsdateien für den Linter (.stylelintrc und .eslintrc), auf die ich hier nicht eingehe, sowie ein leeres bundle.scss - nach Konfiguration ist diese Datei erforderlich; wir möchten aber nicht, dass sie irgendwelche Auswirkungen hat.
Ebenfalls im Ordner befindet sich bundle.jsx und der Ordner components. bundle.jsx dient uns als Einstieg in die Applikation: Hier wird React in das DOM eingehängt, nämlich unter dem <div> mit der id root, das wir im Template definiert hatten. Von diesem Punkt abwärts ist React für die Verwaltung des DOM zuständig. Ausserdem importieren wir App.jsx, das sich im components-Ordner befindet, und rendern es:
// bundle.jsx
import './bundle.scss';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './components/App';
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <App />;
root.render(element);