JavaScript met YUIDoc documenteren

Het documenteren van uw code lijkt een beetje op testen; we weten allemaal dat we het moeten doen, we weten niet precies hoe, en de meeste mensen, als we eerlijk zijn, doen het gewoon niet, maar degenen die dat wel doen, zijn er een groot voorstander van. Deze tutorial helpt je een van de beste manieren om het aan te pakken: YUIDoc.


Wat is YUIDoc?

YUIDoc genereert API-documentatie op basis van opmerkingen die u schrijft.

YUIDoc is een NodeJS-app die API-documentatie (in de vorm van HTML) genereert op basis van opmerkingen die u in uw JavaScript-broncode schrijft. Eigenlijk is het niet alleen voor JavaScript: elke programmeertaal die blokopmerkingen ondersteunt die worden afgebakend door / * * / werkt voor YUIDoc. Zoals je misschien wel raadt, is YUIDoc een van de tools die Yahoo! publiceert samen met hun YUI-bibliotheek.

Om YUIDoc te installeren, hebt u NodeJS en de Node Package Manager (npm) eerst geïnstalleerd. Vervolgens kunt u YUIDoc via installeren npm -g installeer yuidocjs. Je zult het gebruiken door te hardlopen yuidoc ; hier later meer over.


Het draait allemaal om de tags

U weet dus dat YUIDoc zijn documentatie haalt uit de opmerkingen met meerdere regels in het bronbestand. Natuurlijk kunt u opmerkingen hebben die geen deel uitmaken van de documentatie. Om YUIDoc een opmerking als significant te laten herkennen, moet deze beginnen met een dubbele start: / **. Zo:

 / ** YUIDoc verwerkt dit * / / * Maar niet dit * /

Natuurlijk telt het van binnen wat telt (binnen de blokken van commentaar, dat is). Elk moet een en slechts één primaire tag bevatten; het kan ook nul of meer secundaire tags bevatten. Echt, YUIDoc is zo simpel: voeg opmerkingen toe met de juiste tags aan je code en presto: documentatie! Laten we wat tags leren. Hier is hoe we dit gaan doen: we gaan over de tags, en waar ze worden gebruikt, met eenvoudige voorbeelden van hun gebruik; dan zullen we een aantal code schrijven en documenteren zodat je een beter idee hebt van hoe de tags samenwerken.


Primaire tags

Voordat u naar de primaire tags gaat, moet u onthouden dat elk commentaarblok slechts één primaire tag kan hebben. Deze beschrijven wat een bepaald stuk code is.

@module

De @module tag beschrijft een groep relatieklassen. (Ja, ja, JavaScript heeft geen klassen: YUIDoc verwijst naar constructorfuncties.) Als u YUIDoc gebruikte om BackboneJS te documenteren, ruggegraat object zou een module zijn, omdat deze de module bevat Model, Verzameling, Uitzicht, en andere klassen. Vlak na de tag plaats je de naam van de module.

 / ** @module Backbone * / var Backbone = Backbone || ;

@klasse

De @klasse tag beschrijft treffend een enkele klasse. In de YUI-bibliotheek betekent dit meestal een constructorfunctie, maar als u liever een ander patroon gebruikt en uw klas belt, kunt u dat ook doen. Elke opmerking met een @klasse tag moet ook een @statisch of @constructor tag (secundaire tags die we binnenkort zullen bespreken).

 / ** @class Model * / function Model () 

Als uw klas deel uitmaakt van een module, hoeft u niets te doen binnen de @klasse opmerking om aan te geven dat: zorg ervoor dat er een is @module commentaarblok bovenaan dat bestand.

@methode

Uiteraard zal elke klas op zijn minst een paar methoden hebben, en je zult de @methode tag om ze te beschrijven. De naam van de methode gaat achter de tag en u gebruikt de secundaire tags @return en @params om de methode te beschrijven.

 / ** @method render * / View.prototype.render = functie (gegevens) 

@eigendom

De @eigendom tag wordt gebruikt om de eigenschappen van een klasse te labelen. Je zult de @type en @standaard secundaire tags met deze zeker.

 / ** @property templateString * / this.templateString = "div";

@evenement

Als u speciale aangepaste gebeurtenissen hebt die een klasse kan activeren, wilt u de @evenement tag om ze te beschrijven. Dit is wat de YUIDoc-documentatie te zeggen heeft:

Een @evenement blok is enigszins vergelijkbaar met een @methode blokkeren, behalve dat @return is niet relevant, en @param wordt gebruikt om eigenschappen te beschrijven die aan het gebeurtenisobject hangen en die terugbellen naar het ontvangen van het evenement.


Secundaire tags

Commentaarblokken kunnen meerdere secundaire tags hebben; ze hebben vaak een handjevol en soms zelfs meer dan één van hetzelfde type. Laten we eens kijken naar enkele daarvan die u vaak zult gebruiken.

@submodule

Als u uw modules opsplitst in submodules (misschien een submodule per bestand, misschien niet), de @submodule tag staat tot uw dienst.

 / ** @module Util @submodule array * / Util.array = ;

@extends

De @extends tag is handig als u superklasse / subklasse-relaties hebt. U kunt aangeven welke klasse de ouder is van de momenteel gedocumenteerde klasse:

 / ** @class AppView @extends Backbone.View * / var AppView = Backbone.View.extend ();

@constructor

Als een klasse kan worden geïnstantieerd, betekent dit dat deze een constructorfunctie nodig heeft. Als u het standaard prototypepatroon in JavaScript gebruikt, is de klassendeclaratie ook de constructor. Dat betekent dat u vaak zoiets ziet als dit:

 / ** @class Recept @constructor * / function Recept () 

Je herinnert je me waarschijnlijk dat ik dat allemaal heb gezegd @klasse tag moet ofwel een @constructor of @statisch secundaire tag.

@statisch

Over dat gesproken @statisch, hier is het. Een klasse wordt als statisch beschouwd als u er geen instantie van kunt maken. Een goed voorbeeld hiervan is de ingebouwde Wiskunde object: je maakt er nooit een instantie van (nieuwe wiskunde ()), noem je de methoden van de klas zelf.

 / ** @class MathHelpers @static * / var MathHelpers = ;

Een methode kan ook statisch zijn: als een klasse kan worden geïnstantieerd, maar ook enkele methoden op klasseniveau heeft, worden deze methoden als statisch beschouwd (ze worden in de klasse aangeroepen, niet in het exemplaar).

 / ** @class Person @constructor * / function Person ()  / ** @method all @static * / Person.all = function () ;

In dit voorbeeld kunt u een maken Persoon bijvoorbeeld, maar de allemaal methode is statisch.

@laatste

Deze tag wordt gebruikt voor eigenschappen of kenmerken en markeert de eigenschap als een constante: deze mag niet worden gewijzigd. Hoewel JavaScript geen echte constanten in zijn huidige staat heeft, kan uw coderingspatroon of stijlgids ze in principe gebruiken, dus dit zal handig zijn voor dat.

 / ** @property DATE_FORMAT @final * / var DATE_FORMAT = "% B% d,% Y";

@param

Hier is een belangrijke: de @param tag wordt gebruikt om de parameters van a te definiëren @methode (inclusief een @constructor) of een @evenement. Er zijn drie stukjes informatie die achter de @param tag: de naam van de parameter, het type (dat optioneel is) en de beschrijving. Deze kunnen in de volgorde staan naam type beschrijving of type naam beschrijving; maar in beide gevallen moet het type worden omringd door accolades.

 / ** @method greet @param person string De naam van de begroeting * / function greet (persoon) 

Er zijn een paar manieren om de naam deel ook. Als u het tussen vierkante haakjes zet, wordt dit als optioneel gemarkeerd terwijl u het plaatst = SOMEVAL nadat het laat zien wat de standaardwaarde is (uiteraard hebben alleen optionele parameters een standaardwaarde). Als het een tijdelijke aanduiding voor meer dan één argument is, voegt u toe * dat laten zien. (Duidelijk, naam* is een tijdelijke aanduiding voor 1 of meer argumenten, terwijl [naam]* is een placeholder voor 0 of meer).

 / ** @class Template @constructor @param-sjabloon String De sjabloonreeks @param [data = ] Object Het object waarvan de eigenschappen worden weergegeven in de template * / function Template (sjabloon, gegevens) 

@return

De meeste van uw methoden willen een waarde retourneren, dus dit is de tag die die waarde beschrijft. Vergeet niet om het te vertellen welk type de waarde is, en geef het een beschrijving.

 / ** @method toHTML @param [template = Recipe.defaultTemplate] Template Een template-object @return String De inhoud van het recept is opgemaakt in HTML met de standaard of ingeleverd sjabloon. * / Recipe.prototype.toHTML = functie (sjabloon) terug "wat dan ook"; ;

@type

Herinner de @eigendom primaire tag? U zult willen definiëren welk type deze eigenschappen zijn, toch? Nou ja, de @type tag is precies wat je nodig hebt. Geef het type op achter de tag; je kunt ook meerdere typen aanbieden door ze te scheiden met verticale balken:

 / ** @property URL @type String * / URL: "http://net.tutsplus.com", / ** @property person @type String | Person | Object * / this.person = new Person ();

@privaat / @protected

Traditionele programmeertalen bieden privé-eigenschappen of -methoden: deze zijn niet toegankelijk van buiten de instantie. Net als constanten, JavaScript heeft ze alleen door te oefenen, maar je kunt gebruiken @privaat om deze te taggen als je ze gebruikt. Houd er rekening mee dat YUIDoc geen privé-eigenschappen weergeeft in de documenten die het genereert (logisch), dus hiermee kunt u een functie documenteren voor uw eigen voordeel en niet laten verschijnen in de documenten.

 / ** @method _toString @private * / var _toString = Object.prototype.toString.call;

Beschermde eigenschappen en methoden bevinden zich halverwege tussen openbaar en privé: ze zijn alleen toegankelijk vanuit instanties en instanties van subklassen. Als dat iets is dat u in JavaScript doet, is dit uw tag: @protected.

@requires

Als een module afhankelijk is van een of meer andere modules, kunt u deze gebruiken @requires om dat te markeren:

 / ** @module MyFramework.localstorage @requires MyFramework * /

Let daar op @requires kan ook een lijst van afhankelijkheden nemen, gescheiden door komma's.

@standaard

Bij het declareren van een @eigendom, je zou het misschien handig vinden om het een te geven @standaard waarde. @standaard moet altijd worden gebruikt met @type.

 / ** @property element @type String @default "div" * / element: "div",

@toepassingen

Zoals we al zeiden, JavaScript heeft niet echt klassen, maar het is flexibel genoeg om de illusie van klassen en zelfs subklassen te creëren. Wat nog cooler is, is dat het flexibel genoeg is om mixins of modules te hebben: dit is waar een klasse eigenschappen of methoden van een andere klasse "leent". En het is ook geen overerving, omdat je kunt mixen in delen van meer dan één klasse (natuurlijk heeft YUI het vermogen om dit te doen, maar dat geldt ook voor Dojo en andere bibliotheken). Als je dit doet, zul je merken @toepassingen erg handig: hiermee kun je aangeven welke klassen een bepaalde klasse in delen van mengt.

 / ** @class ModalWindow @uses Window @uses DragDroppable * / var ModalWindow = new Class (mixes: [Window, DragDroppable], ...);

Opmerking: ik heb net die mixin-syntaxis verzonnen, maar ik ben er vrij zeker van dat ik ergens iets vergelijkbaars heb gezien.

@voorbeeld

Wilt u een voorbeeld opnemen van hoe een bepaald stuk code te gebruiken? Gebruik de @voorbeeld tag en schrijf vervolgens het onderstaande voorbeeld, het één niveau inspringend. U kunt zoveel voorbeelden toevoegen als u wilt.

 / ** @method greet @example person.greet ("Jane"); * / Person.prototype.greet = functie (naam) ;

@chainable

Je bent waarschijnlijk bekend met koppelbare methoden van jQuery. U weet wel, waar u een methode van een methode-aanroep kunt aanroepen, omdat de methoden het object retourneren? Markeer uw methoden als zodanig met @chainable.

 / ** @method addClass @chainable * / jQuery.prototype.addClass = function (class) // dingen; geef dit terug; 

@verouderd / @sinds / @beta

Deze drie tags gaan allemaal over ondersteuning voor de code (en het kan elke code zijn: module, klasse, methode of iets anders). Gebruik @verouderd om sommige functionaliteit te markeren als niet langer de beste manier om dit te doen (verouderde functionaliteit zal waarschijnlijk worden verwijderd in een toekomstige versie van de code). Optioneel kunt u een bericht opnemen waarin wordt uitgelegd wat de huidige manier is om dit te doen.

 / ** @method toJSON @deprecated Geef het object door aan 'JSON.parse' in plaats van * / Something.toJSON = function () ;

De @sinds tag vertelt lezers alleen welke versie de gegeven code heeft toegevoegd. En @beta markeert bètacode: YUI suggereert dat @beta code kan 'in de nabije toekomst' onverenigbare wijzigingen ondergaan.

 / ** @class Tooltip @since 1.2.3 @constructor * / function Tooltip () 

@uitbreiding / @extensionfor / extension_for

De @uitbreiding tag (en zijn aliassen) is vrijwel het tegenovergestelde van @toepassingen. Gebruik het om te markeren in welke klassen de uitbreidingsklasse kan worden verwerkt. Besef natuurlijk dat dit niet wil zeggen dat het altijd vermengd is, alleen dat het kan zijn.

 / ** @class Draggable @extensionfor ModalWindow * /

Opmerkingen en markdown

Voordat we naar een concreet voorbeeld kijken, wil ik nog twee dingen aan het licht brengen over de documentatiecommentaarblokken.

Ten eerste wilt u vaak een beetje meer informatie over uw code toevoegen dan wat de tags bieden. Misschien wil je het doel van de methoden beschrijven, of hoe een klas in het grotere geheel past. Voeg deze opmerkingen boven aan het reactieblok toe, boven een van de tags. YUIDoc zal ze opmerken en opnemen in de documentatie.

 / ** De klasse 'Router' wordt gebruikt voor ... @class Router @static * / var Router = ;

Ten tweede zult u blij zijn te weten dat deze opmerkingen, evenals alle beschrijvingen of berichten geschreven na de tags, kunnen worden geschreven in Markdown, en YUIDoc zal het naar de juiste HTML converteren. U kunt zelfs code-blokken inspringen in uw opmerkingen en syntax highlighting krijgen!


Een voorbeeld

Nu je de tags hebt geleerd, laten we een code schrijven en documenteren. Laten we een maken Op te slaan module, die twee klassen bevat: Item en kar. Elk Item exemplaar is een type item in de winkelinventaris: het heeft een naam, een prijs en een aantal. EEN kar bijvoorbeeld kan items toevoegen aan de winkelwagen en de totale prijs van de items in de winkelwagen berekenen (inclusief belasting). Het is vrij eenvoudig, maar geeft ons voldoende gevarieerde functionaliteit om veel van de tags te gebruiken die we hebben besproken. Ik heb de volgende code erin gezet store.js.

We beginnen met het maken van de module:

 / ** * Deze module bevat klassen voor het runnen van een winkel. * @module Store * / var Store = Store || ;

Laten we nu een "constante" maken: het belastingtarief.

 / ** * 'TAX_RATE' wordt opgeslagen als een percentage. Waarde is 13. * @property TAX_RATE * @static * @final * @type Number * / Store.TAX_RATE = 13;

Dit is een constante (@laatste) @eigendom van @type Aantal. Merk op dat ik heb opgenomen @statisch: dit is omdat, om wat voor reden dan ook, wanneer we de documentatie voor dit bestand genereren, YUIDoc dit als een eigenschap van onze Item klasse: het lijkt erop dat YUIDoc geen ondersteuning biedt voor het hebben van een eigenschap op een module. Ik denk dat ik een statische klasse zou kunnen maken om dit constant te houden (en andere constanten die zouden kunnen komen als we dit verder zouden ontwikkelen), maar ik heb het op deze manier achtergelaten voor een herinnering: om een ​​tool als YUIDoc optimaal te gebruiken, jij moet misschien de manier veranderen waarop je codeert. Je zult moeten beslissen of dat is wat je wilt doen.

Nu, voor de Item klasse:

 / ** * @class Item * @constructor * @param name String Itemnaam * @param prijs Number Item prijs * @param quantity Number Item hoeveelheid (het nummer dat beschikbaar is om te kopen) * / Store.Item = functie (naam, prijs, aantal) / ** * @property naam * @type String * / this.name = naam; / ** * @eigendomsprijs * @type String * / this.price = prijs * 100; / ** * @property quantity * @type Number * / this.quantity = aantal; / ** * @property id * @type Number * / this.id = Store.Item._id ++; Store.Item.list [this.id] = this; ;

Zoals u kunt zien, heeft deze constructor drie parameters. Vervolgens zijn er drie eigenschappen binnen de constructor die we ook beschrijven. Omdat we alles willen geven Item een unieke ID, moeten we een statische (class-level) property opslaan om de ID te verhogen, en een andere statische eigenschap, een object dat de Items door hun ID.

 / ** * '_id' wordt opgehoogd wanneer een nieuw item wordt gemaakt, dus heeft elk item een ​​unieke ID * @property id * @type Number * @static * @private * / Store.Item._id = 1; / ** * @eigenschapslijst * @static * @type Object * / Store.Item.list = ;

Hoe zit het met kar klasse?

 / ** * @class Cart * @constructor * @param name String Klantnaam * / Store.Cart = function (name) / ** * @property name * @type String * / this.name = name; / ** * @property-items * @type Object * @default  * / this.items = ; ;

Er is hier niets nieuws: merk op dat we verklaren dat de standaard (of initiële) staat van de items eigenschap is een leeg object.

Nu, de methoden. Voor de Voeg item toe, een van de parameters is optioneel, dus we verklaren het als zo, en geven het een standaardwaarde van 1. Merk ook op dat we de methode maken @chainable.

 / ** * Voegt 1 of meer items toe aan het winkelwagentje als de gekozen hoeveelheid * beschikbaar is. Als dat niet het geval is, wordt er geen toegevoegd. * * @method addItem * @param item Object Een 'Item' Object * @param [aantal = 1] Number Het aantal items dat aan het winkelmandje moet worden toegevoegd * @chainable * / Store.Cart.prototype.addItem = functie (artikel, aantal) hoeveelheid = aantal || 1; if (item.quantity> = quantity) this.items [item.id] = this.items [item.id] || 0; this.items [item.id] + = aantal; item.quantity - = aantal;  stuur dit terug; ;

Ten slotte willen we de totale prijs inclusief belastingen kunnen teruggeven. Merk op dat we de prijs wiskunde in centen doen, en dan converteren naar dollars en afronden naar twee decimalen.

 / ** * @methode totaal * @return Number totale btw-inventariswaarde van winkelwagentjes * / Store.Cart.prototype.total = function () var subtotal, id; subtotaal = 0; for (id in this.items) if (this.items.hasOwnProperty (id)) subtotal + = Store.Item.list [id] .price * this.items [id];  retourneer parseFloat (((subtotaal * (1 + Store.TAX_RATE / 100)) / 100) .toFixed (2)); ;

Als u deze code wilt uitproberen, volgen hier een eenvoudige test:

 var apple, pear, book, desk, assertEquals; assertEquals = function (one, two, msg) console.log (((een === twee)? "PASS:": "FAIL:") + msg); ; apple = nieuwe Store.Item ('Granny Smith Apple', 1.00, 5); peer = nieuwe Store.Item ('Barlett Pear', 2.00, 3); book = new Store.Item ('On Writing Well', 15.99, 2); desk = new Store.Item ('IKEA Gallant', 123.45, 1); cart = nieuwe Store.Cart ('Andrew'); cart.addItem (apple, 1) .addItem (book, 3) .addItem (desk, 1); assertEquals (apple.quantity, 4, "door 1 appel toe te voegen, wordt 1 uit de artikelhoeveelheid verwijderd"); assertEquals (book.quantity, 2, "probeert meer boeken toe te voegen dan er middelen zijn die er geen zijn toegevoegd"); assertEquals (cart.total (), 140.63, "totale prijs voor 1 appel en 1 desk is 140.63");

De documentatie genereren

Nu we de code en opmerkingenblokken hebben geschreven, is het tijd om de documentatie te genereren.

Als je het wereldwijd via npm hebt geïnstalleerd, kun je het gewoon uitvoeren yuidoc pad naar js. In mijn geval is dat zo

 yuidoc .

Nu, je zult zien dat je een hebt uit map in die map; Open out / index.html, en je zult de documentatie zien. Hier is welk deel van de kar klassedocumentatie ziet er als volgt uit:


Uitvoer configureren

Er zijn verschillende configuratie-opties die u kunt instellen bij het gebruik van YUIDoc. Natuurlijk, je kunt ze instellen als commandolijnvlaggen, maar ik zet ze liever in een JSON-configuratiebestand. Maak een bestand met de naam in uw projectdirectory yuidoc.json. Ten eerste is er een heleboel algemene projectinformatie die u kunt instellen; dit heeft niet echt invloed op de uitvoer, maar het is goed om ze te documenteren:

 "name": "JavaScript met YUIDoc documenteren", "description": "Een tutorial over YUIDoc, voor Nettuts +", "version": "1.0.0", "url": "http://net.tutsplus.com "

Dan zijn er een aantal daadwerkelijke opties die u kunt instellen. Hier zijn een paar interessante;

  • linkNatives: stel dit in op "true" om native types zoals String of Number te koppelen aan de MDN-documenten.
  • outdir: gebruik deze om de naam te wijzigen uit directory
  • paden: gebruik dit om in te stellen welke paden YUIDoc zoekt naar JavaScript-bestanden.
  • uitsluiten: stel dit in op een door komma's gescheiden lijst van bestanden die YUIDoc moet negeren.

Zolang u de paden opties, kunt u uitvoeren yuidoc -c yuidoc.json en YUIDoc wordt uitgevoerd. Zelfs als u niet instelt paden en ren gewoon yuidoc ., YUIDoc zal dat configuratiebestand zien en het toepassen.

Dit is mijn totale configuratiebestand voor dit project:

 "name": "JavaScript met YUIDoc documenteren", "description": "Een tutorial over YUIDoc, voor Nettuts +", "version": "1.0.0", "url": "http://net.tutsplus.com "," options ": " linkNatives ":" true "," outdir ":" ./docs "," paths ":". " 

evaluatie

Op basis van de tags die YUIDoc biedt, kunt u zien dat het is gemaakt voor JavaScript dat is geschreven in de traditionele OOP-stijl, en speciaal voor YUI-widgets en dergelijke (in feite heb ik verschillende tags weggelaten die YUI-specifiek waren). Vanwege dit alles zult u merken dat verschillende tags gewoon niet zo nuttig voor u zijn. Dan moet je jezelf afvragen of je bereid bent je codeerstijl te veranderen om beter overeen te komen met de manier waarop YUIDoc "denkt." Maar zelfs als je niet gaat veranderen, denk ik dat je zult merken dat de meeste YUIDoc-tags passen in orde.

De grotere vraag aan mij is of je je documentatie graag inline wilt hebben met je code.

De voorbeeldcode die we hierboven schreven is 120 regels met opmerkingen, 40 regels zonder. Uiteraard is dat een supereenvoudige code, en vrijwel elk voorbeeld uit de echte wereld zou meer in evenwicht zijn; het kan echter moeilijk zijn om zo'n code af te lezen. Persoonlijk denk ik dat ik YUIDoc een eerlijke rechtszaak zal geven: ik zal mijn JavaScript documenteren terwijl ik het schrijf (of in ieder geval, naast het) voor de komende paar weken. Ik ben benieuwd of en hoe dit van invloed is op mijn coderingsstijl en werkstroom.

Je kent de routine: hou ervan of haat het, laat het me weten in de comments!


Voor meer

  • YUIDoc 0.3.0 Publiceer Blogpost
  • YUIDoc-startpagina
  • YUIDoc gebruiken
  • YUIDoc syntaxreferentie
  • YUIDoc-thema's