Minulla on pitkä perinne blogini päivittämisestä aika ajoin uusille teknologia-alustoille. Viimeisen vuoden ajan alustana on ollut Angular-Fullstack, ja yhdessä Prerender.io:n kanssa se on toiminut hyvin. Sivusto indeksoituu siististi hakukoneisiin ja artikkelit ovat jaettavissa sosiaalisessa mediassa.

Angular-Fullstack on kuitenkin sikäli rajallinen ratkaisu, että se vaatii oman Node.js-palvelimen ylläpitämistä. Olen sillä kannalla, että jatkossa web-sivustot tullaan palvelemaan enenevissä määrin staattisina tiedostoina. Tällaisessa blogissa ei ole pohjimmiltaan mitään sellaista toiminnallisuutta, joka edellyttäisi oman palvelimen ylläpitämistä.

Angularilla rakennetun sovelluksen voikin periaatteessa tallentaa yksittäisiksi, staattisiksi HTML-sivuiksi ja palvella esimerkiksi Amazon S3:sta (ja tarvittaessa CloudFrontista) käsin. Ongelmana on, että Angular ei ymmärrä hyödyntää valmiiksi generoidun HTML-sivun sisältöä, vaan ylikirjoittaa sen käynnistyessään. Tämä tarkoittaa, että sivu "välähtää" lataamisen jälkeen, kun Angular kääntää template-sivupohjat uudelleen ja päivittää ne selaimen DOM-puuhun. Erityisen ikävää tässä on se, että mahdolliset mainokset ladataan kahteen kertaan, mikä aiheuttaa ongelmia kaupallisessa käytössä.

Facebookin julkaisema React.js ei kärsi vastaavasta ongelmasta, koska se toimii erilaisella periaatteella kuin Angular. React luo template-sivupohjasta aina virtuaalisen DOM-puun ja vertaa sitä staattisesta HTML:stä ladattuun DOM-puuhun. Jos näissä ei ole eroa, niin mitään ei päivitetä, jolloin myöskään mainokset eivät lataudu uudelleen.

Tämän ominaisuuden vuoksi päädyin siirtämään oman blogini hiljalleen React-alustan varaan. Päätin kuitenkin tehdä tämän askeleittain, jotten joutuisi toteuttamaan kaikkea tyhjästä uudelleen. Tässä siirtymässä osoittautui hyödylliseksi ngReact-niminen kirjasto, jonka avulla Angular-sovelluksessa voi käyttää React-näkymiä Angularin omien sivupohjien rinnalla.

Omassa tapauksessani vanhat sivupohjat oli alun perin määritelty angular-ui-router-reitteinä tähän tapaan:

angular.module('kfalcknetApp')
  .config(function ($stateProvider) {
    $stateProvider
      .state('main', {
        url: '/',
        templateUrl: 'app/main/main.html',
        controller: 'MainCtrl'
      });
  });

React-versio samasta reitistä näyttää tältä (templateUrl on vaihtunut templateksi):

angular.module('kfalcknetApp')
  .config(function ($stateProvider) {
    $stateProvider
      .state('main', {
        url: '/',
        template: '<react-component name="MainComponent" props="reactProps"/>',
        controller: 'MainCtrl'
      });
  });

Sivupohjatiedosto, joka oli aiemmin nimeltään main.html, muuttui puolestaan main.jsx-tiedostoksi. Tavallisen JSX-pohjan ympärille tarvittiin tällainen "kuorrute":

var MainComponent;
angular.module('kfalcknetApp')
.run(function ($document, Article) {
  MainComponent = React.createClass({
    /* JSX-sivupohja tulee tähän */
  });
  angular.module('kfalcknetApp').value('MainComponent', MainComponent);
});

Ideana on, että React-komponentti luodaan angular.run() -funktion sisällä, jolloin voidaan hyödyntää Angularin Dependency Injection -ominaisuutta ja napata mukaan kaikki tarvittavat palvelut ja resurssit. Siirtymävaiheen aikana voidaan siis käyttää esimerkiksi $stateParams-objektia ja hakea tarvittava data palvelimelta $resource-objekteilla.

Sivuhuomiona todettakoon, että myös Angularin kontrollereista voi halutessaan välittää Property-muuttujia React-komponenteille. ngReact tukee tätä asettamalla ne kontrollerissa muuttujaan $scope.reactProps (tai mikä muuttuja sitten onkaan määritelty yllä mainitussa <react-component>-tägissä). Itse halusin kuitenkin eliminoida niiden tarpeen ja tehdä tarvittavat alustukset React-komponenttien omissa componentWillMount()-metodeissa. Tällöin React-komponentit toimivat itsenäisesti ja ne voidaan myöhemmin irrottaa kokonaan Angularista.

Nyt olen siis puolivälissä projektiani siirtää blogini kokonaan React-pohjaiseksi. Seuraavaksi aion kokeilla, miten Angularin ui-router-reitittimen korvaaminen onnistuu React Routerilla, ja pysyykö staattiseksi HTML:ksi renderöity sisältö silloin koskemattomana Angularin ja Reactin käynnistyessä. Jos tämä onnistuu, niin voin tehdä koko blogistani staattisen kopion ja palvella sen jatkossa suoraan Amazonin S3:sta.

Published 4.7.2015