Entwicklung browserübergreifender Erweiterungen

    In meinem letzten Artikel habe ich die Veröffentlichung einer Browser-Erweiterung für Google Chrome erwähnt, mit der die Sucheffizienz gesteigert werden kann, indem relevante Informationen aus Artikeln in sozialen Netzwerken bereitgestellt werden, die Ihnen gefallen.

    Heute unterstützen wir 3 Hauptbrowser: Chrome, Firefox und Safari. Trotz der unterschiedlichen Plattformen werden alle aus einer Codebasis zusammengesetzt. Ich werde Ihnen erzählen, wie dies geschehen ist und wie ich mein Leben durch die Entwicklung von Browser-Erweiterungen vereinfachen kann.

    Zu Beginn der Reise


    Alles begann damit, dass ich eine einfache Erweiterung für Chrome erstellt habe. Ich stelle übrigens fest, dass sich die Entwicklung für Chrome als die angenehmste und bequemste erwiesen hat. Insbesondere nach dem lokalen Debuggen habe ich den Inhalt der Erweiterung in .zipden Web Store gepackt und in diesen hochgeladen , ohne mich um die Automatisierung zu kümmern.

    Die Erweiterung wurde von unserer Zielgruppe gut angepasst. Laut Kennzahlen und Nutzerbewertungen ist dies genau das, was wir brauchen. Und da 15% unseres Traffics von Firefox kommt, sollte es der nächste sein.

    Das Wesentliche aller Browsererweiterungen ist dasselbe - dies sind HTML / CSS / JS-Anwendungen mit einer eigenen Manifestdatei, die die Eigenschaften und den Inhalt sowie den Quellcode selbst beschreibt. Daher lautete meine Hauptidee wie folgt: Ich kopiere das Erweiterungs-Repository für Chrome und passe es für Firefox an.

    Während der Arbeit verspürte ich jedoch ein Gefühl der "Schuld" für das Kopieren und Einfügen, das vielen Programmierern vertraut war. Es war offensichtlich, dass 99% des Codes zwischen den Erweiterungen wiederverwendet wurden und die Aussicht auf eine erweiterte Funktionalität, die verschiedene Zweige unterstützt, zu einem Problem werden könnte.

    Es kam vor, dass ich eine ausgezeichnete Octotree- Erweiterung bemerkte (ich empfehle sie allen, die GitHub aktiv nutzen), ich bemerkte einen Fehler darin und beschloss, ihn zu beheben. Als ich das Repository geklont habe und anfing, mich mit dem Inhalt zu befassen, fand ich eine interessante Funktion - alle 3 Octotree-Erweiterungen werden von einem Repository gesammelt. Wie Likeastore ist Octotree eine einfache Inhaltsinjektion und deshalb war ihr Modell auch für mich großartig.

    Ich habe den Erstellungsprozess in Octotree für mein Projekt angepasst und verbessert (der Fehler wurde übrigens auch behoben ) , um zu sehen, was passiert ist.

    Anwendungsstruktur


    Ich werde eine Antragsstruktur vorschlagen, die meiner Meinung nach für Erweiterungen geeignet ist.

    Bild

    build , dist - automatisch generierte Ordner, in die der Quellcode der Erweiterungen bzw. die verteilungsfertige Anwendung passen.

    css , img , js - Quellcode der Erweiterung.

    Hersteller - plattformspezifischer Code, für jeden Browser ein eigener Ordner.

    Werkzeuge - Werkzeuge, die für die Montage benötigt werden.

    Alles wird von gulp gesammelt, dem „umgedachten“ Sammlerprojekt für node.js. Und selbst wenn Sie den Knoten nicht in der Produktion verwenden, empfehle ich dringend, ihn auf Ihrem Computer zu installieren. In der npm-Galaxie werden jetzt viele nützliche Dinge angezeigt.

    Plattformabhängiger Code


    Beginnen wir mit dem Wichtigsten: Wenn Sie ein neues Projekt starten oder ein vorhandenes anpassen möchten, müssen Sie klar verstehen, welche plattformspezifischen Aufrufe erforderlich sind, und das separate Modul hervorheben.

    In meinem Fall gab es nur einen solchen Aufruf - das Abrufen der URL zu der Ressource in der Erweiterung (in meinem Fall zu den Bildern). Daher wurde eine separate Datei, browser.js, hervorgehoben.

    ;(function (window) {
    	var app = window.app = window.app || {};
    	app.browser = {
    		name: 'Chrome',
    		getUrl: function (url) {
    			return chrome.extension.getURL(url);
    		}
    	};
    })(window);
    

    Relevante Versionen für Firefox und Safari .

    In komplexeren Fällen wird browser.js erweitert, um alle erforderlichen Aufrufe zu erfüllen und eine Fassade zwischen Ihrem Code und dem Browser zu bilden.

    Bild

    Neben der Fassade enthält der plattformspezifische Code Manifeste und Erweiterungseinstellungen. Für Chome ist dies manifest.jsonFirefox main.js+ package.jsonund schließlich Safari, das in der alten Art und Weise .plist-Dateien verwendet - Info.plist, Settings.plist, Update.plist.

    Automatisieren Sie das Bauen mit Schluck


    Die Aufgabe der Assembly besteht darin, die Quellcodedateien des Erweiterungs- und plattformspezifischen Codes in Ordner zu kopieren, deren Struktur vom Browser selbst vorgegeben wird.

    Dazu erstellen Sie 3 Schluckaufgabe,

    var gulp     = require('gulp');
    var clean    = require('gulp-clean');
    var es       = require('event-stream');
    var rseq     = require('gulp-run-sequence');
    var zip      = require('gulp-zip');
    var shell    = require('gulp-shell');
    var chrome   = require('./vendor/chrome/manifest');
    var firefox  = require('./vendor/firefox/package');
    function pipe(src, transforms, dest) {
    	if (typeof transforms === 'string') {
    		dest = transforms;
    		transforms = null;
    	}
    	var stream = gulp.src(src);
    	transforms && transforms.forEach(function(transform) {
    		stream = stream.pipe(transform);
    	});
    	if (dest) {
    		stream = stream.pipe(gulp.dest(dest));
    	}
    	return stream;
    }
    gulp.task('clean', function() {
    	return pipe('./build', [clean()]);
    });
    gulp.task('chrome', function() {
    	return es.merge(
    		pipe('./libs/**/*', './build/chrome/libs'),
    		pipe('./img/**/*', './build/chrome/img'),
    		pipe('./js/**/*', './build/chrome/js'),
    		pipe('./css/**/*', './build/chrome/css'),
    		pipe('./vendor/chrome/browser.js', './build/chrome/js'),
    		pipe('./vendor/chrome/manifest.json', './build/chrome/')
    	);
    });
    gulp.task('firefox', function() {
    	return es.merge(
    		pipe('./libs/**/*', './build/firefox/data/libs'),
    		pipe('./img/**/*', './build/firefox/data/img'),
    		pipe('./js/**/*', './build/firefox/data/js'),
    		pipe('./css/**/*', './build/firefox/data/css'),
    		pipe('./vendor/firefox/browser.js', './build/firefox/data/js'),
    		pipe('./vendor/firefox/main.js', './build/firefox/data'),
    		pipe('./vendor/firefox/package.json', './build/firefox/')
    	);
    });
    gulp.task('safari', function() {
    	return es.merge(
    		pipe('./libs/**/*', './build/safari/likeastore.safariextension/libs'),
    		pipe('./img/**/*', './build/safari/likeastore.safariextension/img'),
    		pipe('./js/**/*', './build/safari/likeastore.safariextension/js'),
    		pipe('./css/**/*', './build/safari/likeastore.safariextension/css'),
    		pipe('./vendor/safari/browser.js', './build/safari/likeastore.safariextension/js'),
    		pipe('./vendor/safari/Info.plist', './build/safari/likeastore.safariextension'),
    		pipe('./vendor/safari/Settings.plist', './build/safari/likeastore.safariextension')
    	);
    });
    

    Die Standardaufgabe, die alle drei Erweiterungen sammelt,

    gulp.task('default', function(cb) {
    	return rseq('clean', ['chrome', 'firefox', 'safari'], cb);
    });
    

    Außerdem ist es für die Entwicklung sehr praktisch, wenn sich der Code ändert und gleichzeitig die Montage automatisch durchgeführt wird.

    gulp.task('watch', function() {
    	gulp.watch(['./js/**/*', './css/**/*', './vendor/**/*', './img/**/*'], ['default']);
    });
    

    Die Erweiterung für die Verteilung vorbereiten


    Aber die Assembly selbst ist nicht alles, ich möchte in der Lage sein, die Anwendung in ein Format zu packen, das für die Platzierung im entsprechenden App Store bereit ist )

    Bei Chrome müssen Sie lediglich ein .zipArchiv erstellen, das bereits auf der Seite des Chrome Web Store signiert und überprüft wurde.

    gulp.task('chrome-dist', function () {
    	gulp.src('./build/chrome/**/*')
    		.pipe(zip('chrome-extension-' + chrome.version + '.zip'))
    		.pipe(gulp.dest('./dist/chrome'));
    });
    

    Für Firefox ist es etwas komplizierter: Sie benötigen ein SDK mit dem Tool cfx, mit dem Sie die Erweiterung in eine xpiDatei „umschließen“ können .

    gulp.task('firefox-dist', shell.task([
    	'mkdir -p dist/firefox',
    	'cd ./build/firefox && ../../tools/addon-sdk-1.16/bin/cfx xpi --output-file=../../dist/firefox/firefox-extension-' + firefox.version + '.xpi > /dev/null',
    ]));
    

    Aber mit Safari ist es im Allgemeinen ein "Mist". Sie können die Anwendung nur im .safariextz-Paket in Safari selbst erstellen. Ich habe mehr als eine Stunde gebraucht, um die Anweisung zum Arbeiten zu bringen, aber alles umsonst. Leider ist es jetzt nicht möglich, Ihr Entwicklungszertifikat in ein .p12Format zu exportieren. Daher ist es nicht möglich, die erforderlichen Schlüssel zum Signieren eines Pakets zu erstellen. Safari muss noch manuell gepackt werden, die Verteilungstask wird vereinfacht, um die Update.plist-Datei zu kopieren.

    gulp.task('safari-dist', function () {
    	pipe('./vendor/safari/Update.plist', './dist/safari');
    });
    

    Zusammenfassend


    Der Entwicklungsprozess von einem Repository aus ist einfach und unterhaltsam. Wie bereits erwähnt, ist Chrome für mich die bequemste Entwicklungsumgebung. Alle Änderungen werden dort hinzugefügt und getestet.

    $ gulp watch
    

    Nachdem alles in Chrome funktioniert, überprüfen Sie Firefox

    $ gulp firefox-run
    

    Und auch im "manuellen" Modus in Safari.

    Wir beschließen, eine neue Version zu veröffentlichen, die entsprechenden Manifestdateien mit der neuen Version zu aktualisieren und auszuführen

    $ gulp dist
    

    Bild

    Als Ergebnis im Ordner / dist, welche Dateien verteilt werden. Es wäre ideal, wenn der App Store eine API hätte, über die Sie die neue Version hochladen können, aber vorerst müssen Sie dies von Hand tun. Alle Details bitte hier .

    Jetzt auch beliebt: