Creazione di un sito multipagina con meteore

Come con qualsiasi applicazione Web, la creazione di siti multipagina richiede un set specializzato di strumenti. In questo articolo, daremo un'occhiata allo sviluppo di una libreria che non solo può differenziare tra i diversi URI, ma uno che sfrutta le caratteristiche principali di Meteor.


Funzioni della libreria previste

Ogni volta che devo sviluppare una libreria specifica e focalizzata come questa, mi piace iniziare con il risultato di, come voglio che funzioni?

Quindi, possiamo iniziare scrivendo alcune delle funzionalità che vorremmo avere:

  • La possibilità di caricare pagine diverse in base all'URI
  • Lettura dei parametri dall'URI (segnaposto)
  • Mantenere le pagine dinamiche secondo lo standard Meteor

Sembra abbastanza buono. Ora dopo aver esaminato queste funzionalità, si potrebbe pensare che siano abbastanza simili a un tipo di libreria "router" e sono d'accordo. Quindi, diamo un'occhiata a come la nostra libreria 'router' funzionerebbe in azione:

Router.addRoute ('/ home', 'homeTemplate'); Router.addRoute ('/ user /: username', 'profileTemplate'); Router.addRoute ('/ contact', 'contactTemplate'); Router.run ();

Nel design, hai concetti come 'form-follows-function', che usa l'idea di mettere tutto per primo e di progettarlo in un secondo momento.

Nel codice, trovo spesso più utile il contrario. Noi come sviluppatori, possiamo lavorare in molte direzioni e avere un esempio funzionante di come dovrebbe essere il design, ci tiene concentrati ed efficienti.

Ora che so cosa voglio fare, è solo questione di implementarlo. Quindi, diamo un'altra occhiata alle nostre funzionalità che abbiamo scritto sopra; vogliamo essere in grado di aggiungere rotte e fare in modo che Meteor restituisca il modello specificato. Naturalmente, le visualizzazioni di Meteor funzionano sui modelli del manubrio per impostazione predefinita, quindi ho preso la decisione di rendere questa la denominazione del nostro router.

La ripartizione

Quindi, suddividiamo il progetto in diverse sezioni in modo da sapere su quali funzionalità dobbiamo lavorare.

  • Inizieremo recuperando l'URI della pagina corrente, in quanto dobbiamo abbinare le rotte a qualcosa. Questo può essere fatto, abbastanza facilmente, usando il window.location.pathname variabile, fornita dal browser.
  • Successivamente, abbiamo bisogno della possibilità di aggiungere percorsi. Anche questo è abbastanza semplice ora che abbiamo fatto qualche esempio di codice; avremo una funzione chiamata addroute che accetta un modello di percorso e un nome di modello. Questa funzione dovrà quindi memorizzare tutti questi oggetti di percorso all'interno di una sorta di array.
  • Con l'URI corrente e una serie di percorsi memorizzati, avremo bisogno di un qualche tipo di metodo per vedere se corrispondono.
  • E per ultimo, ma non meno importante, avremo bisogno di prendere una rotta abbinata e mostrare il suo modello allegato.

Si spera che si possa notare che, stendendo i requisiti, è davvero utile semplificare la fase di pianificazione. Ora siamo pronti per entrare.


Impostare

Per iniziare, creiamo un nuovo progetto Meteor, chiamerò il mio 'routerdemo'. Ora dentro, creeremo una cartella chiamata 'lib'all'interno di un'altra cartella denominata'cliente':

meteor creare routerdemo cd routerdemo mkdir -p client / lib

Quindi, crea un file chiamato 'router.js'all'interno del nuovo creato lib cartella. Il motivo per cui lo stiamo attaccandocliente', è perché il server non ha accesso a window.location.pathname variabile e come tale, non funzionerà con il nostro router. Mettendo roba in una cartella chiamata 'cliente'assicura che verranno eseguiti solo dal lato client.

Ora dentro il router.js file appena creato, inseriamo alcune impalcature:

////////////////////////// // Router ///////////////////// ///// Router = uri: / * URL corrente * /, rotte: [], addRoute: / * funzione per aggiungere una rotta * /, getMatchingRoute: / * per ottenere la route corrispondente * /, eseguire: / * funzione per visualizzare il modello del percorso abbinato * /;

Penso che sia uno scaffold piuttosto buono, ho persino inserito il codice per l'array delle rotte e ho aggiunto alcuni commenti (progressi!). Ora per sviluppare ulteriormente la nostra biblioteca, dobbiamo discutere su come abbinare questi percorsi insieme.


Percorsi di corrispondenza

Questo non è semplice come currentRoute === route, come abbiamo a che fare con segnaposti dinamici. Vogliamo un percorso per/ID utente'per abbinare un URI di'/ User / 42' e così via.

Per fare ciò, dovremo dividere l'URI e fare un'analisi più approfondita. Ora alcune persone potrebbero pensare di usare una regex, ma è un po 'esagerato se me lo chiedi. Un approccio molto più semplice sarebbe quello di dividere i segmenti e assicurarsi che i due percorsi abbiano lo stesso numero di segmenti, e anche garantire che le parti del percorso che non sono segnaposto, corrispondano.

Questo può essere facilmente ottenuto dividendo l'URI dove mai c'è una barra ('/'), usando il .Diviso metodo. Quindi il nostro primo controllo assicurerebbe che le due rotte abbiano lo stesso numero di segmenti.

Se il percorso è '/ID utente'e otteniamo un URI di'/ Profile / 42 / foo / bar', non abbiamo nemmeno bisogno di fare ulteriori controlli, uno ha due segmenti e l'altro ne ha quattro, quindi sembra un buon controllo primario. La prossima cosa che possiamo fare è filtrare gli elenchi e accertarci che ogni pezzo che non è un segnaposto corrisponda. Se questi due controlli sono veri, sappiamo che la rotta corrisponde.

Impostazione della variabile URI

Quindi iniziamo con l'impostazione di uri variabile:

uri: _.compact (window.location.pathname.split ("/")),

Nel codice precedente, stiamo suddividendo l'array sulle barre e trasformando la stringa in una serie di segmenti. Quindi stiamo usando Underscore compatto funzione per rimuovere qualsiasi elemento vuoto dalla lista, questi potrebbero essere causati da una barra in avanti all'inizio o da qualcuno che usa due barre in avanti per errore. In questo modo, rende il nostro sistema molto più indulgente.

Aggiunta di rotte

Successivamente, abbiamo bisogno di creare la funzione per aggiungere un percorso, questo è un processo abbastanza simile, ma poiché stiamo andando ad abbinare i segnaposto in seguito, stiamo andando a memorizzare non solo i segmenti e il nome del modello, ma gli indici per anche i segnaposto.

Ecco la funzione completata:

addRoute: function (route, template) var segments = _.compact (route.split ("/")); var placeholders = _.reduce (segment, function (currentArr, piece, index) if (piece.substr (0, 1) === ":") currentArr.push (index); segment [index] = piece. substr (1); restituisce currentArr;, []); this.routes.push (route: segments, template: template, placeholderIndexes: placeholders); ,

Iniziamo dividendo la rotta in segmenti, proprio come abbiamo fatto per l'URI, ma questa volta abbiamo anche bisogno di memorizzare gli indici dei segnaposto per riferimento futuro, usando Underscore's ridurre metodo.

Per l'inconsapevole, il ridurre la funzione è simile a ogni metodo, scorre anche tutti gli elementi di una lista, la differenza è che passa qualunque cosa ogni iterazione ritorna all'elemento successivo, restituendo infine i risultati alla variabile data. Stiamo iniziando con un array vuoto (il terzo parametro) e stiamo aggiungendo ogni indice quando li troviamo e passando quell'array lungo fino a quando, infine, viene restituito al segnaposto variabile.

La prossima cosa che vedrai succedere qui è che stiamo rinominando i segmenti che sono segnaposto e rimuovendo i due punti. Lo facciamo puramente per ragioni estetiche e in seguito, renderà più facile fare riferimento nei modelli.

Infine, trasferiamo i nuovi dati nel nostro array di rotte, creato in precedenza.

Abbinare un percorso a un URI

Il prossimo passo è filtrare l'elenco e cercare una rotta che corrisponda all'URI corrente.

Ecco la funzione completa:

getMatchingRoute: function () for (var i in this.routes) var route = this.routes [i]; var data = ; if (route.segments.length === this.uri.length) var match = _.every (route.segments, function (seg, i) if (_.contains (route.placeholderIndexes, i)) data [seg] = this.uri [i]; return true; else return seg === this.uri [i];, this); if (match) return data: data, template: route.template // no matches (aggiungi 404 o template predefinito forse?) return false; ,

Stiamo facendo un bel po 'di cose qui, percorriamolo. Iniziamo spostando in rassegna la gamma di percorsi e assegniamo il percorso corrente a una variabile, insieme a un oggetto dati vuoto per memorizzare i segnaposto.

Successivamente, eseguiamo il controllo iniziale per accertarci che i due percorsi abbiano lo stesso numero di segmenti, altrimenti passeremo alla prossima rotta. Se hanno lo stesso numero di componenti, dobbiamo controllare se i segmenti corrispondono, questo può essere fatto usando Underscore's '_.ogni' funzione. Questa funzione è di nuovo come l '_.ogni'metodo, tranne che restituisce un valore booleano. Il modo in cui funziona è che eseguirà la funzione per ogni elemento dell'array, se tutti restituiscono true, la funzione restituirà true, altrimenti restituirà false, quindi è perfetto per fare cose come questa in cui dobbiamo verificare ogni segmento.

Ora il controllo che stiamo eseguendo è piuttosto semplice, se si tratta di un segnaposto, quindi si adatta automaticamente, in quanto un segnaposto può essere uguale a qualsiasi valore. Se non è un segnaposto, ci assicuriamo che i due segmenti corrispondano, piuttosto semplice.

Per verificare se questo è o meno un segnaposto, passiamo l'indice dei segmenti corrente (memorizzato in 'io') a Underscore's _.contains funzione, che controllerà il suo valore.

Ora ti starai chiedendo quale sia la prima riga all'interno di questo 'Se'l'istruzione sta facendo, bene, sta memorizzando il segmento nell'array di dati sotto il nome del segnaposto specificato. Quindi, ad esempio, hai un percorso di '/nome utente'e l'URI corrente è'/ User / bob', quindi questa riga aggiungerà una proprietà all'oggetto di dati chiamato'nome'e passargli un valore di peso.

Il resto è abbastanza ovvio, passiamo vero o falso, a seconda delle circostanze, e il risultato viene memorizzato in 'incontro'. Se la corrispondenza è vera, restituiamo i dati insieme al nome dei modelli e, se non ci sono corrispondenze, restituiamo false. E questo è tutto per il nostro getMatchingRoute metodo.

Finora, possiamo ottenere l'URI corrente, possiamo aggiungere rotte e possiamo trovare un percorso di abbinamento, l'unica cosa che rimane è mostrare il percorso corretto, e per questo dobbiamo scrivere il 'correre' metodo.


Visualizzazione del modello

Meteor utilizza i manubri per i modelli e memorizza tutti i modelli in una variabile, opportunamente denominata, 'Modello'. Ora, se hai familiarità con i manubri, sai che questi modelli sono solo funzioni, e chiamandole (eventualmente passando alcuni dati) recuperiamo il codice HTML del template.

Ora, chiamare queste funzioni per ottenere l'HTML del modello funzionerebbe correttamente, ma non è molto simile a Meteor, in quanto ciò che ci ritroveremmo è solo un normale sito web statico. Fortunatamente, aggiungere il comportamento dinamico è più facile di quanto si possa pensare, tutto ciò che dobbiamo fare è racchiudere la chiamata della funzione in un 'Meteor.render'chiama. Inserendolo in questa funzione, reagirà ai cambiamenti nei dati e lo manterrà "live".

Il metodo Run

Per questo motivo, l'esecuzione del router è molto semplice, creiamo il metodo di esecuzione:

run: function () var route = this.getMatchingRoute (); if (route) var fragment = Meteor.render (function () if (Template [route.template]! == undefined) return Template [route.template] (route.data);); document.body.appendChild (frammento);  else // 404

Iniziamo prendendo il percorso corrispondente, usando il getMatchingRoute funzione che abbiamo appena scritto, quindi assicuriamo che ci sia una corrispondenza, e infine usiamo un altro dichiarazione per gestire la visualizzazione di un 404.

All'interno dell'istruzione if, chiamiamo Meteor.render e all'interno, controlliamo e chiamiamo il modello restituito, passando con esso i dati dai segnaposto. Questa funzione restituirà un frammento HTML, che possiamo quindi aggiungere al corpo del documento.

Quindi con circa 60 linee di codice, abbiamo completato il nostro router.

Provalo

Il prossimo passo è testarlo. Userò lo stesso codice che abbiamo scritto in precedenza quando abbiamo pianificato questo progetto, in quanto sarà una buona misura della nostra realizzazione, di ciò che avremmo voluto realizzare. Aggiungiamo un file chiamato main.js dentro il cliente cartella e aggiungere quanto segue:

Meteor.startup (function () Router.addRoute ('/ home', 'homeTemplate'); Router.addRoute ('/ user /: username', 'profileTemplate'); Router.addRoute ('/ contact', 'contactTemplate '); Router.run (););

Nel codice precedente, dobbiamo prima assicurarci che i nostri modelli e il nostro corpo siano disponibili prima di provare a lavorare con il nostro router. Lo facciamo avvolgendo tutto il nostro codice all'interno del Meteor.startup chiamata al metodo. Ciò garantirà che tutto sia pronto e all'interno del avviare metodo, possiamo quindi aggiungere i nostri percorsi ed eseguire il router.

Creare i nostri modelli

Ora creiamo un paio di modelli, questo può essere fatto ovunque, è possibile creare una sottocartella all'interno del cliente cartella denominata modelli e creare un file HTML separato per ciascuno, ma poiché questi saranno modelli brevi e solo a scopo di esempio, li metto insieme in un file chiamato 'templates.html' dentro il 'cliente' cartella:

  

Il primo modello è piuttosto semplice, contiene solo un piccolo codice HTML per l'intestazione della home page. Il secondo modello è molto simile al primo modello, ma questa volta utilizziamo il nome utente parametro di percorso. Ora l'ultimo modello usa anche un segnaposto, ma il suo percorso non ha il twitterName segmento. Questo perché i segnaposto Meteor standard continueranno a funzionare e funzioneranno in modo reattivo.

Di nuovo dentro cliente cartella, creiamo ora un file chiamato 'templates.js', per dichiarare il segnaposto di contatto.

Template.contactTemplate.twitterName = function () Session.setDefault ('twitter_name', '@gabrielmanricks'); return Session.get ('twitter_name'); 

Potresti aver appena restituito una stringa, ma volevo dimostrare che tutto è ancora reattivo. L'ultimo passaggio consiste nell'eliminare i file html e js predefiniti dalla directory root (nel mio caso sono denominati routerdemo.html e routerdemo.js) Fatto ciò, avviare il server Meteor e navigare verso i percorsi specificati.

Prova ad andare a '/casa' o 'utente / gmanricks' o '/contatto'e dovrebbero lavorare tutti per te come previsto. Un'altra cosa è che, poiché abbiamo memorizzato il nome di Twitter in Session, possiamo semplicemente aprire la console del browser nella pagina dei contatti e inserire:

Session.set ('twitter_name', '@nettuts');

E vedrai che la pagina si aggiornerà in tempo reale!


Sommario

In questo articolo, abbiamo creato una libreria di base del router, pur continuando a dargli un tocco di Meteor. Abbiamo trattato molti concetti chiave e, come risulta, anche molti concetti di Underscore.

Alla fine, spero di aver ricevuto il messaggio che non c'è una vera "magia" in corso qui. Si tratta davvero di implementare ciò di cui hai bisogno, al contrario di ciò che puoi.

Grazie per la lettura, spero che ti sia piaciuto. Come sempre, se hai qualche domanda puoi lasciarli qui sotto o chiedermi sul IRC NetTuts o sul mio Twitter.

Nota: Se sei interessato a saperne di più su Meteor, ho appena pubblicato il mio nuovo libro, che descrive in dettaglio il processo di creazione di un'app dalla sua ideazione e pianificazione alla messa in sicurezza e all'implementazione. Puoi ritirare il libro sia in formato ebook che in softcover da Amazon.