Jak na to: Modernizovaný AngularJS 1.5+ s ES6, Webpack, Mocha, SASS a komponenty

Existuje mnoho důvodů, proč byste mohli chtít pracovat s AngularJS 1.x, takže budu předpokládat, že máte své důvody.

Angular! == AngularJS „Tento web a veškerý jeho obsah odkazují na AngularJS (verze 1.x), pokud hledáte nejnovější Angular, navštivte prosím angular.io“ - angularjs.org

Pro nové projekty bych doporučil použít React, protože právě zde se vyvíjí dynamika frontendu.

Udělal jsem reprosoustavu GitHub, kterou můžete vidličkou / klonem zahájit svůj vlastní projekt:

jsdoc_output // kde jsou generovány dokumenty
node_modules // kam směřují vaše prodejce
.gignignore
mocha-webpack.opts // určete jinou konfiguraci webového balíčku pro testování
package.json
README.md
webpack.config.base.js
webpack.config.js // rozšiřuje základní konfiguraci
webpack.config.test.js // rozšiřuje základní konfiguraci
veřejnost
| index-bundle.js // balíček vytvořený webpackem
| index.html
| index.js // webpack
|
\ --- superÚžasný komponent
        componentStylez.sass
        componentTemplate.html
        fancyJsModule.js
        theComponent.js
        theComponent.spec.js
        theComponentController.js

Generováno pomocí stromu / a / f na oknech

Podívejme se na index.html


  
  
  
  
  

    Proměnná na řadiči nad komponentami: {{IndexCtrl.fancyValue}}   

  
Dosud nebyla provedena žádná akce

Zde vidíte, že naše dvě tlačítka jsou dva super-úžasné komponenty. Jedná se o úhlové komponenty 1.5.

Úhlové komponenty 1,5

Úhlové komponenty 1.5 jsou pouze direktivy s lepšími výchozími hodnotami. Vždy jsou prvky, existuje výchozí „Controller as $ ctrl“ a mají izolovaný rozsah. Většinu toho, co jsem se naučil ze součástí, jsem se dozvěděl zde https://toddmotto.com/exploring-the-angular-1-5-component-method/

Komponenty mají dvě vazby, některé vstupy a některé výstupy.

Tyto komponenty jsou užitečné, protože nám umožňují zapouzdřit kombinaci funkcí zobrazení a řadiče. Pojďme se podívat na soubor komponenty

importujte šablonu z './componentTemplate.html'
importovat komponentuStylez z './componentStylez.sass'
importovat {ComponentController} z './theComponentController.js'
const bindings = {
  someInput: '<',
  someOutput: '&'
}
export const theComponent = {
  řadič: ComponentController,
  šablona,
  vázání
}

Všimněte si, jak lze každý prvek ovladače znovu použít. Řadič může být specifický pro tuto součást nebo to může být řadič, který se používá jinde.

Tento soubor dále obsahuje odkazy na vše, co potřebujete vědět o tom, jak součást. Komponenta je zcela samostatná, nemusíte se dělat starosti s tím, jak je používána ve větší aplikaci, aby byla vyrobena.

Řadič využívá běžné funkce ES6 a já se nebudu zabývat tím, jak to funguje, ale vezměte v úvahu pouze použitou strukturu třídy a nedostatek rozsahu $. Výsledkem je framework-agnostický řadič, mínus využití události životního cyklu komponenty ($ onInit)

importovat fancyFunction z './fancyJsModule.js'
/ **
 * Poskytuje obslužné prvky pro komponentu
 * /
třída ComponentController {
  / **
   * Oznamuje, že vstupní vazby nejsou definovány
   * @return {undefined} undefined
   * /
  konstruktor () {
    console.log ('vstupní vazby nejsou definovány!', this.someInput)
  }
  / **
   * Volá someOutput s hodnotou someInput vloženou do fancyFunction
   * @return {undefined} undefined
   * /
  doSuperThings () {
    console.log ('dělá super věci')
    this.someOutput ({value: fancyFunction (this.someInput, 3)})
  }
  / **
   * Oznamuje, že jsou definovány vstupní vazby
   * @return {undefined} undefined
   * /
  $ onInit () {
    console.log ('jsou definovány vstupní vazby!', this.someInput)
  }
}
exportovat {ComponentController}

Standardní formátování JS

Zjevným rozdílem je nedostatek středníků. Osobně se domnívám, že to poskytuje čistěji vypadající kód, a problémy spojené s používáním středníku jsou elegantně vyřešeny pomocí linter / formatteru StandardJS, který vám v tom zabrání v podivných problémech.

Webpack (což může být matoucí)

Všimněte si, jak musíme importovat pouze index.bundle.js do index.html. Je to proto, že používáme Webpack, který spojuje všechny naše prostředky do jednoho souboru. To zahrnuje naše šablony, javascript, css a vše, co si dokážete představit.

Webpack je ošuntělý zvíře a je to zvíře. Je to dost komplikované, že to lidé dali na své životopisy. Přesouvá spoustu složitosti z různých částí aplikace do souboru webpack.config.js.

Důkaz této složitosti lze nalézt ve skutečnosti, že máme příčinu pro 3 soubory webpack.config * .js. Jeden poskytuje základnu, druhý je pro přizpůsobení našeho testovacího nastavení a třetí je pro rozdělení kódu na kousky dodavatele (což nechceme dělat v našem testovacím nastavení, aby se podivné interakce s CommonsChunkPlugin).

var cesta = vyžadovat ('cesta')
var webpack = vyžadovat ('webpack')
module.exports = {
  položka: {
    'index': path.join (__ dirname, '/public/index.js')
  },
  výstup: {
    název souboru: '[jméno] -bundle.js',
    path: path.join (__ dirname, '/ public /'),
    devtoolLineToLine: true,
    pathinfo: true,
    sourceMapFilename: '[jméno] .js.map',
    publicPath: path.join (__ dirname, '/ src / main / webapp /')
  },
  modul: {
    nakladače: [
      {test: /\.js$/, loader: 'babel-loader', vyloučit: / node_modules /},
      {test: /\.css$/, loader: 'style-loader! css-loader'},
      {test: /\.sass$/, zavaděče: ['style-loader', 'css-loader', 'sass-loader']},
      {test: /\.html$/, loader: 'raw-loader'},
      // vložené základní adresy URL64 pro <= 8 000 obrázků, přímé adresy URL pro ostatní
      {test: /\.(png|jpg)$/, loader: 'url-loader? limit = 8192'},
      // pomáhá načíst bootstrap css.
      {test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'url? limit = 10000 & minetype = application / font-woff'},
      {test: /\.woff2$/,
        loader: 'url? limit = 10000 & minetype = application / font-woff'},
      {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'url? limit = 10000 & minetype = application / octet-stream'},
      {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'file'},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'url? limit = 10000 & minetype = image / svg + xml'}
    ]
  },
  pluginy: [
    nový webpack.HotModuleReplacementPlugin ()
  ],
  devServer: {
    publicPath: '/',
    contentBase: path.join (__ dirname, '/ public'),
    komprimovat: true
  },
  devtool: 'eval'
}

Nebudu zde vysvětlovat všechno, protože to je to, pro co jsou dokumenty webpacku (tento odkaz je pro Webpack 1, i když používáme Webpack 2. Dokumenty Webpack 2 jsou důkladné pouze v jejich neúplnosti, ale vidí migrace dokumentace).

Chcete-li poskytnout přehled, musíte určit:

  • Kde začíná vaše aplikace
  • Kam svazek jde
  • Jak se chystáte magicky importovat věci?
  • Jaké doplňky používáte?
  • Nastavení vašeho webpack-dev-serveru
  • Jak jsou nastaveny vaše zdrojové mapy.

Co? Pluginy? Zdrojové mapy? Proč potřebuji jiný server?

Pluginy

Zde používáme pouze plugin HotModuleReplacement (HMR). Umožňuje našemu prohlížeči automaticky se znovu načíst při změně souboru. To magicky odstraní jeden krok normální iterace zápisu, uložení, testu.

Existují spousty dalších pluginů (jeden, který jsem se neobjevil, abych to vyzkoušel vyniká: https://github.com/owen-it/ng-loader)

Zde je seznam oblíbených pluginů pro webpack: https://github.com/webpack-contrib/awesome-webpack (proč dělá Webpack tolik věcí?)

Zdrojové mapy

Zdrojové mapy jsou produktem ES6 a balíčků. Ještě jsem nepřišel na to, jak je vylepšit, přesto dochází k nešťastnému kompromisu mezi rychlostí a kvalitou, ke kterému dochází u sourcemaps, protože ty perfektní mohou být poměrně pomalé. Naše ES6 konverze je dosaženo pomocí babel loader.

Když se podíváme zpět na theComponent.js, obsahuje to většinu našeho webpacku

importujte šablonu z './componentTemplate.html'
importovat komponentuStylez z './componentStylez.sass'
importovat {ComponentController} z './theComponentController.js'
const bindings = {
  someInput: '<',
  someOutput: '&'
}
export const theComponent = {
  řadič: ComponentController,
  šablona,
  vázání
}

Všimněte si, jak importujeme html, SASS a ES6 zde. Toho je dosaženo prostřednictvím našich nakladačů. Použitý zavaděč je založen na názvu souboru.

Webpack-dev-server

Webpack-dev-server je úžasná věc, bez ohledu na to, zda máte skutečný backend. Podporuje HMR a je to statický souborový server, který urychluje váš vývoj. Kromě toho, použití webpack-dev-serveru vás donutí demontovat váš frontend a backend.

Být schopen provádět frontendový vývoj bez potřeby „skutečného“ serveru je úžasný z mnoha důvodů. Přinutí vás k vytváření praktických falešných dat, přesně vědí, jaké funkce patří k backendu vs. frontendu, dává vám HMR a dělá váš frontend hostitelským téměř na jakémkoli serveru, s jasnou smlouvou mezi frontendem a backendem.

V tomto nastavení je webpack-dev-server spolu se vším ostatním potřebným pro vývoj frontendu spuštěn jediným příkazem npm run dev, jak je uvedeno v package.json

{
  "name": "modern-angularjs-starter",
  "version": "0.0.1",
  "description": "Base project",
  "main": "index.js",
  "skripty": {
    "dev": "současně - kill-others \" webpack-dev-server --host 0.0.0.0 \ "\" npm spusťte dokumenty \ "",
    "docs_gen": "jsdoc -r -d jsdoc_output / public /",
    "docs_watch": "watch \" npm run docs_gen \ "public",
    "docs_serve": "echo Dokumenty se zobrazují na portu 8082! && live-server -q --port = 8082 - no-browser jsdoc_output /",
    "docs": "současně - kill-others \" npm run docs_serve \ "\" npm run docs_watch \ "",
    "postinstall": "bower install",
    "webpack": "webpack",
    "test": "mocha-webpack public / ** / *. spec.js"
  },
  "devDependencies": {/ * skryté pro vesmír * /}
  "závislosti": {/ * skryté pro vesmír * /}
}

Všimněte si použití současně (https://www.npmjs.com/package/concurrently)

To nám umožňuje spustit 2 blokovací příkazy paralelně.

Všimněte si, že existují také příkazy pro testování a dokumentaci. Příkazy dokumentace generují stránky JSDoc a poté jsou hostitelem na malém serveru, který automaticky aktualizuje (podobně jako HMR) prohlížeč, když dojde ke změně. Tímto způsobem můžete sledovat, jak se vaše dokumenty aktualizují při psaní, pokud je ukládáte často.

V tomto projektu to není prokázáno, nicméně specifikování typů v JSDoc je dobrý způsob, jak specifikovat datové smlouvy mezi frontendem / backendem. Případně můžete použít strojopis (k tomu jsou zavaděče).

Testování jednotek: (protože stojí za námahu)

Testování pomocí ES6 + AngularJS + Webpack je obtížné, aby se dařilo. Každá z těchto příčin způsobuje komplikace. Pro testování jednotek jsem se nakonec usadil na velmi malých jednotkách a testoval jsem své regulátory AngularJS jako funkce v uzlu. Karma je docela populární, ale podle mého názoru nejsou testy opravdu jednotkové testy. Bylo by však užitečné mít obojí.

Máme tedy mocha-webpack. To nám umožňuje použít import v našich testech, aniž bychom pro každý z nich určili vstupní bod.

Nejtěžší částí testování je vysmívat se importům ES6. Existuje několik různých způsobů, jak toho dosáhnout, ale jediný, který nevyžadoval úpravu testovaného souboru, byl injektér.

To je zvláště užitečné pro psaní testů, kdy je před provedením testu někdy nutné provést zesměšňování věcí uvnitř vašeho modulu. - vstřikovač
/ * eslint-disable * /
importovat chai z 'chai'
importovat sinon z „sinon“
const theControllerInjector = vyžadovat ('inject-loader! ./ theComponentController.js')
nechť {očekávat, měl by se uplatňovat} = chai
description ('superAwesomeComponent', function () {
  nechat pahýl
  nechte ovladač komponent
  nechat řadič
beforeEach (funkce setupComponent () {
    stub = sinon.stub (). návraty (1)
    theComponentController = theControllerInjector ({
      // Modul je opravdu jednoduchý, takže není nutné jej zesměšňovat
      // Ve skutečné aplikaci by to mohlo být mnohem složitější (tj. Něco, co umožňuje volání API)
      './fancyJsModule.js': stub
    }), ComponentController
    controller = new theComponentController ()
    controller.someOutput = sinon.stub ()
    controller.someInput = 1
  })
  description ('doSuperThings', function () {
    it ('calls fancyFunction', function () {
      controller.doSuperThings ()
      assert (stub.calledOnce)
    })
  })
})

Chcete-li použít injekční-zavaděč, používáme starou syntaxi zaváděcího požadavku + webpack, protože neexistuje import souboru zástupných znaků, který můžeme udělat pro import (nechceme, aby se všechny soubory js dostaly do injekčního zavaděče pořád) . Návrat tohoto požadavku nám dává funkci, kterou můžeme zavolat s objektem, který vytrácí různé dovozy:

theComponentController = theControllerInjector ({
  './fancyJsModule.js': stub
}), ComponentController

Zde vytěsňujeme fancyJsModule z importu našeho kontroléru. To nám umožňuje vrátit falešnou hodnotu a rozvrátit veškerou logiku, kterou by modul mohl udělat, abychom mohli izolovat všechny problémy, které se vyskytnou v testu.

Jako naši knihovnu prosazování používáme chai, sinon pro zesměšňování / špionáž a moka pro provádění testů.

Tento test se nepokouší být dobrým příkladem toho, co testovat, je pouze ukázat, jak lze testování nastavit pomocí ES6 + Webpack + Mocha + Angular.

Cílem je přinutit vývojáře, aby se zaměřil na psaní popisovačů AngularJS jako skutečných funkcí. Existuje silná tendence, aby tyto obsluhy byly prováděny čistě pro vedlejší účinky, a vytvoření těchto testů tuto skutečnost zvýrazní.

Soo…

Tato architektura poskytuje způsob, jak modernizovat frontend AngularJS, aniž by provedl skok do rámce. Jednou z největších výhod tohoto přístupu je to, že abstrahuje mnoho kódu specifického pro AngularJS.

Jedním z nejzložitějších prvků tohoto přístupu je rozhodování o tom, na co se budou moduly AngularJS používat pro vs. Snažím se co nejvíce upřednostňovat ES6. To by mělo usnadnit přenos aplikace pomocí této architektury do jiného rámce.

AngularJS má v sobě stále slušné množství života, ale není žádná debata o tom, že je za svým vrcholem. ES6 / 7 jsou však stále na vzestupu (http://vanilla-js.com).

Dlouho žijí AngularJS!

Mimochodem, podívejte se na mé předchozí výpůjčky na JS https://medium.com/@narthur157/let-s-talk-about-javascript-bdb0bdf57fae