Documentazione di JavaScript con YUIDoc

Documentare il tuo codice è un po 'come test; sappiamo tutti che dovremmo farlo, non siamo veramente sicuri di come, e la maggior parte della gente, se siamo onesti, semplicemente no, ma quelli che lo fanno sono grandi sostenitori di ciò. Questo tutorial ti farà conoscere uno dei modi migliori per affrontarlo: YUIDoc.


Cos'è YUIDoc?

YUIDoc genererà la documentazione API in base ai commenti che scrivi.

YUIDoc è un'app NodeJS che genera documentazione API (sotto forma di HTML), in base ai commenti che scrivi nel codice sorgente JavaScript. In realtà, non è solo per JavaScript: qualsiasi linguaggio di programmazione che supporta i commenti di blocco delimitati da / * * / lavora per YUIDoc. Come puoi immaginare, YUIDoc è uno degli strumenti che Yahoo! pubblica insieme alla loro biblioteca YUI.

Per installare YUIDoc, avrai bisogno prima di NodeJS e del gestore di pacchetti Node (npm). Quindi, è possibile installare YUIDoc tramite npm -g installa yuidocjs. Lo userai correndo yuidoc ; più su questo più tardi.


È tutto sui tag

Quindi, sai che YUIDoc ottiene la sua documentazione dai commenti multilinea nel file sorgente. Naturalmente, potresti avere commenti che non fanno parte della documentazione. Affinché YUIDoc riconosca un commento come significativo, deve iniziare con un doppio avvio: / **. Così:

 / ** YUIDoc elaborerà questo * / / * Ma non questo * /

Certo, è ciò che conta dentro (dentro i blocchi dei commenti, cioè). Ciascuno deve includere uno e un solo tag principale; può anche includere zero o più tag secondari. In realtà, YUIDoc è così semplice: aggiungi i commenti con i tag giusti al tuo codice, e presto: documentazione! Quindi impariamo alcuni tag. Ecco come faremo: analizzeremo i tag e dove vengono utilizzati, con semplici esempi dei loro usi; quindi, scriveremo e documenteremo del codice in modo da avere un'idea migliore di come i tag interagiscono.


Tag primari

Prima di entrare nei tag principali, ricorda che ogni blocco di commenti può avere solo un singolo tag primario. Questi descrivono cos'è un dato pezzo di codice.

@modulo

Il @modulo tag descrive un gruppo di classi correlate. (Sì, sì, JavaScript non ha classi: YUIDoc si riferisce alle funzioni di costruzione.) Se stavi usando YUIDoc per documentare BackboneJS, il Spina dorsale oggetto sarebbe un modulo, perché contiene il Modello, Collezione, vista, e altre classi. Subito dopo il tag, inserisci il nome del modulo.

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

@classe

Il @classe tag descrive appropriatamente una singola classe. Nella libreria YUI, questo di solito significa una funzione di costruzione, ma se preferisci usare un modello diverso e chiamare la tua classe, puoi farlo anche tu. Ogni commento con a @classe il tag dovrebbe avere anche un @statico o @costruttore tag (tag secondari di cui parleremo a breve).

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

Se la tua classe fa parte di un modulo, non devi fare nulla all'interno del @classe commentare per designare questo: assicurati che ci sia un @modulo blocco di commenti nella parte superiore di quel file.

@metodo

Naturalmente, ogni classe avrà almeno alcuni metodi, e tu userai il @metodo tag per descriverli. Il nome del metodo seguirà il tag e utilizzerai i tag secondari @ritorno e @params per descrivere il metodo.

 / ** @method render * / View.prototype.render = function (data) 

@proprietà

Il @proprietà il tag è usato per taggare le proprietà di una classe. Ti consigliamo di utilizzare il @genere e @predefinito tag secondari con questo, di sicuro.

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

@evento

Se hai eventi speciali speciali che una classe può licenziare, ti consigliamo di utilizzare @evento tag per descriverli. Ecco cosa ha da dire la documentazione YUIDoc:

Un @evento il blocco è in qualche modo simile a a @metodo blocco, tranne quello @ritorno è irrilevante, e @param è usato per descrivere le proprietà che pendono dall'oggetto evento che i callback ascoltano per la ricezione dell'evento.


Tag secondari

I blocchi di commenti possono avere più di un tag secondario; spesso ne hanno una manciata, a volte anche più di una dello stesso tipo. Diamo un'occhiata ad alcuni di quelli che userete spesso.

@submodule

Se stai dividendo i tuoi moduli in sottomoduli (forse un sottomodulo per file, forse no), il @submodule il tag è al tuo servizio.

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

@extends

Il @extends il tag è utile quando hai relazioni di superclasse / sottoclasse. Puoi rivendicare quale classe è il genitore della classe attualmente documentata:

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

@costruttore

Se una classe può essere istanziata, significa che ha bisogno di una funzione di costruzione. Se stai utilizzando il modello prototipo standard in JavaScript, la dichiarazione di classe è anche il costruttore. Ciò significa che vedrai spesso qualcosa del genere:

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

In effetti, probabilmente mi ricordi di averlo detto ogni @classe il tag dovrebbe avere a @costruttore o @statico tag secondario.

@statico

Parlando di @statico, Ecco qui. Una classe è considerata statica quando non è possibile crearne un'istanza. Un buon esempio di questo è il built-in Matematica oggetto: non ne crei mai un'istanza (nuovo Math ()), tu chiami i suoi metodi dalla classe stessa.

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

Un metodo può anche essere statico: se una classe può essere istanziata, ma ha anche alcuni metodi a livello di classe, questi metodi sono considerati statici (vengono chiamati sulla classe, non sull'istanza).

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

In questo esempio, puoi creare un Persona istanza, ma il tutti il metodo è statico.

@finale

Questo tag viene utilizzato per proprietà o attributi e contrassegna detta proprietà come costante: non dovrebbe essere modificata. Mentre JavaScript non ha costanti reali nel suo stato attuale, il tuo modello di codice o la guida di stile potrebbero usarle in linea di principio, quindi questo sarà utile per quello.

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

@param

Ecco un importante: il @param tag è usato per definire i parametri di a @metodo (incluso a @costruttore) o a @evento. Ci sono tre bit di informazioni che vanno dopo il @param tag: il nome del parametro, il tipo (che è facoltativo) e la descrizione. Questi possono essere nell'ordine nome descrizione del tipo o tipo nome descrizione; ma in entrambi i casi, il tipo deve essere circondato da parentesi graffe.

 / ** @method greet @param person string Il nome della persona da salutare * / function greet (person) 

Ci sono alcuni modi per personalizzare il nome parte pure. Metterlo tra parentesi quadre lo contrassegna come facoltativo, mentre lo si inserisce = someVal dopo mostra quale è il valore predefinito (ovviamente, solo i parametri opzionali hanno un valore predefinito). Quindi, se è un segnaposto per più di un argomento, append * per dimostrarlo. (Ovviamente, nome* è un segnaposto per 1 o più argomenti, mentre [nome]* è un segnaposto per 0 o più).

 / ** @class Template @constructor @param template String The template string @param [data = ] Object L'oggetto le cui proprietà saranno renderizzate nel template * / function Template (template, data) 

@ritorno

La maggior parte dei tuoi metodi vorranno restituire un valore, quindi questo è il tag che descrive quel valore. Non dimenticare di dirgli di che tipo è il valore e dargli una descrizione.

 / ** @method toHTML @param [template = Recipe.defaultTemplate] Template Un oggetto modello @return String Il contenuto della ricetta formattato in HTML con il modello predefinito o passato. * / Recipe.prototype.toHTML = function (template) return "whatever"; ;

@genere

Ricorda il @proprietà tag principale? Dovrai definire di che tipo sono queste proprietà, giusto? Bene, il @genere tag è proprio quello che ti serve. Specificare il tipo dopo il tag; puoi anche offrire più tipi separandoli con barre verticali:

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

@privato / @protected

I linguaggi di programmazione tradizionali offrono proprietà o metodi privati: questi non sono accessibili dall'esterno. Proprio come le costanti, JavaScript li ha solo con la pratica, ma puoi usarli @privato taggare questi se li usi. Nota che YUIDoc non mostra proprietà private nei documenti che genera (ha senso), quindi questo ti permette di documentare una funzionalità a tuo vantaggio e di non mostrarla nei documenti.

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

Proprietà e metodi protetti sono a metà strada tra pubblico e privato: sono accessibili solo all'interno di istanze e istanze di sottoclassi. Se è una cosa che fai in JavaScript, ecco il tuo tag: @protected.

@richiede

Se un modulo dipende da uno o più altri moduli, puoi utilizzare @richiede per sottolineare che:

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

Nota che @richiede potrebbe anche prendere una lista di dipendenze, separate da virgole.

@predefinito

Quando si dichiara a @proprietà, potresti trovare utile dargli un @predefinito valore. @predefinito dovrebbe sempre essere usato con @genere.

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

@uses

Come abbiamo detto, JavaScript non ha realmente classi, ma è abbastanza flessibile da creare l'illusione delle classi e persino delle sottoclassi. Ciò che è ancora più interessante è che è abbastanza flessibile da avere mixin o moduli: è qui che una classe "prende in prestito" proprietà o metodi da un'altra classe. Inoltre, non è un'eredità, perché puoi mescolare parti di più di una classe (Ovviamente, YUI ha la capacità di farlo, ma anche Dojo e altre librerie). Se lo fai, lo troverai @uses molto utile: ti permette di dichiarare quali classi una data classe si sta mescolando in parti di.

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

Nota: ho appena composto questa sintassi mix, ma sono abbastanza sicuro di aver visto qualcosa di simile da qualche parte.

@esempio

Vuoi includere un esempio di come utilizzare un particolare pezzo di codice? Utilizzare il @esempio tag, quindi scrivi l'esempio seguente, indentandolo un livello. Puoi aggiungere tutti gli esempi che desideri.

 / ** @method greet @esempio person.greet ("Jane"); * / Person.prototype.greet = function (name) ;

@chainable

Probabilmente hai familiarità con i metodi concatenabili di jQuery. Sai, dove puoi chiamare un metodo da una chiamata al metodo, perché i metodi restituiscono l'oggetto? Segna i tuoi metodi come tali con @chainable.

 / ** @method addClass @chainable * / jQuery.prototype.addClass = function (class) // stuff; restituiscilo; 

@deprecated / @da / @beta

Questi tre tag riguardano esclusivamente il supporto per il codice (e potrebbe essere qualsiasi codice: modulo, classe, metodo o qualcos'altro). Uso @deprecated per contrassegnare alcune funzionalità come non più il modo migliore per farlo (la funzionalità deprecata verrà probabilmente rimossa in una versione futura del codice). Opzionalmente, puoi includere un messaggio che spiega quale sia il modo attuale di farlo.

 / ** @method toJSON @deprecated Passa invece l'oggetto a 'JSON.parse' invece * / Something.toJSON = function () ;

Il @da tag indica solo ai lettori quale versione del codice specificato ha aggiunto. E @beta segna il codice beta: YUI lo suggerisce @beta il codice potrebbe "subire cambiamenti non compatibili all'indietro nel prossimo futuro".

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

@estensione / @extensionfor / extension_for

Il @estensione tag (e i suoi alias) è praticamente l'opposto di @uses. Usalo per contrassegnare in quali classi è possibile mescolare la classe di estensione. Certo, renditi conto che questo non significa che sia sempre mescolato, solo che può essere.

 / ** @class Draggable @extensionfor ModalWindow * /

Commenti e Markdown

Prima di esaminare un esempio concreto, vorrei sottolineare altre due cose sui blocchi di commento della documentazione.

In primo luogo, vorrete aggiungere un po 'più informazioni sul vostro codice rispetto a ciò che offrono i tag. Forse vuoi descrivere lo scopo dei metodi, o come una classe si inserisce nel quadro più ampio. Aggiungi questi commenti nella parte superiore del blocco dei commenti, sopra i tag. YUIDoc li noterà e li includerà nella documentazione.

 / ** La classe 'Router' è usata per ... @class Router @static * / var Router = ;

In secondo luogo, sarete felici di sapere che questi commenti, così come ogni descrizione o messaggio scritto dopo i tag, possono essere scritti in Markdown e YUIDoc lo convertirà nell'HTML corretto. Puoi anche indentare esempi di blocchi di codice nei tuoi commenti e ottenere l'evidenziazione della sintassi!


Un esempio

Ora che hai imparato i tag, scriviamo effettivamente un codice e lo documentiamo. Creiamo a Memorizzare modulo, che contiene due classi: Articolo e Carrello. Ogni Articolo l'istanza sarà un tipo di articolo nell'inventario del negozio: avrà un nome, un prezzo e una quantità. UN Carrello l'istanza può aggiungere articoli al carrello e calcolare il prezzo totale degli articoli nel carrello (tasse incluse). È abbastanza semplice, ma ci offre funzionalità abbastanza diverse per utilizzare molti dei tag che abbiamo discusso. Ho inserito tutto il codice seguente store.js.

Iniziamo creando il modulo:

 / ** * Questo modulo contiene classi per l'esecuzione di un negozio. * @module Store * / var Store = Negozio || ;

Ora creiamo una "costante": l'aliquota d'imposta.

 / ** * 'TAX_RATE' è memorizzato come percentuale. Il valore è 13. * @property TAX_RATE * @static * @final * @type Number * / Store.TAX_RATE = 13;

Questa è una costante (@finale) @proprietà di @genere Numero. Avviso che ho incluso @statico: questo perché, per qualche motivo, quando generiamo la documentazione per questo file, YUIDoc lo mostrerà come una proprietà del nostro Articolo classe: sembra che YUIDoc non supporti una proprietà su un modulo. Immagino di poter creare una classe statica per mantenere questa costante (e altre costanti che potrebbero venire se lo sviluppassimo ulteriormente), ma ho lasciato in questo modo un promemoria: per utilizzare uno strumento come YUIDoc al massimo delle potenzialità, potrebbe dover cambiare il modo in cui si codifica. Dovrai decidere se è quello che vuoi fare.

Ora, per il Articolo classe:

 / ** * @class Item * @constructor * @param name String Nome articolo * @ prezzo articolo numero Prezzo articolo * @ quantità quantità numero Quantità articolo (il numero disponibile per l'acquisto) * / Store.Item = funzione (nome, prezzo, quantità) / ** * @property name * @type String * / this.name = name; / ** * prezzo @property * @type String * / this.price = price * 100; / ** * quantità @property * @type Number * / this.quantity = quantity; / ** * @property id * @type Number * / this.id = Store.Item._id ++; Store.Item.list [this.id] = this; ;

Come puoi vedere, questo costruttore ha tre parametri. Quindi, ci sono tre proprietà all'interno del costruttore che stiamo anche descrivendo. Dal momento che vogliamo dare ogni Articolo un ID univoco, abbiamo bisogno di memorizzare una proprietà statica (a livello di classe) per incrementare l'ID e un'altra proprietà statica, un oggetto che tiene traccia del Articolos dal loro ID.

 / ** * '_id' viene incrementato quando viene creato un nuovo elemento, quindi ogni elemento ha un ID univoco * @property id * @tipo numero * @static * @private * / Store.Item._id = 1; / ** * lista @property * @static * @type Object * / Store.Item.list = ;

Che ne dici di Carrello classe?

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

Non c'è davvero nulla di nuovo qui: notiamo che stiamo dichiarando che lo stato predefinito (o iniziale) del elementi la proprietà è un oggetto vuoto.

Ora, i metodi. Per il Aggiungi articolo, uno dei parametri è facoltativo, quindi lo dichiariamo come tale e diamo un valore predefinito di 1. Inoltre, notiamo che creiamo il metodo @chainable.

 / ** * Aggiunge 1 o più di un dato articolo al carrello, se la quantità scelta * è disponibile. In caso contrario, nessuno viene aggiunto. * * @method addItem * @param item Object Un oggetto 'Item' * @param [quantity = 1] Number Il numero di elementi da aggiungere al carrello * @chainable * / Store.Cart.prototype.addItem = funzione (articolo, quantità) quantità = quantità || 1; if (item.quantity> = quantity) this.items [item.id] = this.items [item.id] || 0; this.items [item.id] + = quantity; item.quantity - = quantity;  restituisci questo; ;

Infine, vogliamo essere in grado di restituire il prezzo totale, tasse incluse. Si noti che stiamo calcolando il prezzo in centesimi, quindi convertendo in dollari e arrotondando a due cifre decimali.

 / ** * @method total * @return Number valore totale incluso del valore del carrello * / Store.Cart.prototype.total = function () var subtotal, id; subtotale = 0; for (id in this.items) if (this.items.hasOwnProperty (id)) subtotal + = Store.Item.list [id] .price * this.items [id];  return parseFloat (((subtotal * (1 + Store.TAX_RATE / 100)) / 100) .toFixed (2)); ;

Se vuoi testare questo codice, ecco un semplice test:

 var mela, pera, libro, scrivania, assertEquals; assertEquals = function (one, two, msg) console.log ((una === due)? "PASS:": "FAIL:") + msg); ; apple = new Store.Item ('Granny Smith Apple', 1.00, 5); pera = nuovo 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); carrello = nuovo Store.Cart ('Andrew'); cart.addItem (apple, 1) .addItem (book, 3) .addItem (desk, 1); assertEquals (apple.quantity, 4, "l'aggiunta di 1 mela rimuove 1 dalla quantità dell'articolo"); assertEquals (book.quantity, 2, "tentare di aggiungere più libri di quanti ce ne siano)"; assertEquals (cart.total (), 140,63, "prezzo totale per 1 mela e 1 banco è 140,63");

Generazione della documentazione

Ora che abbiamo scritto il codice ei blocchi dei commenti, è il momento di generare la documentazione.

Se lo hai installato globalmente via npm, sarai in grado di eseguire semplicemente yuidoc path to js. Nel mio caso, quello è

 yuidoc .

Ora vedrai che hai un su directory in quella cartella; Aperto out / index.html, e vedrai la documentazione. Ecco quale parte del Carrello la documentazione di classe sarà simile a:


Configurazione dell'output

Ci sono diverse opzioni di configurazione che puoi impostare quando usi YUIDoc. Certo, puoi impostarli come flag a riga di comando, ma preferisco impostarli in un file di configurazione JSON. Nella directory del progetto, crea un file chiamato yuidoc.json. Per prima cosa, ci sono un sacco di informazioni generali sul progetto che puoi impostare; questo non influisce molto sull'output, ma è bene documentarli:

 "nome": "Documentazione di JavaScript con YUIDoc", "descrizione": "Un tutorial su YUIDoc, per Nettuts +", "versione": "1.0.0", "url": "http://net.tutsplus.com "

Quindi, ci sono un certo numero di opzioni reali che puoi impostare. Qui ci sono un paio di quelli interessanti;

  • linkNatives: imposta questa opzione su "true" per collegare tipi nativi come String o Number ai documenti MDN.
  • outdir: usa questo per rinominare il su elenco
  • percorsi: usa questo per impostare quali percorsi YUIDoc cerca i file JavaScript.
  • escludere: imposta questo in un elenco di file separati da virgola che YUIDoc deve ignorare.

Finché imposti il percorsi opzioni, puoi correre yuidoc -c yuidoc.json e YUIDoc funzionerà. Anche se non si imposta percorsi e corri yuidoc ., YUIDoc vedrà quel file di configurazione e lo applicherà.

Ecco il mio file di configurazione completo per questo progetto:

 "nome": "Documentazione di JavaScript con YUIDoc", "descrizione": "Un tutorial su YUIDoc, per Nettuts +", "versione": "1.0.0", "url": "http://net.tutsplus.com "," options ": " linkNatives ":" true "," outdir ":" ./docs "," paths ":". " 

Valutazione

Sulla base dei tag offerti da YUIDoc, è possibile vedere che è stato creato per JavaScript scritto in stile OOP tradizionale, oltre che appositamente per i widget YUI e simili (in effetti, ho omesso diversi tag che erano specifici per YUI). A causa di tutto questo, potresti scoprire che diversi tag non sono così utili per te. Poi, devi chiederti se sei disposto a cambiare il tuo stile di codice per adattarlo meglio al modo in cui YUIDoc "pensa". Ma anche se non cambierai, penso che troverai che la maggior parte dei tag YUIDoc si adatterà bene.

La più grande domanda per me è se ti piace avere la tua documentazione in linea con il tuo codice.

Il codice di esempio che abbiamo scritto sopra è di 120 righe con commenti, 40 righe senza. Ovviamente, questo è un codice super semplice, e praticamente qualsiasi esempio del mondo reale sarebbe più equilibrato; tuttavia, leggere questo codice intercalato potrebbe essere difficile. Personalmente, penso che darò a YUIDoc un processo equo: documenterò il mio JavaScript mentre lo scrivo (o almeno, accanto a lui) per le prossime settimane. Sarò interessato a vedere se o come influenza il mio stile di codifica e il flusso di lavoro.

Conosci la routine: amala o odi, fammelo sapere nei commenti!


Per più

  • Post sul blog di YUIDoc 0.3.0
  • YUIDoc Home Page
  • Utilizzando YUIDoc
  • Riferimento della sintassi YUIDoc
  • Temi YUIDoc