Dit is het verhaal over een project van mij. Een grote. Een mengsel tussen PHP en Node.js. Het is een applicatie met één pagina vanuit één oogpunt en een voor SEO geoptimaliseerde website van een andere. Tonnen JavaScript, CSS en HTML zijn geschreven. In één woord een spaghetti-nachtmerrie voor elke ontwikkelaar. Er waren vallen en opstaan. Bugs produceren en oplossen. Vechten met de nieuwste technologieën en eindigen met een wonderbaarlijk eenvoudige bibliotheek, wat het onderwerp van dit artikel is.
Zoals het normaal gebeurt, werd het project als niet zo groot beschouwd. De brief kwam binnen, we bespraken hoe de ontwikkeling zou worden aangepakt, welke technologieën zouden worden gebruikt en hoe we ze zullen gebruiken. We hebben een plan gemaakt en zijn aan het werk gegaan. In het begin hadden we een paar pagina's die werden beheerd door een CMS. In het begin was er niet zo veel JavaScript-code, omdat ons systeem de meeste inhoud opleverde.
Hier is een ruwe structuur van het project:
We zetten onze client-side code in verschillende mappen. De server-side code was op dit moment alleen PHP, dus het ging naar de php
directory. We verpakken alles in ongeveer 30 bestanden en alles was in orde.
In de periode van enkele maanden probeerden we verschillende concepten en veranderden we de projectcode verschillende keren. Vanuit het huidige gezichtspunt kon ik vier grote problemen ontdekken die we hebben ontmoet.
Het lijkt erop dat de klant tevreden was met het resultaat en besloot meer te investeren in zijn uiterlijk op internet. Er werd ons gevraagd om een paar nieuwe functies te bouwen. Sommigen waren gewoon nieuwe inhoudsplaatsen, andere waren toevoegingen aan reeds bestaande pagina's. We zijn begonnen met het toevoegen van meer en meer bestanden in alle bovenstaande mappen. Het begon een beetje rommelig te worden, dus we besloten om submappen voor de verschillende pagina's te maken en de benodigde code daar op te slaan.
Bijvoorbeeld de CSS-stijlen voor de wat betreft pagina was binnen css / over / styles.css
. JavaScript in js / over / scripts.js
enzovoorts. We gebruikten een PHP-script dat de bestanden samenvoegt. Er waren natuurlijk delen van de site die op verschillende pagina's stonden en we plaatsten ze erin gemeenschappelijk
directories. Dit was een tijdje goed, maar het werkte niet lang goed, want toen de mappen vol raakten, was het traag om iets aan te passen. Je moest drie verschillende mappen zoeken om te vinden wat je nodig had. De site was nog voornamelijk geschreven in PHP.
Op dat moment werden mobiele applicaties populair. De klant wilde zijn site beschikbaar hebben voor mobiele apparaten en dit is het grote keerpunt in het project. We moesten de site converteren naar een enkele pagina-applicatie. En dat niet alleen, het moest ook vele real-time functies bevatten. Natuurlijk moest niet alle inhoud van de site dynamisch worden geladen. De SEO was nog steeds een belangrijk onderdeel van de visie van de klant. We kozen de MEAN-stack voor de komende delen. Het probleem was met de oude pagina's. Hun inhoud moest door PHP worden bediend, maar de logica van de pagina's veranderde en deze was volledig gemaakt met JavaScript. Gedurende enkele weken voelden we ons als de passagiers van Titanic. We hadden haast om iets vrij te geven, maar er was gat na gat en al snel was ons schip vol met water (beestjes).
We gebruikten GruntJS een tijdje, maar migreerden naar Gulp. Het heeft veel geholpen omdat we onze ontwikkelingssnelheid hebben verhoogd. Het was echter nog steeds te vervelend om bestaande componenten toe te voegen of te bewerken. De solide architectuur die we in het begin hadden was getransformeerd in een complexe mix van bestanden. Ja, er waren strikte conventies voor het benoemen en plaatsen van deze bestanden, maar het was nog steeds te rommelig. We sloegen toen onze hoofden samen en kwamen met het volgende formaat:
We hebben de site opgedeeld in verschillende componenten, die op zwarte dozen lijken. Ze leven in hun eigen map. Alles met betrekking tot de component is in de map opgeslagen. We hebben zorgvuldig de API's van de klassen ontworpen. Ze waren toetsbaar en communicatief. We ontdekten dat een dergelijke structuur beter werkte omdat we tonnen onafhankelijke modules hadden. Ja, we zijn de JavaScript-bestanden aan het mixen met CSS-stijlen en HTML-sjablonen, maar het was gewoon makkelijker om op unit-basis te werken, in plaats van diep in verschillende mappen te graven.
Die pagina's die oud waren en die we via PHP moesten leveren, waren ook vol JavaScript-logica. In sommige gevallen werkte Angular echter niet erg goed. We moesten kleine hacks maken om de dingen soepel te laten verlopen. We eindigden met een mix tussen hoekige controllers en aangepaste code. Het goede nieuws was dat het budget van het project werd uitgebreid en we besloten om ons eigen raamwerk te gebruiken. In die tijd was ik mijn eigen CSS-preprocessor aan het ontwikkelen. Het project gaat echt, heel snel. Al snel porteerde ik mijn bibliotheek voor gebruik aan de clientzijde. Regel voor regel werd het omgevormd tot een klein kader, waarmee we begonnen te integreren in het project.
Dit is waarschijnlijk wat je vraagt. Welnu, er zijn een dozijn andere die een breed scala aan mogelijkheden bieden. Ja, dat is waar, maar ... we hadden geen breed scala aan mogelijkheden nodig. We hadden specifieke dingen nodig en niets meer. We waren er klaar voor om het feit te accepteren dat we met een populair raamwerk een paar kilobytes aan de algehele paginalading kunnen toevoegen. Dat was geen groot probleem.
De status van onze codebasis was het probleem. We waren gericht op het bouwen van goede architectuur en we zijn het er allemaal over eens dat de oplossing op maat soms beter past. Het gebruik van Angular, Ember, Knockout of Backbone komt met zijn voordelen, maar de waarheid is dat er geen universeel kader is.
Ik hou van de woorden van Jeremy Keith, in zijn toespraak De kracht van eenvoud zei hij dat het belangrijkste bij het kiezen van je gereedschap de filosofie is van de persoon die de tool heeft gemaakt en of die filosofie aansluit bij die van jou. Als de ideeën van het raamwerk niet op één lijn liggen met die van jou, ga je er snel tegenin. Hetzelfde gebeurde met ons. We hebben geprobeerd om Angular te gebruiken en er waren te veel problemen. Problemen die we konden oplossen, maar we gebruikten hacks en complexe oplossingen.
We hebben ook geprobeerd, maar het werkte niet, omdat het sterk gebaseerd is op de routeringsmechanismen. Backbone was een goede keuze en het kwam het dichtst in de buurt van onze visie. Toen ik AbsurdJS introduceerde, hebben we besloten om het te gebruiken.
AbsurdJS is oorspronkelijk gestart als een CSS-preprocessor, uitgebreid naar een HTML-preprocessor en het is met succes geport voor gebruik door de client. Dus in het begin gebruiken we het voor het compileren van JavaScript naar HTML of CSS. Ja, je hebt me goed gehoord; we zijn begonnen met het schrijven van onze stijlen en markeringen in JavaScript (klinkt misschien vreemd, maar blijf lezen). Ik duwde de bibliotheek naar voren en een tiental functionaliteiten werden toegevoegd.
Als je een complex systeem hebt, met veel pagina's, wil je echt geen grote problemen oplossen. Het is veel beter om alles in kleinere taken op te splitsen en ze één voor één op te lossen. We deden hetzelfde. We hebben besloten dat onze applicatie uit kleinere componenten zal bestaan, zoals:
var absurd = Absurd (); var MyComp = absurd.component ('MyComp', constructor: function () // ...); var instantie = MyComp ();
absurd.component
definieert een klasse. Oproep aan de Mycomp ()
methode maakt een nieuw exemplaar.
Met al deze kleine componenten hadden we een kanaal nodig voor communicatie. Het waarnemerspatroon was perfect voor deze zaak. Dus, elke component is een evenementendispatcher.
var MyComp = absurd.component ('MyComp', doSomething: function () this.dispatch ('something-happen');); var instantie = MyComp (); instance.on ('something-happen', function () console.log ('Hello!');); instance.doSomething ();
We zijn ook in staat om samen met het bericht gegevens door te geven. De definitie van de componenten en hun "luister-dispatch" -karakter is vrij triviaal. Ik heb dit concept overgenomen van de andere populaire kaders, omdat het er natuurlijk uitziet. Het was ook veel gemakkelijker voor mijn collega's om AbsurdJS te gaan gebruiken.
Samen met de op PHP gebaseerde markup hadden we dynamisch DOM-elementen gemaakt. Dit betekent dat we toegang nodig hadden tot de bestaande DOM-elementen of nieuwe, die later aan de pagina worden toegevoegd. Laten we zeggen dat we de volgende HTML hebben:
Pagina titel
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Hier is een component die de kop ophaalt:
absurd.component ('MyComp', html: '.content h1', constructor: function () this.populate (); console.log (this.el.innerHTML); // Page title) ();
De bevolken
methode is de enige magie methode in de hele bibliotheek. Het doet verschillende dingen zoals het compileren van CSS of HTML, het bindt gebeurtenissen en dergelijke dingen. In het bovenstaande voorbeeld ziet het dat er een is html
eigendom en initialiseert de el
variabele die naar het DOM-element wijst. Dit werkt best goed voor ons, want toen we eenmaal die referentie kregen, konden we met de elementen en de kinderen werken. Voor die componenten waarvoor dynamisch gecreëerde elementen nodig waren, is de html
property accepteert een object.
absurd.component ('MyComp', html: 'div.content': h1: 'Paginatitel', p: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', constructor: function () this .populate (); document.querySelector ('body'). appendChild (this.el);) ();
De JSON hierboven is vertaald naar dezelfde HTML-markup. Ik heb voor JSON gekozen omdat deze vanuit een JavaScript-oogpunt veel flexibeler is. We kunnen objecten samenvoegen, delen vervangen of verwijderen. In de meeste van de populaire kaders zijn de sjablonen gewoon tekst die het manipuleren bemoeilijkt. AbsurdJS heeft ook een eigen sjablonengine.
absurd.component ('MyComp', html: 'div.content': h1: '<% this.title %>', ul: ['<% for(var i=0; i', li: '<% this.availableFor[i] %>','<% %>'], titel:' That \ 's awesome', availableFor: ['alle browsers', 'Node.js'], constructor: function () this.populate (); document.querySelector ( 'body') appendChild (this.el).; ) ();
Het resultaat is:
Dat is geweldig
- alle browsers
- Node.js
De deze
sleutelwoord in de bovenstaande uitdrukkingen, verwijst naar het onderdeel zelf. De code tussen <%
en %>
is geldig JavaScript. Functies zoals berekende eigenschappen kunnen dus gemakkelijk rechtstreeks in de definitie van de sjabloon worden ontwikkeld. Natuurlijk kunnen we dezelfde sjabloonmotor gebruiken met reeds bestaande markeringen. Bijvoorbeeld:
<% this.title %>
<% for(var i=0; i<this.availableFor.length; i++) %>
- <% this.availableFor[i] %>
<% %>
... kan worden beheerd met de volgende component (het resultaat is hetzelfde):
absurd.component ('MyComp', html: '.content', titel: 'That \' s awesome ', availableFor: [' all browsers ',' Node.js '], constructor: function () this.populate ();) ();
Hoe dan ook, het gaat erom dat we sjablonen konden definiëren of helemaal opnieuw konden creëren. We zijn ook in staat om de gegevens die op een eenvoudige en natuurlijke manier worden ingespoten te controleren. Alles is gewoon eigenschappen van het goede oude JavaScript-object.
We hebben het hele systeem met succes gesplitst in kleine modules. De onderdelen die vóór Angular controllers waren, werden AbsurdJS-componenten. We realiseerden ons dat hun HTML nauw was gekoppeld aan hun definitie, die het beheer van de markup in de applicatie volledig veranderde. We stopten met nadenken over de aaneenschakeling, conventies of iets dergelijks. We hoefden helemaal geen HTML-bestanden te maken. Als ik terugkijk, kon ik dit exacte moment in onze commit-geschiedenis zien. Het is gemakkelijk zichtbaar omdat veel bestanden uit de codebasis zijn verwijderd.
Toen dacht ik, wat er zal gebeuren als we hetzelfde doen met de CSS. Het was natuurlijk mogelijk omdat AbsurdJS een CSS-preprocessor was en CSS kon produceren. We hebben zojuist de gecompileerde string ontvangen, een nieuwe maken stijl
tag in de hoofd
van de huidige pagina en injecteer het daar.
absurd.component ('MyComp', css: '.content': h1: color: '# 99FF00', opvulling: 0, marge: 0, p: fontSize: '20px', html : '.content', constructor: function () this.populate ();) ();
Hier is de stijl
tag die wordt geproduceerd:
En dag na dag hebben we de CSS-stijlen van de SASS-bestanden overgedragen (omdat we op een gegeven moment SASS als een CSS-preprocessor hebben gekozen) voor de AbsurdJS-componenten. Eerlijk gezegd was het vrij eenvoudig omdat alle mixins en variabelen die we hebben, werden gedefinieerd als JavaScript-functies en variabelen. Het delen van de stijlen was nog eenvoudiger omdat alles JavasSript was.
... wanneer alles perfect werkt, maar je voelt dat er iets mis is
We keken naar de code. Het werkte. AbsurdJS reed zelfs de oude delen. De nieuwe dingen gebruiken dezelfde bibliotheek. De HTML en de CSS waren mooi gescheiden en werden direct in de definitie van de componenten geplaatst. Ik voelde echter dat er iets mis was. Ik stopte een tijdje en vroeg mezelf: "Waar wordt het web van gemaakt?".
En wat we deden, is een beetje anders. Het lijkt meer op de onderstaande afbeelding.
Ik bouw websites al meer dan tien jaar en ik herinner me de tijd dat we allemaal vochten voor de grote scheiding van deze drie bouwmaterialen. En wat ik in dit project heb gedaan, is precies het tegenovergestelde. Er waren (bijna) geen CSS- en HTML-bestanden. Alles was JavaScript.
Veel mensen zullen zeggen dat dit belachelijk is en dat we het geld van de klant terug moeten geven. Ja, dit zou waar kunnen zijn, maar dit concept werkte perfect in ons geval. We hebben geen aanvraag geschreven. In feite hebben we een aantal onafhankelijke componenten geschreven. Ik geloof dat het web een combinatie van kant-en-klare componenten zal zijn.
Wij, als ontwikkelaars, zullen dergelijke componenten moeten ontwikkelen en waarschijnlijk verbinden met en gebruik maken van dergelijke componenten die door anderen zijn geschreven. Projecten zoals AbsurdJS of Polymer laten zien dat dit mogelijk is en ik moedig je aan om in deze richting te experimenteren.
Dus uiteindelijk verliepen de zaken van de klant goed. Het was zo goed dat hij besloot om een nieuwe dienst te lanceren. En raad eens. Hij wilde dat sommige delen van de bestaande applicatie naar het nieuwe project werden overgebracht. Ik kan je niet vertellen hoe blij we waren om componenten van de ene plaats naar de andere te verplaatsen. We hoefden niets in te stellen, HTML-markup of CSS-bestanden kopiëren. We hebben zojuist het JavaScript-bestand van de component ontvangen, deze ergens geplaatst en er een exemplaar van gemaakt. Het werkte gewoon omdat er geen afhankelijkheden waren. Het zou me niet verbazen als een aantal van deze componenten binnenkort te koop worden aangeboden. Ze zijn vrij licht en bieden een mooie functionaliteit in verband met het product van de klant.
Ja, we hebben regels overtreden. Regels waar ik het persoonlijk mee eens ben. Regels die ik vele jaren heb gevolgd. De realiteit is echter dat we allemaal kwaliteit willen en soms is die kwaliteit bereikbaar door de regels te overtreden. We willen een goede, goed gestructureerde code produceren die gemakkelijk te onderhouden, flexibel en uitbreidbaar is. We willen niet achterom kijken en zeggen: "Oh mijn god ... was dat geschreven door mij !?". Als ik nu terugkijk, weet ik waarom de code er zo uitziet. Het lijkt erop dat het specifiek voor dat project is geschreven.
Als je deze tutorial interessant vond, kijk dan eens naar de officiële pagina van AbsurdJS. Er zijn handleidingen, documentatie en artikelen. U kunt de bibliotheek zelfs online proberen. Zoals elke andere tool, is AbsurdJS ontworpen voor specifiek gebruik. Het past goed bij ons project en kan geschikt zijn voor het jouwe. Ik noem het zelfs geen raamwerk, omdat ik deze definitie niet leuk vind. Het is eerder een toolbox dan een full-stack framework. Je kunt er gerust mee experimenteren, trekverzoeken indienen of problemen melden. Het is volledig open source en beschikbaar op GitHub.