7. diel - Nasadenie na server
V minulom diele som ukazoval výrobu user interface pre hru. V tomto poslednom diele ukážem nasadzovaní celého projektu na server.
Nasadzovanie na server
Prvú polovicu tutoriálu som vyrábal statické JavaScriptové súbory. Tie sa dajú ľahko nakopírovať na server napr. Pomocou FTP. Nevýhodou tohto postupu je, že po každej zmene musím aktualizované súbory prekopírovať ručne. V priebehu kopírovania nastáva doba, kedy sú na serveri zmiešané súbory zo starej a novej verzie. A ak kopírovanie zlyhá, môže sa produkčná verzia rozbiť úplne.
Potom som ukázal, aké sú nevýhody čistého JavaScriptu a prešiel som na TypeScript. Teraz už nemôžem súbory prekopírovať priamo. Môžem si ale nechať vytvoriť transpilovaný javascriptový súbor a ďalej postupovať úplne rovnako:
npm build
To, čo sa mi vytvorí do zložky build, nakopírujte na server.
Tento postup je OK, ak budem aktualizovať aplikáciu niekoľkokrát ročne, navštevuje ju málo ľudí a nie je potrebná príliš veľká spoľahlivosť. Ak by som chcel aktualizovať častejšie napr. Každý deň či týždeň, bolo by dobré celý postup automatizovať.
Continuous integration
Mnoho hostingov má podporu continuous integration. To je proces, ktorý prevedie zdrojové kódy aplikácie na stav, keď je nová verzia nasadená pre užívateľov. Pokiaľ máš vlastný (alebo virtuálny) server môžeš si nainštalovať niektorý z continuous integration nástrojov. Jedným z nich je napr. Strider.
Impulzom pre nasadenie novej verzie môže byť tlačidlo v administrácie, príkaz v konzole alebo nový commit v git repozitári (obvykle vo vetve production). Potom sa spustí niekoľko krokov:
- Vytvorí sa nejaké oddelené miesto (napr. Nový priečinok) pre novú verziu aplikácie.
- Do tohto miesta sa naklonuje git repozitár na commit, ktorý má byť nasadený.
- Spustí sa NPM install. To nainštaluje všetky závyslosti, ktoré máme nastavené v package.json.
- Spustí sa NPM build. To vytvorí zo zdrojových súborov a nainštalovaných závyslostí jeden produkčné javascriptový súbor.
- Spustí sa testy. (Popíšem ďalej v článku.)
- Spustí sa samotná aplikácia. Tu si líšia jednoduché hostingy (napr. GitHub Pages), ktoré jednoducho cez https odosielajú vytvorené produkčné súbory a ozajstné node.js hostingy (napr. Herok), ktoré spustia prednastavený node.js skript.
- Stará verzia sa prepne sa za novú.
My použijeme vyššie zmienené GitHub Pages. Najskôr nainštalujeme nástroj, ktorý vie s GitHub Pages pracovať:
npm install gh-pages --dev
Do package.json pridáme url hry na GitHub pages pod hodnotu homepage (tá je vo formáte https: // [užívateľ].github.io / [projekt] /). A do scripts pridáme predeploy a deploy.
{ "name": "web-game", "homepage": "https://webappgames.github.io/web-game/", "dependencies": { //... }, "devDependencies": { "gh-pages": "^1.0.0", //... }, "scripts": { //... "predeploy": "npm run build", "deploy": "gh-pages -d build" } }
Teraz stačí v administrácii GitHub nastaviť GitHub Pages na vetvu "gh-pages".
Testy
Pokiaľ commitnu niečo chybného, proces continuous integration zachová pôvodnú verziu a ma len upozorní. Tým sa vyhnem nasadenie chybné verzia pre používateľa či znefunkčnenie celej webovej stránky. Existuje niekoľko najbežnejších druhov chýb:
- Chybu v syntaxi (napr. Chýbajúce zátvorku) odhalí TypeScript transpilátor.
- Chybu v typovanie odhalí TypeScript transpilátor. Ak by sme nepoužívali TypeScript, tento druh chýb by sa prejavil až za behu, čo by mohlo mať oveľa horšie následky.
- Chyba za behu (napr. Komponenta vyhodí chybu pri rendrovanie).
- Logická chyba (napr. Akcie zmení stav aplikácie tak, ako sme neočakávali).
Chyby v syntaxi a typovanie sú otravné, odhalí sa ale ešte pred nasadením programu na produkciu. Logické chyby a chyby za behu sa odhaľujú oveľa horšie. Mať chybu v bežiaci aplikácii je nepríjemné a často veľmi drahé. Preto sa píšu automatizované testy, ktoré odhaľujú chyby ešte pred nasadením aplikácie pre užívateľov. Ako správne písať testy je veľmi zložitá disciplína. Ja pre hru napíšem niekoľko jednoduchých testov:
Components.test.ts
import * as React from "react"; import * as ReactDOM from "react-dom"; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import * as injectTapEventPlugin from 'react-tap-event-plugin'; import {createStore} from 'redux'; import { Provider } from 'react-redux'; import {stateReducer} from '../redux-reducers/index'; import Root from "../react-components/root"; import Heading from "../react-components/heading"; import BlockColor from "../react-components/block-color"; injectTapEventPlugin(); const store = createStore(stateReducer, {}); it('Root renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render( <Provider store={store}> <MuiThemeProvider> <Root/> </MuiThemeProvider> </Provider> , div); }); it('Heading renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render( <Provider store={store}> <MuiThemeProvider> <Heading/> </MuiThemeProvider> </Provider> , div); }); it('BlockColor renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render( <Provider store={store}> <MuiThemeProvider> <BlockColor/> </MuiThemeProvider> </Provider> , div); });
State.test.ts
import {createStore} from 'redux'; import {stateReducer} from '../redux-reducers/index'; const store = createStore(stateReducer, {}); describe('blocks', () => { it('should increase number of blocks after dispatch BLOCK_ADD', () => { const oldState = store.getState(); store.dispatch({type:'BLOCK_ADD',newBlock:{}}); const newState = store.getState(); expect(newState.blocks.length).toBe(oldState.blocks.length+1); }); it('should descrease number of blocks after dispatch BLOCK_DELETE', () => { const oldState = store.getState(); store.dispatch({type:'BLOCK_DELETE',blockId:oldState.blocks[0].id}); const newState = store.getState(); expect(newState.blocks.length).toBe(oldState.blocks.length-1); }); }); describe('ui', () => { it('should toggle drawer after dispatch UI_DRAWER_TOGGLE', () => { const oldState = store.getState(); store.dispatch({type:'UI_DRAWER_TOGGLE'}); const newState = store.getState(); expect(newState.ui.drawer).toBe(!oldState.ui.drawer); }); it('should change color after dispatch UI_COLOR_SET', () => { store.dispatch({type:'UI_COLOR_SET',value:'#123456'}); const newState = store.getState(); expect(newState.ui.color).toBe('#123456'); }); });
Dokončenú hru si môžeš stiahnuť pod článkom, alebo ísť do Git repozitára, kde nájdeš najnovšiu verziu zdrojových kódov. Alebo si ju rovno môžeš vyskúšať na webappgames.github.io/web-game.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 79x (9.89 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript