Dokumentere JavaScript med YUIDoc

Dokumentere koden din er noe som å teste; Vi vet alle at vi skal gjøre det, vi er ikke helt sikre på hvordan, og de fleste, hvis vi er ærlige, gjør det ikke, men de som gjør det er store fortaler for det. Denne opplæringen vil gi deg fart på en av de beste måtene å takle det på: YUIDoc.


Hva er YUIDoc?

YUIDoc vil generere API dokumentasjon basert på kommentarer du skriver.

YUIDoc er en NodeJS-app som genererer API-dokumentasjon (i form av HTML), basert på kommentarer du skriver i JavaScript-kildekoden. Egentlig er det ikke bare for JavaScript: et hvilket som helst programmeringsspråk som støtter blokkkommentarer avgrenset av / * * / jobber for YUIDoc. Som du kanskje gjetter, er YUIDoc et av verktøyene som Yahoo! publiserer sammen med deres YUI-bibliotek.

For å installere YUIDoc, trenger du NodeJS og Node Package Manager (npm) installert først. Deretter kan du installere YUIDoc via npm -g installere yuidocjs. Du bruker den ved å kjøre yuidoc ; mer om dette senere.


Det handler om etikettene

Så, du vet at YUIDoc får dokumentasjonen fra de multiline kommentarene i kildefilen. Selvfølgelig kan du ha kommentarer som ikke er en del av dokumentasjonen. For YUIDoc å gjenkjenne en kommentar som signifikant, må den starte med en dobbel start: / **. Så:

 / ** YUIDoc vil behandle dette * / / * Men ikke dette * /

Selvfølgelig er det hva som er inne i det som teller (inne i kommentarblokker, det er). Hver må inneholde en og eneste primærmerke; det kan også inneholde null eller flere sekundære koder. Virkelig, YUIDoc er så enkelt: legg til kommentarer med de riktige kodene til koden din, og presto: dokumentasjon! Så la oss lære noen tagger. Slik skal vi gjøre dette: Vi går over merkene, og hvor de brukes, med enkle eksempler på deres bruk da skal vi skrive og dokumentere noe kode, slik at du får en bedre ide om hvordan kodene fungerer sammen.


Primæretiketter

Før du kommer inn i de primære kodene, husk at hver kommentarblokk kun kan ha en enkelt primærkode. Disse beskriver hva en gitt del av koden er.

@module

De @module tag beskriver en gruppe relaterte klasser. (Ja, ja, JavaScript har ingen klasser: YUIDoc refererer til konstruktørfunksjoner.) Hvis du brukte YUIDoc til å dokumentere BackboneJS, Backbone objektet ville være en modul, fordi den holder Modell, Samling, Utsikt, og andre klasser. Rett etter merket legger du navnet på modulen.

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

@klasse

De @klasse Merket beskriver passende en enkelt klasse. I YUI-biblioteket betyr dette vanligvis en konstruktørfunksjon, men hvis du foretrekker å bruke et annet mønster og ringe det til klassen din, kan du også gjøre det. Hver kommentar med en @klasse tag skal også ha a @static eller @constructor tag (sekundære koder som vi diskuterer snart).

 / ** @ klasse Modell * / funksjon Modell () 

Hvis klassen din er en del av en modul, trenger du ikke gjøre noe innenfor @klasse kommentere for å utpeke det: bare sørg for at det er en @module kommentarblokk øverst på den filen.

@metode

Selvfølgelig vil hver klasse ha minst noen få metoder, og du vil bruke @metode tag for å beskrive dem. Metodenavnet vil gå etter taggen, og du vil bruke sekundære merkene @komme tilbake og @params å beskrive metoden.

 / ** @metod gjengi * / View.prototype.render = funksjon (data) 

@eiendom

De @eiendom tag brukes til å merke egenskapene til en klasse. Du vil bruke @type og @misligholde sekundære koder med denne, sikkert.

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

@begivenhet

Hvis du har spesielle tilpassede hendelser som en klasse kan brenne, vil du bruke @begivenhet tag for å beskrive dem. Her er hva YUIDoc dokumentasjonen har å si:

en @begivenhet blokk er noe lik en @metode blokkere, bortsett fra det @komme tilbake er irrelevant, og @param brukes til å beskrive egenskaper som henger av hendelsesobjektet som tilbakekallinger lytter etter hendelsen mottar.


Sekundære merker

Kommentarblokker kan ha mer enn ett sekundært merke; de vil ofte ha en håndfull, og noen ganger enda mer enn en av samme type. La oss se på noen av dem du vil bruke ofte.

@submodule

Hvis du deler modulene dine i submoduler (kanskje en submodule per fil, kanskje ikke), @submodule tag er til tjeneste.

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

@strekker

De @strekker tag er nyttig når du har superclass / subclass-relasjoner. Du kan hevde hvilken klasse som er forelder for den for tiden dokumenterte klassen:

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

@constructor

Hvis en klasse kan bli instantiated, betyr det at den trenger en konstruktørfunksjon. Hvis du bruker standard prototypemønster i JavaScript, er klassedeklarasjonen også konstruktøren. Det betyr at du ofte ser noe slikt:

 / ** @class Oppskrift @constructor * / funksjon Oppskrift () 

Faktisk husker du sannsynligvis at jeg sier at hver @klasse tag skal ha enten a @constructor eller @static sekundær tagg.

@static

Apropos @static, her er det. En klasse anses som statisk når du ikke kan opprette en forekomst av den. Et godt eksempel på dette er den innebygde Matte objekt: du lager aldri en forekomst av det (ny matematikk ()), kaller du metodene fra klassen selv.

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

En metode kan også være statisk: Hvis en klasse kan bli instantiated, men også har noen klassenivåmetoder, anses disse metodene for å være statiske (de kalles på klassen, ikke forekomsten).

 / ** @ klasse Person @constructor * / funksjon Person ()  / ** @metode alle @static * / Person.all = funksjon () ;

I dette eksemplet kan du opprette en Person eksempel, men alle Metoden er statisk.

@endelig

Denne taggen brukes til egenskaper eller attributter, og markerer egenskapen som en konstant: den bør ikke endres. Selv om JavaScript ikke har ekte konstanter i sin nåværende tilstand, kan kodemønsteret eller stilguiden bruke dem i prinsippet, så dette vil være nyttig for det.

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

@param

Her er en viktig en: @param tag brukes til å definere parametrene for a @metode (inkludert a @constructor) eller en @begivenhet. Det er tre biter av info som går etter @param tag: navnet på parameteren, typen (som er valgfri) og beskrivelsen. Disse kan enten være i rekkefølgen navn type beskrivelse eller type navn beskrivelse; men i begge tilfeller må typen være omgitt av krøllete bånd.

 / ** @method hilse @param person streng Navn på personen å hilse * / funksjon hilse (person) 

Det er noen måter å tilpasse Navn del også. Å sette den i firkantede parentes markerer den som valgfri, mens du setter = someVal etter at det viser hva standardverdien er (åpenbart har bare valgfrie parametere en standardverdi). Deretter, hvis det er en plassholder for mer enn ett argument, legger du til * å vise det. (Åpenbart, Navn* er en plassholder for 1 eller flere argumenter, mens [Navn]* er en plassholder for 0 eller flere).

 / ** @class Template @constructor @param template String Malenstrengen @param [data = ] Objekt Objektet hvis egenskaper skal gjengis i malen * / funksjon Mal (mal, data) 

@komme tilbake

De fleste metodene dine vil ønske å returnere en verdi, så dette er taggen som beskriver den verdien. Ikke glem å fortelle hva typen er verdien, og gi den en beskrivelse.

 / ** @method toHTML @param [template = Recipe.defaultTemplate] Malform En malobjekt @return String Oppskriftsinnholdet formatert i HTML med standard eller innleveringsmal. * / Oppskrift.prototype.toHTML = funksjon (mal) return "uansett"; ;

@type

Husk @eiendom primærkode? Du vil definere hvilken type disse egenskapene er, ikke sant? Vel, den @type tag er akkurat det du trenger. Angi typen etter merket; Du kan også tilby flere typer ved å skille dem med loddrette barer:

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

@privat / @protected

Tradisjonelle programmeringsspråk tilbyr private egenskaper eller metoder: disse er ikke tilgjengelige fra utenfor forekomsten. På samme måte som konstanter, har JavaScript dem bare etter praksis, men du kan bruke @privat å merke disse hvis du bruker dem. Vær oppmerksom på at YUIDoc ikke viser private egenskaper i dokumentene den genererer (det er fornuftig), slik at dette gjør at du kan dokumentere en funksjon til din fordel, og ikke få den til å vises i dokumentene.

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

Beskyttede egenskaper og metoder er halvveis mellom offentlige og private: de er bare tilgjengelige fra forekomster og forekomster av underklasser. Hvis det er en ting du gjør i JavaScript, her er taggen din: @protected.

@requires

Hvis en modul er avhengig av en eller flere andre moduler, kan du bruke @requires å markere det:

 / ** @module MyFramework.localstorage @ krever MyFramework * /

Noter det @requires kan også ta en liste over avhengigheter, skilt av kommaer.

@misligholde

Når deklarerer a @eiendom, Du kan finne det nyttig å gi det en @misligholde verdi. @misligholde bør alltid brukes med @type.

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

@bruker

Som vi har sagt, har JavaScript egentlig ingen klasser, men det er fleksibelt nok til å skape illusjon av klasser og til og med underklasser. Hva er enda mer kult er at det er fleksibelt nok til å ha mixins eller moduler: dette er hvor en klasse "låner" egenskaper eller metoder fra en annen klasse. Og det er heller ikke arv, fordi du kan blande i deler av mer enn en klasse (selvfølgelig har YUI muligheten til å gjøre dette, men det gjør du også Dojo og andre biblioteker). Hvis du gjør dette, finner du det @bruker veldig nyttig: det lar deg erklære hvilke klasser en bestemt klasse blander i deler av.

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

Merk: Jeg har akkurat gjort opp den blandede syntaksen, men jeg er ganske sikker på at jeg har sett noe lignende et sted.

@eksempel

Vil du inkludere et eksempel på hvordan du bruker et bestemt stykke kode? Bruke @eksempel tag, og skriv deretter eksemplet under, kryp det ett nivå. Du kan legge til så mange eksempler som du vil.

 / ** @method hilse @ eksempel person.greet ("Jane"); * / Person.prototype.greet = funksjon (navn) ;

@chainable

Du er sikkert kjent med chainable metoder fra jQuery. Du vet, hvor du kan ringe en metode av en metodeanrop, fordi metodene returnerer objektet? Merk dine metoder som sådan med @chainable.

 / ** @metode addClass @chainable * / jQuery.prototype.addClass = funksjon (klasse) // stuff; returnere dette; 

@deprecated / @siden / @beta

Disse tre kodene handler om støtte til koden (og det kan være noen kode: modul, klasse, metode eller noe annet). Bruk @deprecated for å markere noen funksjonalitet som ikke lenger den beste måten å gjøre det på (den utdaterte funksjonaliteten vil trolig bli fjernet i en fremtidig versjon av koden). Eventuelt kan du inkludere en melding som forklarer hva den nåværende måten å gjøre det er.

 / ** @method toJSON @deprecated Pass objektet til 'JSON.parse' i stedet * / Something.toJSON = function () ;

De @siden tag bare forteller leserne hvilken versjon den oppgitte koden, hva lagt til i. Og @beta markerer beta-kode: YUI foreslår at @beta kode kan "gjennomgå bakover-inkompatible endringer i nær fremtid."

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

@extension / @extensionfor / extension_for

De @extension tag (og dets aliaser) er ganske mye det motsatte av @bruker. Bruk den til å markere hvilke klasser utvidelsesklassen kan blandes inn i. Selvfølgelig, skjønner at dette ikke betyr at det alltid er blandet inn, akkurat det det kan være.

 / ** @class Draggable @extensionfor ModalWindow * /

Kommentarer og Markdown

Før vi ser på et egentlig eksempel, la meg påpeke to ting om dokumentasjonskommentarene.

Først vil du ofte legge til litt mer informasjon om koden din enn hva merkene tilbyr. Kanskje du vil beskrive formålet med metodene, eller hvordan en klasse passer inn i det større bildet. Legg til disse kommentarene øverst i kommentarblokken, over noen av kodene. YUIDoc vil legge merke til dem og inkludere dem i dokumentasjonen.

 / ** Klassen "Router" brukes til ... @class Router @statisk * / var Router = ;

For det andre vil du være glad for å vite at disse kommentarene, samt eventuelle beskrivelser eller meldinger skrevet etter merkene, kan skrives i Markdown, og YUIDoc vil konvertere den til riktig HTML. Du kan til og med sette inn eksempelkodeblokker i kommentarene dine og få syntaxutheving!


Et eksempel

Nå som du har lært taggene, la oss faktisk skrive noen kode og dokumentere den. La oss lage en butikk modul, som holder to klasser: Punkt og kurven. Hver Punkt forekomst vil være en type gjenstand i lagerbeholdningen: den vil ha et navn, en pris og en mengde. EN kurven eksempel kan legge til varer i handlekurven og beregne totalprisen på varene i handlekurven (inkludert skatt). Det er ganske enkelt, men gir oss nok variert funksjonalitet til å bruke mange av kodene vi har diskutert. Jeg har satt inn all følgende kode store.js.

Vi begynner med å lage modulen:

 / ** * Denne modulen inneholder klasser for å kjøre en butikk. * @module Store * / var Store = Store || ;

La oss nå lage en "konstant": skattesatsen.

 / ** * 'TAX_RATE' lagres i prosent. Verdien er 13. * @property TAX_RATE * @static * @final * @type Number * / Store.TAX_RATE = 13;

Dette er en konstant (@endelig) @eiendom av @type Nummer. Legg merke til at jeg har tatt med @static: Dette er fordi, av en eller annen grunn, når vi genererer dokumentasjonen for denne filen, vil YUIDoc vise dette som en eiendom av vår Punkt klasse: Det ser ut til at YUIDoc ikke støtter å ha en eiendom på en modul. Jeg antar at jeg kunne lage en statisk klasse for å holde denne konstante (og andre konstanter som kan komme hvis vi videreutviklet dette), men jeg har forlatt det på denne måten for en påminnelse: å bruke et verktøy som YUIDoc til sitt fulle potensiale, må kanskje endre måten du kode på. Du må bestemme om det er det du vil gjøre.

Nå, for Punkt klasse:

 / ** * @class Item * @constructor * @param navn String Produktnavn * @param pris Nummer Produktpris * @param mengde Antall Varekvantum (nummeret tilgjengelig for kjøp) * / Store.Item = funksjon (navn, pris, mengde) / ** * @property navn * @type String * / this.name = navn; / ** * @property pris * @type String * / this.price = pris * 100; / ** * @property mengde * @type Antall * / this.quantity = quantity; / ** * @property id * @type Number * / this.id = Store.Item._id ++; Store.Item.list [this.id] = this; ;

Som du kan se, har denne konstruktøren tre parametere. Deretter er det tre egenskaper i konstruktøren som vi også beskriver. Siden vi vil gi hver Punkt en unik ID, må vi lagre en statisk egenskap (klasse-nivå) for å øke ID-en, og en annen statisk egenskap, en gjenstand som sporer Punkts ved deres ID.

 / ** * '_id' økes når et nytt element er opprettet, så hvert element har en unik ID * @property id * @type Number * @static * @private * / Store.Item._id = 1; / ** * @property list * @static * @type Object * / Store.Item.list = ;

Hva med kurven klasse?

 / ** * @class Cart * @constructor * @param navn String Kunde navn * / Store.Cart = funksjon (navn) / ** * @property navn * @type String * / this.name = navn; / ** * @property elementer * @type Object * @default  * / this.items = ; ;

Det er egentlig ikke noe nytt her: Legg merke til at vi erklærer at standard (eller innledende) tilstanden til elementer Egenskapen er en tom gjenstand.

Nå, metodene. For addItem, En av parametrene er valgfri, så vi erklærer det som det, og gir det en standardverdi på 1. Merk også at vi gjør metoden @chainable.

 / ** * Legger til 1 eller flere av et gitt element i handlekurven, hvis det valgte antallet * er tilgjengelig. Hvis ikke, blir ingen lagt til. * * @method addItem * @param element Objekt En 'Objekt' Objekt * @param [quantity = 1] Number Antallet elementer som skal legges til handlekurven * @chainable * / Store.Cart.prototype.addItem = funksjon (vare, mengde) quantity = quantity || 1; if (item.quantity> = quantity) this.items [item.id] = this.items [item.id] || 0; this.items [item.id] + = quantity; item.quantity - = quantity;  returner dette ;

Til slutt ønsker vi å kunne returnere totalprisen, inkludert skatter. Legg merke til at vi gjør prisen matte i cent, og deretter konvertere til dollar og avrunde til to desimaler.

 / ** * @metode total * @return Number skatt-inkludert totalverdi av handlevogn innhold * / Store.Cart.prototype.total = function () var subtotal, id; subtotal = 0; for (id i this.items) if (this.items.hasOwnProperty (id)) subtotal + = Store.Item.list [id] .price * this.items [id];  returnere parseFloat (((subtotal * (1 + Store.TAX_RATE / 100)) / 100) .toFixed (2)); ;

Hvis du vil teste denne koden, er det enkle tester:

 var eple, pære, bok, skrivebord, assertEquals; assertEquals = funksjon (en, to, msg) console.log (((ett === to)? "PASS:": "FAIL:") + msg); ; Apple = New Store.Item ('Granny Smith Apple', 1,00, 5); pære = ny 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); Vogn = Ny butikk.Cart ('Andrew'); cart.addItem (apple, 1) .addItem (book, 3) .addItem (skrivebord, 1); assertEquals (apple.quantity, 4, "adding 1 apple fjerner 1 fra varemerket"); assertEquals (book.quantity, 2, "prøver å legge til flere bøker enn det er betyr at ingen er lagt til"); assertEquals (cart.total (), 140.63, "totalpris for 1 eple og 1 skrivebord er 140,63");

Generering av dokumentasjonen

Nå som vi har skrevet koden og kommentarblokkene, er det på tide å generere dokumentasjonen.

Hvis du har installert den globalt via npm, kan du bare kjøre yuidoc bane til js. I mitt tilfelle er det det

 yuidoc .

Nå ser du at du har en ute katalog i den mappen; åpen ut / index.html, og du vil se dokumentasjonen. Her er hvilken del av kurven klassen dokumentasjon vil se ut som:


Konfigurere utgang

Det er flere konfigurasjonsalternativer du kan angi når du bruker YUIDoc. Jo, du kan sette dem som kommandolinjeflagger, men jeg vil helst sette dem i en JSON-konfigurasjonsfil. I prosjektkatalogen din, opprett en fil som heter yuidoc.json. For det første er det en haug med generell prosjektinformasjon du kan angi; Dette påvirker ikke produksjonen for mye, men det er godt å dokumentere dem:

 "navn": "Dokumentasjon av JavaScript med YUIDoc", "beskrivelse": "En opplæring om YUIDoc, for Nettuts +", "versjon": "1.0.0", "url": "http://net.tutsplus.com "

Deretter er det en rekke faktiske alternativer du kan angi. Her er et par interessante;

  • linkNatives: sett dette til "true" for å knytte innfødte typer som String eller Nummer til MDN-dokumentene.
  • outdir: Bruk denne til å gi nytt navn til ute katalog
  • baner: Bruk dette til å angi hvilke stier YUIDoc søker etter JavaScript-filer.
  • utelukke: sett dette til en kommaseparert liste over filer du vil at YUIDoc skal ignorere.

Så lenge du setter baner alternativer, du kan kjøre yuidoc -c yuidoc.json og YUIDoc vil kjøre. Selv om du ikke angir baner og bare løp yuidoc ., YUIDoc vil se den konfigurasjonsfilen og bruke den.

Her er min totale konfigurasjonsfil for dette prosjektet:

 "navn": "Dokumentasjon av JavaScript med YUIDoc", "beskrivelse": "En opplæring om YUIDoc, for Nettuts +", "versjon": "1.0.0", "url": "http://net.tutsplus.com "," alternativer ": " linkNatives ":" true "," outdir ":" ./docs "," paths ":". " 

evaluering

Basert på kodene YUIDoc tilbyr, kan du se at den ble laget for JavaScript skrevet i tradisjonell OOP-stil, så vel som spesielt for YUI-widgets og slikt (faktisk har jeg utelatt flere koder som var YUI-spesifikke). På grunn av alt dette kan det hende du finner ut at flere koder bare ikke er så nyttige for deg. Deretter må du spørre deg selv om du er villig til å endre kodestilen din for å bedre passe den måten YUIDoc "tenker på." Men selv om du ikke kommer til å endre, tror jeg du finner at de fleste YUIDoc-kodene passer helt fint.

Det største spørsmålet for meg er om du vil ha dokumentasjonen din i tråd med koden din.

Eksempelkoden vi skrev over er 120 linjer med kommentarer, 40 linjer uten. Det er åpenbart at det er super enkel kode, og stort sett ville noen ekteeksempel være mer balansert; Det kan imidlertid være vanskelig å lese en slik interspersed kode. Personlig tror jeg at jeg skal gi YUIDoc en rettferdig rettssak: Jeg skal dokumentere JavaScript når jeg skriver det (eller i det minste langs siden) i de neste ukene. Jeg vil være interessert i å se om eller hvordan det påvirker kodestilen og arbeidsflyten min.

Du kjenner rutinen: elsk det eller hater det, gi meg beskjed i kommentarene!


For mer

  • YUIDoc 0.3.0 Slett blogginnlegg
  • YUIDoc Hjemmeside
  • Bruke YUIDoc
  • YUIDoc-syntaksreferanse
  • YUIDoc Temaer