Skip to content

Latest commit

 

History

History
446 lines (356 loc) · 23.3 KB

README-de-de.md

File metadata and controls

446 lines (356 loc) · 23.3 KB

Einleitung

Das Ziel dieses Style Guides ist, eine Sammlung von Best Practices und Gestaltungsrichtlinien für AngularJS-Anwendungen aufzuzeigen. Sie wurden aus den folgenden Quellen zusammengestellt:

  1. AngularJS-Quelltext
  2. Quelltexte oder Artikel, die ich gelesen habe
  3. Meine eigene Erfahrung

Hinweis 1: Hierbei handelt es sich noch um einen Entwurf des Style Guides, dessen vorrangiges Ziel es ist, gemeinschaftlich von der Community entwickelt zu werden. Die gesamte Community wird es daher begrüßen, wenn Lücken gefüllt werden. Hinweis 2: Bevor du den Richtlinien in einer der Übersetzungen des englischsprachigen Dokuments folgst, vergewissere dich, dass diese aktuell sind. Die jüngste Version des AngularJS Style Guide ist im Dokument README.md.

Du wirst in diesem Style Guide keine allgemeinen Richtlinien für die JavaScript-Entwicklung finden. Solche finden sich unter:

  1. Googles JavaScript-Style-Guide
  2. Mozillas JavaScript-Style-Guide
  3. Douglas Crockfords JavaScript-Style-Guide
  4. Airbnb JavaScript-Style-Guide

Für die AngularJS-Entwicklung ist Googles JavaScript-Style-Guide empfehlenswert.

Im GitHub-Wiki von AngularJS gibt es einen ähnlichen Abschnitt von ProLoser, den du dir hier ansehen kannst.

Inhaltsverzeichnis

Allgemein

Verzeichnisstruktur

Da eine große AngularJS-Anwendung viele Komponenten hat, sollten diese mit Hilfe einer Verzeichnishierarchie strukturiert werden. Es gibt zwei Basis-Herangehensweisen:

  • Auf einer oberen Ebene eine Aufteilung nach Art der Komponenten und auf einer tieferen Ebene eine Aufteilung nach Funktionalität.

Die Verzeichnisstruktur wird in diesem Fall folgendermaßen aussehen:

.
├── app
│   ├── app.js
│   ├── controllers
│   │   ├── page1
│   │   │   ├── FirstCtrl.js
│   │   │   └── SecondCtrl.js
│   │   └── page2
│   │       └── ThirdCtrl.js
│   ├── directives
│   │   ├── page1
│   │   │   └── directive1.js
│   │   └── page2
│   │       ├── directive2.js
│   │       └── directive3.js
│   ├── filters
│   │   ├── page1
│   │   └── page2
│   └── services
│       ├── CommonService.js
│       ├── cache
│       │   ├── Cache1.js
│       │   └── Cache2.js
│       └── models
│           ├── Model1.js
│           └── Model2.js
├── partials
├── lib
└── test
  • Auf einer oberen Ebene eine Aufteilung nach Funktionalität und auf einer tieferen Ebene eine Aufteilung nach Art der Komponenten.

Hier ist das entsprechende Layout:

.
├── app
│   ├── app.js
│   ├── common
│   │   ├── controllers
│   │   ├── directives
│   │   ├── filters
│   │   └── services
│   ├── page1
│   │   ├── controllers
│   │   │   ├── FirstCtrl.js
│   │   │   └── SecondCtrl.js
│   │   ├── directives
│   │   │   └── directive1.js
│   │   ├── filters
│   │   │   ├── filter1.js
│   │   │   └── filter2.js
│   │   └── services
│   │       ├── service1.js
│   │       └── service2.js
│   └── page2
│       ├── controllers
│       │   └── ThirdCtrl.js
│       ├── directives
│       │   ├── directive2.js
│       │   └── directive3.js
│       ├── filters
│       │   └── filter3.js
│       └── services
│           └── service3.js
├── partials
├── lib
└── test
  • Wenn eine Direktive erstellt wird, kann es sinnvoll sein, alle der Direktive zugehörigen Dateien (d. h. Templates, CSS/SASS-Dateien, JavaScript) in das selbe Verzeichnis zu legen. Wenn du dich für diesen Stil entscheidest, setze ihn konsequent im gesamten Projekt um.
app
└── directives
    ├── directive1
    │   ├── directive1.html
    │   ├── directive1.js
    │   └── directive1.sass
    └── directive2
        ├── directive2.html
        ├── directive2.js
            └── directive2.sass

Dieser Ansatz kann mit beiden der oben genannten Verzeichnisstrukturen kombiniert werden.

  • Eine weitere kleine Variation der beiden Verzeichnisstrukturen wird in ng-boilerplate eingesetzt. In dieser liegen die Unit Tests zu einer Komponente direkt im Verzeichnis der jeweiligen Komponente. Werden Änderungen an einer Komponente vorgenommen, ist es auf diese Weise einfacher, ihre Tests zu finden. Gleichzeitig dienen die Tests als Dokumentation und zeigen Anwendungsfälle auf.
services
├── cache
│   ├── cache1.js
│   └── cache1.spec.js
└── models
    ├── model1.js
    └── model1.spec.js
  • Die Datei app.js enthält die Routendefinitionen, die Konfiguration und/oder das manuelle Bootstrapping (falls benötigt).
  • Jede JavaScript-Datei sollte nur eine einzige Komponente enthalten. Die Datei sollte nach dem Namen der Komponente benannt sein.
  • Verwende Angular-Projektstrukturvorlagen wie Yeoman oder ng-boilerplate.

Ich bevorzuge die erste Struktur, weil bei ihr die üblichen Komponenten einfacher gefunden werden können.

Konventionen über die Benennung der Komponenten stehen in jedem Abschnitt über die jeweilige Komponente.

Markup

Auch die HTML-Markup ist wichtig und sollte in einem Team so geschrieben werden, als sei sie von derselben Person.

TLDR; Scripts sollten am Ende einer Seite eingefügt werden.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Meine App</title>
</head>
<body>
  <div ng-app="myApp">
    <div ng-view></div>
  </div>
  <script src="angular.js"></script>
  <script src="app.js"></script>
</body>
</html>

Um den Code nicht unnötig zu verkomplizieren, füge AngularJS-spezifische Direktiven hinter Standard-Attributen ein. Dadurch ist es einfacher, sich den Code anzusehen und durch das Framework erweitertes HTML zu erkennen (was die Wartbarkeit verbessert).

<form class="frm" ng-submit="login.authenticate()">
  <div>
    <input class="ipt" type="text" placeholder="name" require ng-model="user.name">
  </div>
</form>

Andere HTML-Attribute sollten den Empfehlungen des Code Guides folgen.

Optimieren des Digest-Zyklus

  • Watche nur auf die vitalsten Variablen (Beispiel: Wenn du eine Echtzeitkommunikation einsetzt, sollte nicht bei jeder eingehenden Nachricht ein $digest-Loop ausgelöst werden).
  • Für Inhalte, die nur einmal initialisiert und anschließend nicht mehr geändert werden, sollten Einmal-Watcher wie bindonce verwendet werden.
  • Vereinfache Berechnungen in $watch so weit wie möglich. Komplexe und langsame Berechnungen in einem einzigen $watch verlangsamen die gesamte Applikation (der $digest-Loop wird in einem einzelnen Thread ausgeführt, weil JavaScript single-threaded ist).
  • Falls in der Callback-Funktion von $timeout keine gewatchten Variablen geändert werden, setze den dritten Parameter der $timeout-Funktion auf false, um nicht automatisch einen $digest-Zyklus durch den Aufruf des Callbacks auszulösen.

##Namensgebung Die Namensgebung für alle Elemente sind in folgender Tabelle wieder zu finden:

Element Style Beispiel Verwendung bei
Module lowerCamelCase angularApp
Controller funktionalität + 'Ctrl' adminCtrl
Direktiven lowerCamelCase userInfo
Filter lowerCamelCase userFilter
Services UpperCamelCase User Konstruktor
Services lowerCamelCase dataFactory sonstige

Sonstiges

  • Verwende:
    • $timeout statt setTimeout
    • $interval statt setInterval
    • $window statt window
    • $document statt document
    • $http statt $.ajax

Dadurch werden deine Tests einfacher und in manchen Fällen wird einem unerwarteten Verhalten vorgebeugt (zum Beispiel wenn du ein $scope.$apply() in setTimeout vergessen hast).

  • Automatisiere deinen Workflow mit Tools wie:

  • Verwende Promises ($q) statt Callbacks. Dadurch sieht dein Code eleganter und sauberer aus und du wirst nicht in der Callback-Hölle landen.

  • Verwende, wenn möglich, $resource statt $http. Das höhere Abstraktionslevel schützt dich vor Redundanz.

  • Verwende einen Angular Pre-Minifier (wie ngmin oder ng-annotate), um Probleme nach einer Minification zu vermeiden.

  • Verwende keine Globalen. Löse alle Abhängigkeiten durch Dependency Injection auf.

  • Mülle deinen $scope nicht zu. Füge ihm nur Funktionen und Variablen hinzu, die in den Templates verwendet werden.

  • Bevorzuge Controller gegenüber ngInit. ngInit eignet sich nur, um Aliase für spezielle Eigenschaften von ngRepeat zu erstellen. Hiervon abgesehen solltest du immer Controller statt ngInit verwenden, um Werte in einem Scope zu initialisieren.

  • Verwende kein $ als Präfix für die Namen von Variablen, Eigenschaften oder Methoden. Dieser Präfix ist für AngularJS reserviert.

Module

  • Module sollten in lowerCamelCase benannt werden. Um deutlich zu machen, dass das Modul b ein Untermodul von a ist, kannst du sie durch Namespaces verschachteln, z. B.: a.b.

Es gibt zwei verbreitete Wege, nach denen Module strukturiert werden können:

  1. Nach Funktionalität
  2. Nach Typ der Komponente

Derzeit gibt es keinen großen Unterschied, aber die erste Variante sieht sauberer aus. Außerdem wird - wenn lazy-loading für die Module implementiert ist (momentan nicht auf der AngularJS-Roadmap) - die Performance der App verbessert.

Controller

  • Du solltest das DOM nicht aus deinen Controllern heraus manipulieren, dadurch wird das Testen der Controller erschwert und du verstößt gegen das Prinzip der Separation of Concerns. Verwende stattdessen Direktiven.
  • Controller sollen nach ihrer Funktion (zum Beispiel shopping cart, homepage, admin panel) und dem Suffix Ctrl benannt werden. Controller werden in UpperCamelCase benannt (HomePageCtrl, ShoppingCartCtrl, AdminPanelCtrl usw.).
  • Controller sollten nicht als Globale definiert werden (AngularJS erlaubt das zwar, es ist jedoch schlechte Praxis den globalen Namensraum zu verschmutzen).
  • Verwende für Controller-Definitionen die Array-Syntax:
module.controller('MyCtrl', ['dependency1', 'dependency2', ..., 'dependencyn', function(dependency1, dependency2, ..., dependencyn) {
  // body
}]);

Durch die Verwendung dieses Definitionstyps werden Probleme bei der Minification vermieden. Die Array-Definition kann aus der Standardnotation automatisch generiert werden, indem Werkzeuge wie ng-annotate (und der Grunt-Task grunt-ng-annotate) verwendet werden.

  • Verwende die Originalnamen der im Controller verwendeten Abhängigkeiten. Dies hilft dir dabei, lesbareren Code zu schreiben:
module.controller('MyCtrl', ['$scope', function(s) {
  // body
}]);

ist schlechter lesbar als:

module.controller('MyCtrl', ['$scope', function($scope) {
  // body
}]);

Das gilt insbesondere für Dateien, die so viel Code enthalten, dass gescrollt werden muss. Dadurch vergisst du möglicherweise, welche Variable zu welcher Abhängigkeit gehört.

  • Halte Controller so schlank wie möglich und lagere mehrfach verwendete Funktionen in Services aus.
  • Kommuniziere zwischen verschiedenen Controllern, indem du Method Invocation nutzt (das ist möglich, wenn ein Kindcontroller mit seinem Elterncontroller kommunizieren möchte) oder die $emit-, $broadcast- und $on-Methoden verwendest. Über $emit und $broadcast gesendete Nachrichten sollten auf ein Minimum reduziert werden.
  • Erstelle eine Liste aller Nachrichten, die über $emit und $broadcast verschickt werden. Pflege diese Liste, um Kollisionen und Bugs zu vermeiden.
  • Wenn du Daten formatieren musst, kapsle die Formatierungslogik in einem Filter und gebe diesen als Abhängigkeit an:
module.filter('myFormat', function() {
  return function() {
    // body
  };
});

module.controller('MyCtrl', ['$scope', 'myFormatFilter', function($scope, myFormatFilter) {
  // body
}]);

Direktiven

  • Benenne deine Direktiven in lowerCamelCase.
  • Verwende scope statt $scope in deiner Link-Funktion. In den Compile- und Post-/Pre-Link-Funktionen hast du bereits Argumente angegeben, die verwendet werden sobald die Funktion aufgerufen wird. Diese kannst du nicht über eine Dependency Injection ändern. Dieser Stil wird auch im AngularJS-Sourcecode verwendet.
  • Verwende eigene Präfixe für deine Direktiven, um Namenskollisionen mit Bibliotheken von Drittanbietern zu vermeiden.
  • Die Präfixe ng und ui solltest du nicht verwenden, da diese für AngularJS und AngularUI reserviert sind.
  • DOM-Manipulationen dürfen ausschließlich über Direktiven vorgenommen werden.
  • Verwende einen Isolated Scope, wenn du wiederverwendbare Komponenten entwickelst.
  • Binde Direktiven über Attribute oder Elemente ein statt über Kommentare oder Klassen. Das macht deinen Code lesbarer.
  • Verwende zum Aufräumen $scope.$on('$destroy', fn). Dies ist besonders nützlich wenn du Wrapper-Direktiven für Drittanbieter-Plug-ins entwickelst.
  • Vergiss nicht, $sce zu verwenden, wenn du mit Inhalten arbeitest, die nicht vertrauenswürdig sind.

Filter

  • Benenne deine Filter in lowerCamelCase.
  • Halte deine Filter so schlank wie möglich. Durch die $digest-Schleife werden sie häufig aufgerufen, so dass langsame Filter die gesamte Anwendung verlangsamen.
  • Mache nur eine einzige Sache in deinen Filtern, halte sie kohärent. Komplexere Manipulationen können erzielt werden, indem mehrere Filter gepiped werden.

Services

Dieser Abschnitt enthält Informationen über AngularJS' Service-Komponente. Er bezieht sich nicht auf eine spezielle Definitionsweise (d. h. als Provider, Factory oder Service), falls nicht ausdrücklich genannt.

  • Benenne deine Services in camelCase.
    • UpperCamelCase (PascalCase), um Services zu benennen, die als Konstruktoren verwendet werden, d. h.:
module.controller('MainCtrl', function ($scope, User) {
  $scope.user = new User('foo', 42);
});

module.factory('User', function () {
  return function User(name, age) {
    this.name = name;
    this.age = age;
  };
});
  • lowerCamelCase für alle anderen Services.
  • Kapsle die gesamte Anwendungslogik in Services.
  • Services, die eine bestimmte Domäne abbilden, sollten bevorzugt als service statt als factory geschrieben werden. Auf diese Weise können die Vorteile der klassischen Vererbung einfacher genutzt werden:
function Human() {
  // body
}
Human.prototype.talk = function() {
  return "I'm talking";
};

function Developer() {
  // body
}
Developer.prototype = Object.create(Human.prototype);
Developer.prototype.code = function() {
  return "I'm coding";
};

myModule.service('Human', Human);
myModule.service('Developer', Developer);
  • Für einen sitzungsbezogenen Cache kannst du $cacheFactory verwenden. Diesen solltest du nutzen, um die Ergebnisse von Anfragen oder aufwändigen Berechnungen zwischenzuspeichern.
  • Falls ein Service konfiguriert werden muss, definiere ihn als Provider und konfiguriere ihn im config-Callback, wie hier:
angular.module('demo', [])
.config(function ($provide) {
  $provide.provider('sample', function () {
    var foo = 42;
    return {
      setFoo: function (f) {
        foo = f;
      },
      $get: function () {
        return {
          foo: foo
        };
      }
    };
  });
});

var demo = angular.module('demo');

demo.config(function (sampleProvider) {
  sampleProvider.setFoo(41);
});

Templates

  • Verwende ng-bind oder ng-cloak statt einfachen {{ }}, um flackernde Inhalte zu vermeiden.
  • Vermeide es, komplexe Ausdrücke in ein Template zu schreiben.
  • Wenn das src-Attribut eines Bilds dynamisch gesetzt werden soll, verwende ng-src statt src mit einem {{ }}-Template.
  • Wenn du das href-Attribut eines Ankers dynamisch setzen musst, verwende ng-href statt href mit einem {{ }}-Template.
  • Statt in Scopevariablen Strings anzugeben und diese mit {{ }} in ein style-Attribut zu schreiben, benutze die ng-style-Direktive, der als Parameter objektartige Strings und Scopevariablen übergeben werden können:
$scope.divStyle = {
  width: 200,
  position: 'relative'
};
<div ng-style="divStyle">Mein wunderschön gestyltes div, das auch im IE funktioniert</div>;

Routing

  • Verwende resolve, um Abhängigkeiten aufzulösen bevor die View angezeigt wird.

#Testen

TBD

Du kannst diese Anleitung verwenden, solange dieser Abschnitt noch nicht fertig ist.

Mitmachen

Da dieser Style Guide gemeinschaftlich durch die Community erstellt werden soll, sind Beiträge willkommen. Zum Beispiel kannst du etwas beitragen, indem du den Abschnitt über Tests erweiterst oder den Style Guide in deine Sprache übersetzt.

#Mitwirkende

mgechev pascalockert mainyaa rubystream lukaszklis
mgechev pascalockert mainyaa rubystream lukaszklis
cironunes cavarzan tornad jmblog bargaorobalo
cironunes cavarzan tornad jmblog bargaorobalo
astalker valgreens bitdeli-chef dchest gsamokovarov
astalker valgreens bitdeli-chef dchest gsamokovarov
ntaoo hermankan jesselpalmer capaj jordanyee
ntaoo hermankan jesselpalmer capaj jordanyee
nacyot kirstein mo-gr cryptojuice olov
nacyot kirstein mo-gr cryptojuice olov
vorktanamobay thomastuts grapswiz coderhaoxin dreame4
vorktanamobay thomastuts grapswiz coderhaoxin dreame4