[ 改訂版 ] Cycle.js - リアクティブ・プログラミングに特化した JS フレームワーク

[ 改訂版 ] Cycle.js - リアクティブ・プログラミングに特化した JS フレームワーク

[ 追記 ] Meguro.es x Gotanda.js #1 にて発表した際の資料です。Node学園 25時限目 での内容に一部誤りがあったので訂正しました。

------

Node学園 25時限目 ( 2017年4月24日 ) にて発表した際の資料です。
Cycle.js という JS フレームワークの概要と仕組みについて簡単にご紹介しています。

こちらのブログ記事にてより詳しく解説していますので、併せて御覧ください。
https://tech.recruit-mp.co.jp/front-end/post-11898/

82ed567f8497acfed7f7b464225dd536?s=128

wakamsha

April 25, 2017
Tweet

Transcript

  1. $ZDMFKT ϦΞΫςΟϒɾϓϩάϥϛϯάʹಛԽͨ͠+4ϑϨʔϜϫʔΫ - A functional and reactive JavaScript framework for

    predictable code -  /BPLJ:"."%" !XBLBNTIB /PEFֶԂ࣌ݶ໨.FHVSPFTº(PUBOEBKT
  2. 8IBU`T$ZDMFKT  $ZDMFKTͬͯԿʁ

  3. 7JSUVBM%0. 0CTFSWBCMF 0CTFSWBCMFͱ7JSUVBM%0.Λ XFCΞϓϦ։ൃ͠΍͘͢͢ΔͨΊʹʰബ͘ʱϥοϓͨ͠΋ͷ 8IBU`T$ZDMFKT 

  4. -JCSBSJFTUIBUTVQQPSU$ZDMFKT

  5. YTUSFBN3Y+4 w 0CTFSWBCMFTͱ͍͏ΞʔΩςΫνϟΛ༻͍ͨϦΞΫςΟϒϓ ϩάϥϛϯά༻ͷϥΠϒϥϦ w ඇಉظॲཧΛ؆͔ܿͭՄಡੑߴ͘ίʔσΟϯάͰ͖Δ w YTUSFBN͸3T+4ͷܰྔ൛ w ˞ΦϖϨʔλͷ਺΍ϝιου໊͕Ұ෦ҟͳΔ

    w 3Y+4Ͱͷ୅༻΋Մೳ w ˞Ұ෦Ωϟετ͕ඞཁ
  6. 4OBCCEPN w Ծ૝%0.ΛૢΔγϯϓϧͳϥΠϒϥϦ w ϞδϡʔϧػೳΛ༗͓ͯ͠Γɺػೳ֦ு͕༰қʹՄೳ w %0.ੜ੒෦෼͸)ZQFS4DSJQUͷΞʔΩςΫνϟΛ࠾༻ w div()΍input()ͳͲ)5.-λά໊ͦͷ··ͷϝιο υ໊͕༻ҙ͞Ε͍ͯΔ

  7. #BTJD&YBNQMF ಈ͔ͯ͠ΈΑ͏

  8. #BTJD&YBNQMF

  9. import {Observable} from 'rxjs'; import {div, label, input, hr, h1,

    VNode, makeDOMDriver} from '@cycle/dom'; import {run} from '@cycle/rxjs-run'; import {DOMSource} from '@cycle/dom/rxjs-typings'; type Sources = { DOM: DOMSource; } type Sinks = { DOM: Observable<VNode>; } /** * ΞϓϦέʔγϣϯ * @param sources * @returns {{DOM: Observable<VNode>}} */ function main(sources: Sources): Sinks { // ΩʔೖྗΠϕϯτΛऔಘ ( Intent ) const input$: Observable<Event> = sources.DOM.select('.field').events('input'); // ೖྗΠϕϯτ͔Βݱࡏͷঢ়ଶͳ͍͠஋Λऔಘ ( Model ) const name$: Observable<string> = Observable.from(input$) .map((ev: Event) => ev.target.value) .startWith(''); // ݱࡏͷঢ়ଶΛը໘ʹඳը ( View ) const vdom$: Observable<VNode> = name$.map(name => {
  10. /** * ΞϓϦέʔγϣϯ * @param sources * @returns {{DOM: Observable<VNode>}}

    */ function main(sources: Sources): Sinks { // ΩʔೖྗΠϕϯτΛऔಘ ( Intent ) const input$: Observable<Event> = sources.DOM.select('.field').events('input'); // ೖྗΠϕϯτ͔Βݱࡏͷঢ়ଶͳ͍͠஋Λऔಘ ( Model ) const name$: Observable<string> = Observable.from(input$) .map((ev: Event) => ev.target.value) .startWith(''); // ݱࡏͷঢ়ଶΛը໘ʹඳը ( View ) const vdom$: Observable<VNode> = name$.map(name => { return div('.well', [ div('.form-group', [ label('Name: '), input('.field.form-control', {attrs: {type: 'text'}}), ]), hr(), h1(`Hello ${name}`) ]); }); // ݁ՌΛυϥΠόʹग़ྗ͢Δ ( Sinks ) return { DOM: vdom$ }; }
  11. label('Name: '), input('.field.form-control', {attrs: {type: 'text'}}), ]), hr(), h1(`Hello ${name}`)

    ]); }); // ݁ՌΛυϥΠόʹग़ྗ͢Δ ( Sinks ) return { DOM: vdom$ }; } // ΞϓϦέʔγϣϯ͔Βͷ໭Γ஋Λड͚औΔυϥΠό܈Λఆٛ const drivers = { DOM: makeDOMDriver('#app-container') }; // ΞϓϦέʔγϣϯͱυϥΠόΛ݁ͼ͚ͭΔ run(main, drivers);
  12. label('Name: '), input('.field.form-control', {attrs: {type: 'text'}}), ]), hr(), h1(`Hello ${name}`)

    ]); }); // ݁ՌΛυϥΠόʹग़ྗ͢Δ ( Sinks ) return { DOM: vdom$ }; } // ΞϓϦέʔγϣϯ͔Βͷ໭Γ஋Λड͚औΔυϥΠό܈Λఆٛ const drivers = { DOM: makeDOMDriver('#app-container') }; // ΞϓϦέʔγϣϯͱυϥΠόΛ݁ͼ͚ͭΔ run(main, drivers);
  13. %FTJHOQIJMPTPQIZPG$ZDMFKT $ZDMFKTͷઃܭࢥ૝

  14. Application Application %FTJHOQIJMPTPQIZPG$ZDMFKT

  15. driver() main() %FTJHOQIJMPTPQIZPG$ZDMFKT

  16. driver() main() ෭࡞༻ ( Effective ) DOM API %FTJHOQIJMPTPQIZPG$ZDMFKT

  17. driver() main() ෭࡞༻ ( Effective ) DOM API Source Stream

    %FTJHOQIJMPTPQIZPG$ZDMFKT
  18. driver() main() ঢ়ଶ ( State ) ෭࡞༻ ( Effective )

    DOM API Source Stream %FTJHOQIJMPTPQIZPG$ZDMFKT
  19. driver() main() ঢ়ଶ ( State ) ෭࡞༻ ( Effective )

    DOM API Source Stream Sink Stream %FTJHOQIJMPTPQIZPG$ZDMFKT
  20. Rx.Observable .timer(0, 1000) .map(i => `Seconds elapsed: ${i}`) .subscribe( (text:

    string) => { const container = document.querySelector('#app'); container.textContent = text; } ); ঢ়ଶ ( State ) ෭࡞༻ ( Effective ) %FTJHOQIJMPTPQIZPG$ZDMFKT
  21. Rx.Observable .timer(0, 1000) .map(i => `Seconds elapsed: ${i}`) .subscribe( (text:

    string) => { const container = document.querySelector('#app'); container.textContent = text; } ); ঢ়ଶ ( State ) ෭࡞༻ ( Effective ) %FTJHOQIJMPTPQIZPG$ZDMFKT
  22. Rx.Observable .timer(0, 1000) .map(i => `Seconds elapsed: ${i}`) .subscribe( (text:

    string) => { const container = document.querySelector('#app'); container.textContent = text; } ); Observable Subject ঢ়ଶ ( State ) ෭࡞༻ ( Effective ) %FTJHOQIJMPTPQIZPG$ZDMFKT
  23. function main() { return Rx.Observable .timer(0, 1000) .map(i => `Seconds

    elapsed: ${i}`); } function DOMDriver(text$: Observable<string>) { text$.subscribe( (text: string) => { const container = document.querySelector('#app'); container.textContent = text; } ); } Observable Subject %FTJHOQIJMPTPQIZPG$ZDMFKT
  24. function main() { return Rx.Observable .timer(0, 1000) .map(i => `Seconds

    elapsed: ${i}`); } function DOMDriver(text$: Observable<string>) { text$.subscribe( (text: string) => { const container = document.querySelector('#app'); container.textContent = text; } ); } // Run const sink = main(); DOMDriver(sink);
  25. function main() { return Rx.Observable .timer(0, 1000) .map(i => `Seconds

    elapsed: ${i}`); } function DOMDriver(text$: Observable<string>) { text$.subscribe( (text: string) => { const container = document.querySelector('#app'); container.textContent = text; } ); } // Run const sink = main(); DOMDriver(sink); // υϥΠό܈Λఆٛ
 const drivers = {
 DOM: makeDOMDriver('#app')
 };
 // main ͱυϥΠόΛ݁ͼ͚ͭΔ
 run(main, drivers);
  26. *OUSPEVDFNZTFMG Զͷ໊લΛݴͬͯΈΖ ࣗݾ঺հ

  27. גࣜձࣾϦΫϧʔτϚʔέςΟϯάύʔτφʔζ XFCϑϩϯτΤϯυΤϯδχΞ ࢁా ௚थ /BPLJ:"."%" *OUSPEVDFNZTFMG !XBLBNTIB

  28. http://tech.recruit-mp.co.jp NET BIZ DIV. TECH BLOG

  29. None
  30. http://tech.recruit-mp.co.jp 3.1 ϒϩά

  31. 5IBOLZPV