Webkrauts Logo

Webkrauts Webkrauts Schriftzug

- für mehr Qualität im Web

Alles im Blick – Teil 2

Workflow beschleunigen mit Browsersync

Alles im Blick – Teil 2

Viele Entwickler nutzen bereits etablierte Entwicklungsumgebungen, um diverse Aufgaben zu automatisieren. Egal, ob ihr Gulp oder Grunt bevorzugt, Browsersync lässt sich in beide Umgebungen integrieren.

Die grundsätzlichen Funktionen und Vorteile von Browsersync könnt ihr im ersten Teil nachlesen.

Browsersync + Gulp

Um Browsersync in Gulp zu integrieren, wird kein dezidiertes gulp-*-Plugin angeboten und/oder benötigt. Ihr bindet das Node.js-Modul Browsersync nach Installation des Moduls per require() im Gulpfile ein und sprecht es anschließend direkt über seine API an.

Zuerst installiert ihr Browsersync im aktuellen Projektordner (inkl. Eintrag in die package.json):

  1. $ npm install browser-sync --save-dev

Browsersync Task

  1. var gulp = require('gulp');
  2. var bs   = require('browser-sync').create();
  3.  
  4. /**
  5.  * Server-Modus
  6.  * 1. Berücksichtige alle Dateien
  7.  * in dem hier angegebenen Ordner
  8.  */
  9. gulp.task('browser-sync', function() {
  10.   bs.init({
  11.     server: {
  12.       baseDir: './' /* [1] */
  13.     }
  14.   });
  15. });
  16.  
  17. /**
  18.  * Proxy-Modus
  19.  * 1. Übergebe alle Anfragen an die
  20.  * laufende Serverinstanz/den vHost
  21.  */
  22. gulp.task('browser-sync', function() {
  23.   bs.init({
  24.     server: {
  25.       proxy: 'localhost <or> /pfad/zum/ordner/ <or> vhost-name.dev' /* [1] */
  26.     }
  27.   });
  28. });

Der Task, mit dem ihr Browsersync einbindet, unterscheidet sich im hier gezeigten, einfachen Beispiel nur durch eine Zeile: Im server-Modus weist ihr Browsersync an, einen eigenen, statischen Server zu starten. Im proxy-Modus wird ein schon existierender Server/vHost als Zieladresse definiert, an den alle Anfragen übergeben werden (s.u. Anwendungsbeispiele).

Eins nach dem anderen

In seiner aktuellen Version (3.9.x) führt Gulp Tasks immer parallel aus – die Reihenfolge der Verarbeitung ist nicht steuerbar. Die parallele Verarbeitung von Tasks ist oftmals erwünscht (Perfomance), erweist sich in einigen Anwendungsfällen aber als problematisch. So soll bspw. die Synchronisation (ob per Reload oder Injection) von Browsersync erst dann erfolgen, nachdem alle ggf. abhängigen Tasks ausgeführt und abgeschlossen sind (Stefan Baumgartner beschreibt die Problematik in seinem Artikel sehr anschaulich).

Mit Gulp 4.x wird es möglich sein festzulegen, welche Tasks parallel oder/und seriell ausgeführt werden sollen. Zum jetzigen Zeitpunkt füllt diese Lücke das NPM-Modul run-sequence. Der Einsatz dieses Moduls erlaubt es euch, ein einfach zu wartendes Gulpfile ohne verschachtelte oder/und voneinander abhängigen Tasks zu erstellen.

Hinweis: Alle folgenden Anwendungsbeispiele gehen davon aus, dass ihr das NPM Modul run-sequence installiert und eingebunden habt.

LibSASS + CSS Injection

  1. var gulp         = require('gulp');
  2. var sass         = require('gulp-sass');
  3. var bs           = require('browser-sync').create();
  4. var runSequence  = require('run-sequence');
  5.  
  6. gulp.task('browser-sync', function() {
  7.   bs.init({
  8.     server: {
  9.       baseDir: './'
  10.     }
  11.   });
  12. });
  13.  
  14. gulp.task('sass', function() {
  15.   return gulp.src('**/*.scss')
  16.     .pipe(sass())
  17.     .pipe(gulp.dest('./styles.css'))
  18.     .pipe(bs.stream());
  19. });
  20.  
  21. gulp.task('watch-all', function() {
  22.   gulp.watch('**/*.scss', ['sass']);
  23. });
  24.  
  25. gulp.task('default', function() {
  26.   runSequence(['sass','watch-all'], 'browser-sync');
  27. });

In diesem Anwendungsbeispiel wird SASS eingebunden. Bei einer Änderung im SCSS wird das Stylesheet neu kompiliert und ohne erneutes Laden des Browsers in die Website eingefügt (CSS Injection). Damit nur das jeweils neu kompilierte Stylesheet berücksichtigt wird, ist es wichtig, den relevanten .stream() nach gulp.dest aufzurufen (Zeile 18).

LibSASS + Sourcemaps + Autoprefixer + CSS Injection

  1. var gulp         = require('gulp');
  2. var sass         = require('gulp-sass');
  3. var sourcemaps   = require('gulp-sourcemaps');
  4. var autoprefixer = require('gulp-autoprefixer');
  5. var bs           = require('browser-sync').create();
  6. var runSequence  = require('run-sequence');
  7.  
  8. gulp.task('browser-sync', function() {
  9.   bs.init({
  10.     server: {
  11.       baseDir: './'
  12.     }
  13.   });
  14. });
  15.  
  16. gulp.task('sass', function() {
  17.   return gulp.src('**/*.scss')
  18.     .pipe(sourcemaps.init())
  19.     .pipe(sass())
  20.     .pipe(autoprefixer())
  21.     .pipe(sourcemaps.write('./'))
  22.     .pipe(gulp.dest('./styles.css'))
  23.     .pipe(bs.stream({match: '**/*.css'}));
  24. });
  25.  
  26. gulp.task('watch-all', function() {
  27.   gulp.watch('**/*.scss', ['sass']);
  28. });
  29.  
  30. gulp.task('default', function() {
  31.   runSequence(['sass','watch-all'], 'browser-sync');
  32. });

Dieses Anwendungsbeispiel zeigt ein durchaus übliches Setup, bestehend aus SASS inkl. Autoprefixer und Sourcemaps. Auch hier gilt: die Reihenfolge der per .pipe übergebenen Streams ist entscheidend. Sourcemaps (Suffix .map) werden im o.g. Beispiel im selben Verzeichnis erstellt, in dem das kompilierte Stylesheet seine Heimat hat. Nach Durchlauf des Streams (.pipe(gulp.dest('styles.css')), Zeile 22) befinden sich im Zielverzeichnis also zwei Dateien: styles.css und styles.css.map.

Eine CSS Injection ist für Browsersync nur dann möglich, wenn im Stream ausschließlich Dateien mit dem Suffix .css vorhanden sind. Jedes andere Dateiformat veranlasst Browsersync, den Browser neu zu laden. Mit der Filteroption match beschränkt ihr den Stream auf CSS-Dateien (Zeile 23), so dass der CSS Injection nichts im Weg steht.

LibSASS + CSS Injection + Reload bei HTML/JS Änderungen

  1. var gulp         = require('gulp');
  2. var sass         = require('gulp-sass');
  3. var bs           = require('browser-sync').create();
  4. var runSequence  = require('run-sequence');
  5.  
  6. gulp.task('browser-sync', function() {
  7.   bs.init({
  8.     server: {
  9.       baseDir: './'
  10.     }
  11.   });
  12. });
  13.  
  14. gulp.task('sass', function() {
  15.   return gulp.src('**/*.scss')
  16.     .pipe(sass())
  17.     .pipe(gulp.dest('./styles.css'))
  18.     .pipe(bs.stream());
  19. });
  20.  
  21. gulp.task('js', function() {
  22.   return gulp.src('**/*.js')
  23.     .pipe(gulp.dest('./base.js'));
  24. });
  25.  
  26. gulp.task('watch-all', function() {
  27.   gulp.watch('**/*.scss', ['sass']);
  28.   gulp.watch('**/*.js', ['js']).on('change', bs.reload);
  29.   gulp.watch('**/*.html').on('change', bs.reload);
  30. });
  31.  
  32. gulp.task('default', function() {
  33.   runSequence(['sass', 'js', 'watch-all'], 'browser-sync');
  34. });

CSS Injection ist u.a. für Styles eine prima Sache, bei Änderungen im Markup oder/und im Javascript, das ggf. Funktionalität enthält, ist ein Neu-Laden des Browserfensters fast immer sinnvoll. Hier kommt die Browsersync-API-Methode reload zum Einsatz, die ihr im Watch Task (watch-all) als Argument eines change-Events an die gewünschten Tasks anhängt (Zeile 28, 29).

Browsersync + PHP Server

  1. var gulp         = require('gulp');
  2. var bs           = require('browser-sync').create();
  3. var runSequence  = require('run-sequence');
  4. var connect      = require('gulp-connect-php');
  5.  
  6. gulp.task('php', function() {
  7.   php.server({
  8.     base: './app',
  9.     port: 8001,
  10.     }, function (){
  11.     bs.init({
  12.       proxy: 'localhost <or> /pfad/zum/ordner/ <or> vhost-name.dev'
  13.     });
  14.   });
  15. });
  16.  
  17. gulp.task('watch-all', function() {
  18.   gulp.watch('**/*.php').on('change', bs.reload);
  19. });
  20.  
  21. gulp.task('default', function() {
  22.   runSequence(['watch-all'], 'php');
  23. });

Viele CMS-Systeme basieren auf PHP und benötigen somit einen laufenden PHP-Server. Ab Version 5.4 ist in PHP ein abgespeckter Build-In-Server enthalten. Mit dem Gulp-Plugin gulp-connect-php könnt ihr diesen ansprechen, respektive starten. Browsersync wird hier als Funktion in die Server-Konfiguration des Plugins eingebunden. Alle Anfragen werden an den Port der PHP-Instanz durchgereicht (im Beispiel: 8001, Zeile 9). Analog zu statischem Markup, ist es bei Änderungen in PHP-Dateien sinnvoll, die Seiten automatisch neu zu laden (Zeile 18).

Tipp: Oftmals habt ihr beides im Browser geöffnet: die Frontend-Ausgabe und das Admin-Interface des CMS. Damit es bei letzteren nicht zu ungewollten Reloads kommt, entfernt ihr einfach den Port aus der URL, auf dem Browsersync lauscht (Standard: 3000).

Browsersync + Grunt

Für Grunt stellen die Entwickler von Browsersync ein offizielles Plugin bereit, das als Wrapper für das API von Browsersync dient. Sämtliche Optionen zur Konfiguration, die Browsersync bietet, können auch mit Einsatz des Plugins genutzt werden.

  1. $ npm install grunt-browser-sync --save-dev

Analog zu Gulp gab es auch im Zusammenspiel von Grunt + Browsersync + Watch Tasks einige Stolpersteine, die in einem (etwas älteren) Artikel ausführlich beschrieben sind. Mit der aktuellen Major-Version von Browsersync, die seit Anfang diesen Jahres verfügbar ist, haben die Entwickler der API eine Option hinzugefügt, die dieses Problem löst.

Anwendungsbeispiele bringen das Fleisch an den Knochen: Neben der Browsersync-Dokumentation zu Grunt ist der Artikel von Stefan Baumgartner zur Kombination Grunt+PHP-Server empfehlenswert. Dass ihr Browsersync auch ohne das offizielle Plugin in Grunt nutzen könnt, zeigt euch der Artikel von Rui Molefas. Einen informativen Überblick zur Kombination von Grunt + Browsersync liefert dieser Artikel.

Die Kommentare sind geschlossen.