Upgrade to Pro — share decks privately, control downloads, hide ads and more …

フロントエンドもKotlinで書きたい! -WebページをKotlin/JSで作った軌跡-

subroh_0508
August 24, 2019

フロントエンドもKotlinで書きたい! -WebページをKotlin/JSで作った軌跡-

Kotlin Fest 2019 - Hall C 16:45〜
「フロントエンドもKotlinで書きたい! -WebページをKotlin/JSで作った軌跡-」のセッション資料です

subroh_0508

August 24, 2019
Tweet

More Decks by subroh_0508

Other Decks in Technology

Transcript

  1. ϑϩϯτΤϯυ΋,PUMJOͰॻ͖͍ͨʂ
    8FCϖʔδΛ,PUJO+4Ͱ࡞ͬͨي੻
    ,PUMJO'FTU
    ʹ͜͠Γ͞ͿΖʙ[email protected]

    View Slide

  2. "CPVU.F
    w ʹ͜͠Γ͞ͿΖʙ ຊ໊ࡔ্੖৴

    w ౦ژ౎ɾҏ౾େౡग़਎
    w גࣜձࣾ#FBS5BJM
    w "OESPJEΤϯδχΞ ,PUMJO+BWB
    ˑ
    w 8FCΤϯδχΞ 3BJMT3FBDU

    w झຯͰ,PUMJO+4

    View Slide

  3. "HFOEB
    ,PUMJO+4ͷ֓ཁ
    w ྺ࢙ɺࣄྫ঺հ[email protected]
    ,PUMJO+43FBDUKTͰʮ)FMMP 8PSMEʯ
    w OQN,PUMJO+4
    w (SBEMF,PUMJO+4
    ,PUMJO+4ΞϓϦ΁ͷ+4੡ϥΠϒϥϦͷಋೖ

    View Slide

  4. ͜ͷηογϣϯΛ௨ͯ͠ʜ
    w ,PUMJO+4Λ࢖ͬͯ؆୯ͳ8FCΞϓϦΛ࡞Δ͜ͱ͕Ͱ͖Δ
    w ,PUMJO+4Ͱͷ։ൃͷΠϝʔδڞ༗
    w ,PUMJO+4ΞϓϦ΁ͷ+4੡ϥΠϒϥϦಋೖͷ஌ݟڞ༗
    w +4੡ϥΠϒϥϦΛ,PUMJOΒ͘͠ѻ͏ͨΊͷ5JQTɺϋϚΓͲ͜Ζղઆ

    View Slide

  5. ,PUMJO+4ͷ֓ཁ

    View Slide

  6. rެࣜϦϑΝϨϯεݪจ

    View Slide

  7. ,PUMJO+4ͷ֓ཁ
    +4ͰͷϑϩϯτΤϯυ։ൃʹඞཁͳϞϊ
    w +4ίʔυɾελΠϧγʔτ $44
    ɾ)5.-ϑΝΠϧ
    w ύοέʔδ؅ཧπʔϧ OQN

    w Ϟδϡʔϧόϯυϥʔ 8FCQBDL

    w ෳ਺ͷ+4ϞδϡʔϧΛͭʹ·ͱΊɺ)5.-Ͱѻ͍΍͍͢ܗʹ͢Δ
    w Ϟδϡʔϧؒͷґଘؔ܎ղܾɺNJOJGZɺίʔυɾϦιʔεͷม׵
    ྫ54ˠ+4ɺ4BTTˠ$44

    View Slide

  8. ,PUMJO+4ͷ֓ཁ
    w +4ίʔυʹม׵Մೳͳ,PUMJOඪ४ϥΠϒϥϦ
    w ,PUMJOˠ+4΁ͷม׵Λ࣮ߦ͢Δ(SBEMFϓϥάΠϯ
    w OQN8FCQBDLͷίϚϯυΛ࣮ߦ͢Δ(SBEMFϓϥάΠϯ
    ʴIUNMDTTͷ%4-ϥΠϒϥϦɺ֤छ+4 ϥούʔ
    ϥΠϒϥϦ
    ,PUMJO+4͕ఏڙ͢ΔϞϊ
    ,PUMJO͔Β+4΁ͷม׵ɺOQNʹΑΔόʔδϣϯ؅ཧɺ8FCQBDLʹΑΔόϯυϧ
    શͯΛ,PUMJOͷੈքͰ׬݁ͤ͞ɺϑϩϯτΤϯυ։ൃΛ࣮ݱ͢Δʂ

    View Slide

  9. ,PUMJO+4ͷྺ࢙
    ,PUMJOϦϦʔε
    w ,PUMJO+4ਖ਼ࣜϦϦʔεɻʮLPUMJOKTʯͰ,PUMJOˠ+4ม׵͕Ͱ͖ΔΑ͏ʹ
    LPUMJOGSPOUFOEQMVHJOϦϦʔε
    w (SBEMF͔ΒͷOQNɺ8FCQBDLͷίϚϯυ࣮ߦΛαϙʔτ͢ΔϓϥάΠϯ
    ,PUMJOϦϦʔε
    w ,PUMJO.11ొ৔ɻ+7.΍/BUJWFͷϞδϡʔϧͱϩδοΫΛڞ௨Խ
    ,PUMJOϦϦʔε
    w LPUMJOKTˠDPNKFUCSBJOTLPUMJOKTʹվ໊ɺLPUMJOGSPOUFOEQMVHJOͷҰ෦ػೳ͕౷߹
    w ʮLPUMJOGSPOUFOEQMVHJO͸ࠓޙഇࢭ༧ఆʯͱࠂ஌
    ,PUMJOϦϦʔε
    w 8FCQBDLͷઃఆ߲໨௥Ճɺόάमਖ਼

    View Slide

  10. ,PUMJOϦϦʔε
    w ,PUMJO+4ਖ਼ࣜϦϦʔεɻʮLPUMJOKTʯͰ,PUMJOˠ+4ม׵͕Ͱ͖ΔΑ͏ʹ
    LPUMJOGSPOUFOEQMVHJOϦϦʔε
    w (SBEMF͔ΒͷOQNɺ8FCQBDLͷίϚϯυ࣮ߦΛαϙʔτ͢ΔϓϥάΠϯ
    ,PUMJOϦϦʔε
    w ,PUMJO.11ొ৔ɻ+7.΍/BUJWFͷϞδϡʔϧͱϩδοΫΛڞ௨Խ
    ,PUMJOϦϦʔε
    w LPUMJOKTˠDPNKFUCSBJOTLPUMJOKTʹվ໊ɺLPUMJOGSPOUFOEQMVHJOͷҰ෦ػೳ͕౷߹
    w ʮLPUMJOGSPOUFOEQMVHJO͸ࠓޙഇࢭ༧ఆʯͱࠂ஌
    ,PUMJOϦϦʔε
    w 8FCQBDLͷઃఆ߲໨௥Ճɺόάमਖ਼
    ,PUMJO+4ͷྺ࢙
    ,PUMJO.11ͷҰ෦ͱͯ͠
    ֤छϓϥάΠϯ͕·ͱ·ΔྲྀΕ

    View Slide

  11. ,PUMJOϦϦʔε
    w ,PUMJO+4ਖ਼ࣜϦϦʔεɻʮLPUMJOKTʯͰ,PUMJOˠ+4ม׵͕Ͱ͖ΔΑ͏ʹ
    LPUMJOGSPOUFOEQMVHJOϦϦʔε
    w (SBEMF͔ΒͷOQNɺ8FCQBDLͷίϚϯυ࣮ߦΛαϙʔτ͢ΔϓϥάΠϯ
    ,PUMJOϦϦʔε
    w ,PUMJO.11ొ৔ɻ+7.΍/BUJWFͷϞδϡʔϧͱϩδοΫΛڞ௨Խ
    ,PUMJOϦϦʔε
    w LPUMJOKTˠDPNKFUCSBJOTLPUMJOKTʹվ໊ɺLPUMJOGSPOUFOEQMVHJOͷҰ෦ػೳ͕౷߹
    w ʮLPUMJOGSPOUFOEQMVHJO͸ࠓޙഇࢭ༧ఆʯͱࠂ஌
    ,PUMJOϦϦʔε
    w 8FCQBDLͷઃఆ߲໨௥Ճɺόάमਖ਼
    ,PUMJO+4ͷྺ࢙
    ͜ͷ࣌ظͷ৘ใ͕݁ߏଟ͍
    ࠓޙݹ͘ͳΔͷͰ஫ҙʂ

    View Slide

  12. ݁ہͲΜͳϞϊ͕࡞ΕΔͷ
    w ࠷ऴతͳ੒Ռ෺ˠ+4ϑΝΠϧɺ8FCQBDLͷόϯυϧϑΝΠϧ
    w +4Ͱ࡞ΕΔϞϊ͸શͯ,PUMJO+4Ͱ࡞Δ͜ͱ͕Մೳʂ ཧ࿦্

    w ࣄྫ঺հ
    w ࣗ෼ͷϙʔτϑΥϦΦαΠτ
    w খௗ͞Μͷࣗशࣨ ϒϥ΢β্Ͱ,PUMJOΛֶ΂ΔֶशαΠτ

    View Slide

  13. ࣄྫ
    ϙʔτϑΥϦΦαΠτ
    (JUIVCTVCSPIQPSUGPMJP
    ࢖༻ϥΠϒϥϦɾ"1*
    w ,PUMJO+4 W

    w 3FBDUKT
    w .BUFSJBM6*
    ˠ'JSFCBTF)PTUJOHͰϗεςΟϯά
    ɹ-JOLTVCSPINF
    w (PPHMF.BQ"1*
    w SFBDUTXJQFBCMFWJFXT

    View Slide

  14. ࣄྫ
    খௗ͞Μͷࣗशࣨ
    (JUIVCTVCSPIPUPOBTIJLPUMJOEFW
    ػೳ
    w ϒϥ΢β্Ͱ,PUMJO,PBOTͷ՝୊͕Ͱ͖Δ
    w ՝୊ͷΫϦΞɺίϯύΠϧΤϥʔ౳ʹ
    ɹ߹ΘͤͯϨεϙϯεΛฦ͢
    ˠখௗ͞ΜͱϖΞϓϩ͠ͳ͕Β,PUMJOΛֶश
    w ,PUMJOͷ3&1-LPUMJOQMBZHSPVOE
    w ϖʔδભҠ3FBDI3PVUFS
    খௗ͞Μ

    View Slide

  15. View Slide

  16. ࣄྫ
    খௗ͞Μͷࣗशࣨ
    (JUIVCTVCSPIPUPOBTIJLPUMJOEFW
    ࢓༷ϥΠϒϥϦ
    w ,PUMJO+4 W

    w 3FBDUKT
    w .BUFSJBM6*
    ˠϧʔςΟϯάϥΠϒϥϦΛ࢖ͬͨ41"
    ɹ
    খௗ͞Μ
    w 3FBDI3PVUFS
    w LPUMJOQMBZHSPVOE

    View Slide

  17. ,PUMJO+4ɺҙ֎ͱͰ͖Δࢠ
    Կ͔࡞Γͨ͘ͳ͖ͬͯͨ

    View Slide

  18. ,PUMJO+43FBDUKTͰ
    ʮ)FMMP 8PSMEʯ

    View Slide

  19. ,PUMJO+4Ͱ3FBDUΞϓϦΛ࡞Δ

    ,PUMJOͰॻ͍ͨίʔυΛ௚઀OQN8FCQBDLͰϏϧυ
    w OQNɺ8FCQBDLͷίϚϯυΛ௚઀࣮ߦ +4ͷੈքʹ,PUMJOΛಋೖ͢Δ

    w ϝϦοτ+4Ͱॻ͔ΕͨطଘͷϓϩδΣΫτʹಋೖ͠΍͍͢
    w σϝϦοτOQNɺ8FCQBDL͕ͱ͖ͬͭʹ͍͘ ಛʹ,PUMJOΤϯδχΞ

    View Slide

  20. ,PUMJO+4Ͱ3FBDUΞϓϦΛ࡞Δ

    ,PUMJOͰॻ͍ͨίʔυΛ(SBEMFͰϏϧυ
    w (SBEMFܦ༝ͰOQNɺ8FCQBDLͷίϚϯυΛ࣮ߦ͢Δ
    w ϝϦοτ࢖͍׳Εͨ(SBEMFͰॻ͚Δɺ,PUMJO%4-ͷଘࡏ
    ,PUMJO.11ͱͷ਌࿨ੑ͕ߴ͍
    w σϝϦοτطଘͷ+4ϓϩδΣΫτ΁ͷಋೖ͸ݫ͍͠ɺ৘ใ͕গͳ͍

    View Slide


  21. ,PUMJO+4OQN
    w +FU#SBJOTDSFBUFSFBDULPUMJOBQQ
    w ʮDSFBUFSFBDUBQQʯͷ,PUMJO൛
    w ίϚϯυҰൃͰ3FBDUKTͷαϯϓϧΞϓϦΛੜ੒ ཁ/PEFKT

    ˠ,PUMJO+4ʹΑΔ3FBDUΞϓϦͷ࣮૷ΠϝʔδΛ௫Έ΍͍͢

    View Slide


  22. ,PUMJO+4OQN
    w ϞδϡʔϧͷΠϯετʔϧɾϓϩδΣΫτੜ੒ɾ࣮ߦ
    # インストール
    $ npm install -g create-react-kotlin-app
    # プロジェクト生成
    $ create-react-kotlin-app my-app
    # 実行
    $ cd my-app/
    $ npm start

    View Slide

  23. w MPDBMIPTUʹΞΫηε͢Δͱʜ

    ,PUMJO+4OQN

    View Slide

  24. σΟϨΫτϦߏ੒

    View Slide

  25. OQNʹΑΔύοέʔδ؅ཧʹඞཁͳ+40/

    View Slide

  26. ,PUMJO+4ͷϥούʔϞδϡʔϧ͕
    ґଘؔ܎ʹ௥ՃࡁΈʂ
    "devDependencies": {
    "@jetbrains/gen-idea-libs": "^1.0.11",
    "@jetbrains/kotlin-extensions": "^1.0.1-pre.58",
    "@jetbrains/kotlin-react": "^16.5.2-pre.58",
    "@jetbrains/kotlin-react-dom": "^16.5.2-pre.58",
    "@jetbrains/kotlin-webpack-plugin": "^2.0.3",
    "@jetbrains/ts2kt-automator": "^1.0.12",
    "kotlin": "^1.3.0",
    "kotlinx-html": “^0.6.11",

    }

    View Slide

  27. ,PUMJO+4ͷίʔυDTTϦιʔεϑΝΠϧ

    View Slide

  28. JOEFYLUͷதʹNBJOϝιου͕࣮૷
    ˠ࠷ॳʹݺͼग़͞ΕΔ

    View Slide

  29. package index
    import …
    fun main(args: Array) {
    requireAll(require.context("src", true, js("/\\.css$/")))
    render(document.getElementById("root")) {
    child(App::class) { }
    }
    }

    View Slide

  30. package index
    import …
    fun main(args: Array) {
    requireAll(require.context("src", true, js("/\\.css$/")))
    render(document.getElementById("root")) {
    child(App::class) { }
    }
    }
    TSDҎԼͷDTTΛҰׅಡΈࠐΈ

    View Slide

  31. package index
    import …
    fun main(args: Array) {
    requireAll(require.context("src", true, js("/\\.css$/")))
    render(document.getElementById("root")) {
    child(App::class) { }
    }
    }
    "QQίϯϙʔωϯτΛSFOEFS

    View Slide

  32. package app
    import …
    class App : RComponent() {
    override fun RBuilder.render() {
    div("App-header") {
    child(Logo::class) { }
    h2 { +"Welcome to React with Kotlin" }
    }
    p("App-intro") {
    +"To get started, edit "
    code { +"app/App.kt" }
    +" and save to reload."
    }
    p("App-ticker") {
    child(Ticker:class) { attrs.startFrom = 0 }
    }
    }
    }

    View Slide

  33. package app
    import …
    class App : RComponent() {
    override fun RBuilder.render() {
    div("App-header") {
    child(Logo::class) { }
    h2 { +"Welcome to React with Kotlin" }
    }
    p("App-intro") {
    +"To get started, edit "
    code { +"app/App.kt" }
    +" and save to reload."
    }
    p("App-ticker") {
    child(Ticker:class) { attrs.startFrom = 0 }
    }
    }
    }
    3$PNQPOFOUΛܧঝ$PNQPOFOUఆٛ

    View Slide

  34. package app
    import …
    class App : RComponent() {
    override fun RBuilder.render() {
    div("App-header") {
    child(Logo::class) { }
    h2 { +"Welcome to React with Kotlin" }
    }
    p("App-intro") {
    +"To get started, edit "
    code { +"app/App.kt" }
    +" and save to reload."
    }
    p("App-ticker") {
    child(Ticker:class) { attrs.startFrom = 0 }
    }
    }
    }
    SFOEFSϝιου಺ʹIUNMΛ%4-Ͱهड़

    View Slide

  35. package app
    import …
    class App : RComponent() {
    override fun RBuilder.render() {
    div("App-header") {
    child(Logo::class) { }
    h2 { +"Welcome to React with Kotlin" }
    }
    p("App-intro") {
    +"To get started, edit "
    code { +"app/App.kt" }
    +" and save to reload."
    }
    p("App-ticker") {
    child(Ticker:class) { attrs.startFrom = 0 }
    }
    }
    }
    EJWDMBTT/BNFl"QQIFBEFSzʜEJW

    View Slide

  36. package app
    import …
    class App : RComponent() {
    override fun RBuilder.render() {
    div("App-header") {
    child(Logo::class) { }
    h2 { +"Welcome to React with Kotlin" }
    }
    p("App-intro") {
    +"To get started, edit "
    code { +"app/App.kt" }
    +" and save to reload."
    }
    p("App-ticker") {
    child(Ticker:class) { attrs.startFrom = 0 }
    }
    }
    }
    I8FMDPNFUP3FBDUXJUI,PUMJOI

    View Slide

  37. package app
    import …
    class App : RComponent() {
    override fun RBuilder.render() {
    div("App-header") {
    child(Logo::class) { }
    h2 { +"Welcome to React with Kotlin" }
    }
    p("App-intro") {
    +"To get started, edit "
    code { +"app/App.kt" }
    +" and save to reload."
    }
    p("App-ticker") {
    child(Ticker:class) { attrs.startFrom = 0 }
    }
    }
    }
    ճస͢Δϩΰͷදࣔίϯϙʔωϯτ

    View Slide

  38. package app
    import …
    class App : RComponent() {
    override fun RBuilder.render() {
    div("App-header") {
    child(Logo::class) { }
    h2 { +"Welcome to React with Kotlin" }
    }
    p("App-intro") {
    +"To get started, edit "
    code { +"app/App.kt" }
    +" and save to reload."
    }
    p("App-ticker") {
    child(Ticker:class) { attrs.startFrom = 0 }
    }
    }
    }
    ܦա࣌ؒදࣔͷίϯϙʔωϯτ

    View Slide

  39. interface TickerProps : RProps {
    var startFrom: Int
    }
    interface TickerState : RState {
    var secondsElapsed: Int
    }
    class Ticker(props: TickerProps) : RComponent(props) {
    override fun TickerState.init(props: TickerProps) {
    secondsElapsed = props.startFrom
    }
    var timerID: Int? = null
    override fun componentDidMount() {
    timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000)
    }
    override fun componentWillUnmount() {
    window.clearInterval(timerID!!)
    }
    override fun RBuilder.render() {
    +"This app has been running for ${state.secondsElapsed} seconds."
    }
    }

    View Slide

  40. interface TickerProps : RProps {
    var startFrom: Int
    }
    interface TickerState : RState {
    var secondsElapsed: Int
    }
    class Ticker(props: TickerProps) : RComponent(props) {
    override fun TickerState.init(props: TickerProps) {
    secondsElapsed = props.startFrom
    }
    var timerID: Int? = null
    override fun componentDidMount() {
    timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000)
    }
    override fun componentWillUnmount() {
    window.clearInterval(timerID!!)
    }
    override fun RBuilder.render() {
    +"This app has been running for ${state.secondsElapsed} seconds."
    }
    }
    31SPQTΛܧঝͨ͠5JDLFS1SPQT
    ˠ਌ίϯϙʔωϯτ͔Β
    ɹड͚औΔ஋ͷఆٛ

    View Slide

  41. interface TickerProps : RProps {
    var startFrom: Int
    }
    interface TickerState : RState {
    var secondsElapsed: Int
    }
    class Ticker(props: TickerProps) : RComponent(props) {
    override fun TickerState.init(props: TickerProps) {
    secondsElapsed = props.startFrom
    }
    var timerID: Int? = null
    override fun componentDidMount() {
    timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000)
    }
    override fun componentWillUnmount() {
    window.clearInterval(timerID!!)
    }
    override fun RBuilder.render() {
    +"This app has been running for ${state.secondsElapsed} seconds."
    }
    }
    34UBUFΛܧঝͨ͠5JDLFS4UBUF
    ˠ5JDLFSίϯϙʔωϯτͷঢ়ଶ

    View Slide

  42. interface TickerProps : RProps {
    var startFrom: Int
    }
    interface TickerState : RState {
    var secondsElapsed: Int
    }
    class Ticker(props: TickerProps) : RComponent(props) {
    override fun TickerState.init(props: TickerProps) {
    secondsElapsed = props.startFrom
    }
    var timerID: Int? = null
    override fun componentDidMount() {
    timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000)
    }
    override fun componentWillUnmount() {
    window.clearInterval(timerID!!)
    }
    override fun RBuilder.render() {
    +"This app has been running for ${state.secondsElapsed} seconds."
    }
    }
    JOJUؔ਺Ͱܦա࣌ؒͷॳظԽ

    View Slide

  43. interface TickerProps : RProps {
    var startFrom: Int
    }
    interface TickerState : RState {
    var secondsElapsed: Int
    }
    class Ticker(props: TickerProps) : RComponent(props) {
    override fun TickerState.init(props: TickerProps) {
    secondsElapsed = props.startFrom
    }
    var timerID: Int? = null
    override fun componentDidMount() {
    timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000)
    }
    override fun componentWillUnmount() {
    window.clearInterval(timerID!!)
    }
    override fun RBuilder.render() {
    +"This app has been running for ${state.secondsElapsed} seconds."
    }
    }
    ඵ͝ͱʹTUBUFͷ஋Λߋ৽
    ˞TFU4UBUFϝιουͰ஋ߋ৽
    SFOEFSϝιουͷݺͼग़͠

    View Slide

  44. interface TickerProps : RProps {
    var startFrom: Int
    }
    interface TickerState : RState {
    var secondsElapsed: Int
    }
    class Ticker(props: TickerProps) : RComponent(props) {
    override fun TickerState.init(props: TickerProps) {
    secondsElapsed = props.startFrom
    }
    var timerID: Int? = null
    override fun componentDidMount() {
    timerID = window.setInterval({ setState { secondsElapsed += 1 } }, 1000)
    }
    override fun componentWillUnmount() {
    window.clearInterval(timerID!!)
    }
    override fun RBuilder.render() {
    +"This app has been running for ${state.secondsElapsed} seconds."
    }
    }
    ܦա࣌ؒͷදࣔ

    View Slide

  45. ίʔυൺֱ
    class App extends React.Component {
    render() {
    return (


    Welcome to React with Kotlin

    To get started, edit
    app/App.kt
    and save to reload.





    );
    }
    }
    class App : RComponent() {
    override fun RBuilder.render() {
    div("App-header") {
    child(Logo::class) { }
    h2 { +"Welcome to React with Kotlin" }
    }
    p("App-intro") {
    +"To get started, edit "
    code { +"app/App.kt" }
    +" and save to reload."
    }
    p("App-ticker") {
    child(Ticker:class) { attrs.startFrom = 0 }
    }
    }
    }
    +4 ,PUMJO

    View Slide

  46. w +4ϥΠϒϥϦΛ௥Ճ͍ͨ͠ʂ
    w npm install hoge --save
    w ௥Ճͨ͠+4ϥΠϒϥϦΛ,PUMJOͰѻ͍͍ͨʂ
    w ޙड़
    w 8FCQBDLͷઃఆΛΧελϚΠζ͍ͨ͠ʂ
    w npm run eject ͰઃఆϑΝΠϧΛग़ྗՄ

    ,PUMJO+4OQN

    View Slide

  47. *OUFMMJ+*%&"6MUJNBUFͰʮ)FMMP 8PSMEʯ·Ͱ

    ,PUMJO+4(SBEMF
    $SFBUF/FX1SPKFDU
    ˠ(SBEMF
    ˠ,PUMJO+4GPSCSPXTFS
    ˠ/FYU

    View Slide


  48. ,PUMJO+4(SBEMF
    w (SPVQ*E
    w "SUJGBDU*E
    w 1SPKFDUOBNF
    w 1SPKFDUMPDBUJPO
    ͓޷ΈͰ

    View Slide


  49. ,PUMJO+4(SBEMF
    ϓϩδΣΫτͷ਽ܗ͕ੜ੒ʂ
    ΍Δ͜ͱ
    w (SBEMFʹґଘؔ܎ɺϏϧυઃఆ௥Ճ
    w ,PUMJO+4ͷίʔυ௥Ճ
    w JOEFYIUNMͷ࡞੒
    4BNQMF3FQPTVCSPILPUMJOSFBDUTBNQMF

    View Slide

  50. (SBEMF1MVHJOͷࢦఆSFQPTJUPSJFTͷࢦఆ

    ,PUMJO+4(SBEMF
    plugins {
    id 'org.jetbrains.kotlin.js' version '1.3.50'
    }
    group 'subroh0508.net'
    version '1.0-SNAPSHOT'
    repositories {
    jcenter()
    mavenCentral()
    maven { url "http://dl.bintray.com/kotlin/kotlin-js-wrappers" }
    maven { url 'https://plugins.gradle.org/m2/' }
    }

    View Slide

  51. 8FCQBDLͷઃఆ

    ,PUMJO+4(SBEMF
    kotlin {
    target {
    browser {
    runTask {
    devServer = new KotlinWebpackConfig.DevServer(
    true, false, true, true, false,
    8080,
    null,
    ["$projectDir/src/main/resources".toString()]
    )
    archiveFileName = "kotlin-react-sample.js"
    }
    }
    }

    View Slide

  52. 8FCQBDLͷઃఆ

    ,PUMJO+4(SBEMF
    kotlin {
    target {
    browser {
    runTask {
    devServer = new KotlinWebpackConfig.DevServer(
    true, false, true, true, false,
    8080,
    null,
    ["$projectDir/src/main/resources".toString()]
    )
    archiveFileName = "kotlin-react-sample.js"
    }
    }
    }
    CSPXTFSϒϩοΫ಺Ͱ
    8FCQBDLͷઃఆΛهड़

    View Slide

  53. kotlin {
    target {
    browser {
    runTask {
    devServer = new KotlinWebpackConfig.DevServer(
    true, false, true, true, false,
    8080,
    null,
    ["$projectDir/src/main/resources".toString()]
    )
    archiveFileName = "kotlin-react-sample.js"
    }
    }
    }
    8FCQBDLͷઃఆ

    ,PUMJO+4(SBEMF
    QPSUɾQSPYZɾDPOUFOU#BTF౳Λࢦఆ
    ˠϦιʔεσΟϨΫτϦͷύεΛ
    DPOUFOU#BTFʹࢦఆ

    View Slide

  54. kotlin {
    target {
    browser {
    runTask {
    devServer = new KotlinWebpackConfig.DevServer(
    true, false, true, true, false,
    8080,
    null,
    ["$projectDir/src/main/resources".toString()]
    )
    archiveFileName = "kotlin-react-sample.js"
    }
    }
    }
    8FCQBDLͷઃఆ

    ,PUMJO+4(SBEMF
    BSDIJWF'JMF/BNF
    ˠόϯυϧޙͷϑΝΠϧ໊

    View Slide

  55. ϥΠϒϥϦͷґଘؔ܎

    ,PUMJO+4(SBEMF
    sourceSets {
    main {
    dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
    implementation "org.jetbrains.kotlinx:kotlinx-html-js:0.6.12"
    implementation "org.jetbrains:kotlin-react:16.9.0-pre.83-kotlin-1.3.41"
    implementation "org.jetbrains:kotlin-react-dom:16.9.0-pre.83-kotlin-1.3.41"
    implementation "org.jetbrains:kotlin-styled:1.0.0-pre.83-kotlin-1.3.41"
    implementation npm("core-js", "~3.1.4")
    implementation npm("react", "16.9.0")
    implementation npm("react-dom", "16.9.0")
    implementation npm("styled-components", "3.4.10")
    implementation npm("inline-style-prefixer", "5.0.4")
    }
    }
    }

    View Slide

  56. sourceSets {
    main {
    dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
    implementation "org.jetbrains.kotlinx:kotlinx-html-js:0.6.12"
    implementation "org.jetbrains:kotlin-react:16.9.0-pre.83-kotlin-1.3.41"
    implementation "org.jetbrains:kotlin-react-dom:16.9.0-pre.83-kotlin-1.3.41"
    implementation "org.jetbrains:kotlin-styled:1.0.0-pre.83-kotlin-1.3.41"
    implementation npm("core-js", "~3.1.4")
    implementation npm("react", "16.9.0")
    implementation npm("react-dom", "16.9.0")
    implementation npm("styled-components", "3.4.10")
    implementation npm("inline-style-prefixer", "5.0.4")
    }
    }
    }
    ϥΠϒϥϦͷґଘؔ܎

    ,PUMJO+4(SBEMF
    ,PUMJO+4ͷඪ४ϥΠϒϥϦ

    View Slide

  57. ϥΠϒϥϦͷґଘؔ܎
    sourceSets {
    main {
    dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
    implementation "org.jetbrains.kotlinx:kotlinx-html-js:0.6.12"
    implementation "org.jetbrains:kotlin-react:16.9.0-pre.83-kotlin-1.3.41"
    implementation "org.jetbrains:kotlin-react-dom:16.9.0-pre.83-kotlin-1.3.41"
    implementation "org.jetbrains:kotlin-styled:1.0.0-pre.83-kotlin-1.3.41"
    implementation npm("core-js", "~3.1.4")
    implementation npm("react", "16.9.0")
    implementation npm("react-dom", "16.9.0")
    implementation npm("styled-components", "3.4.10")
    implementation npm("inline-style-prefixer", "5.0.4")
    }
    }
    }

    ,PUMJO+4(SBEMF
    LPUMJOYIUNMIUNMͷ%4-ϥΠϒϥϦ
    LPUMJOSFBDU3FBDUͷϥούʔϥΠϒϥϦ

    View Slide

  58. sourceSets {
    main {
    dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
    implementation "org.jetbrains.kotlinx:kotlinx-html-js:0.6.12"
    implementation "org.jetbrains:kotlin-react:16.9.0-pre.83-kotlin-1.3.41"
    implementation "org.jetbrains:kotlin-react-dom:16.9.0-pre.83-kotlin-1.3.41"
    implementation "org.jetbrains:kotlin-styled:1.0.0-pre.83-kotlin-1.3.41"
    implementation npm("core-js", "~3.1.4")
    implementation npm("react", "16.9.0")
    implementation npm("react-dom", "16.9.0")
    implementation npm("styled-components", "3.4.10")
    implementation npm("inline-style-prefixer", "5.0.4")
    }
    }
    }
    ϥΠϒϥϦͷґଘؔ܎

    ,PUMJO+4(SBEMF
    +4ͷϥΠϒϥϦ͸OQN
    Ͱࢦఆ

    View Slide

  59. ,PUMJO+4ͷίʔσΟϯά

    ,PUMJO+4(SBEMF
    // main.kt
    fun main() {
    render(document.getElementById("root")) {
    child(App::class) { }
    }
    }
    // App.kt
    class App : RComponent() {
    override fun RBuilder.render() {
    div { +"Hello, World!" }
    }
    }

    View Slide

  60. JOEFYIUNMͷ࡞੒

    ,PUMJO+4(SBEMF




    Title






    ˡIUNMϑΝΠϧΛ
    SFTPVSDFTҎԼʹ഑ஔ

    View Slide


  61. ,PUMJO+4(SBEMF
    ࣮ߦʂ
    $ ./gradlew run
    ,PUMJOͷ8FCΞϓϦɺ׬੒ʂ

    View Slide

  62. ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ

    interface AppState : RState {
    var count: Int
    }
    class App : RComponent() {
    override fun AppState.init() {
    count = 0
    }
    private fun onClickedButton() {
    setState { count += 1 }
    }
    override fun RBuilder.render() {
    div { +"count: ${state.count}" }
    button {
    attrs.onClickFunction = { onClickedButton() }
    +"click!"
    }
    }
    }

    View Slide

  63. interface AppState : RState {
    var count: Int
    }
    class App : RComponent() {
    override fun AppState.init() {
    count = 0
    }
    private fun onClickedButton() {
    setState { count += 1 }
    }
    override fun RBuilder.render() {
    div { +"count: ${state.count}" }
    button {
    attrs.onClickFunction = { onClickedButton() }
    +"click!"
    }
    }
    }
    TUBUFͷܕఆٛ
    ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ

    View Slide

  64. interface AppState : RState {
    var count: Int
    }
    class App : RComponent() {
    override fun AppState.init() {
    count = 0
    }
    private fun onClickedButton() {
    setState { count += 1 }
    }
    override fun RBuilder.render() {
    div { +"count: ${state.count}" }
    button {
    attrs.onClickFunction = { onClickedButton() }
    +"click!"
    }
    }
    }
    TUBUFͷॳظԽ
    ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ

    View Slide

  65. interface AppState : RState {
    var count: Int
    }
    class App : RComponent() {
    override fun AppState.init() {
    count = 0
    }
    private fun onClickedButton() {
    setState { count += 1 }
    }
    override fun RBuilder.render() {
    div { +"count: ${state.count}" }
    button {
    attrs.onClickFunction = { onClickedButton() }
    +"click!"
    }
    }
    }
    ϘλϯΫϦοΫ࣌ͷίʔϧόοΫؔ਺
    ˠTFU4UBUFϝιουͰΧ΢ϯτΛ૿΍͢
    ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ

    View Slide

  66. interface AppState : RState {
    var count: Int
    }
    class App : RComponent() {
    override fun AppState.init() {
    count = 0
    }
    private fun onClickedButton() {
    setState { count += 1 }
    }
    override fun RBuilder.render() {
    div { +"count: ${state.count}" }
    button {
    attrs.onClickFunction = { onClickedButton() }
    +"click!"
    }
    }
    }
    Χ΢ϯτͷදࣔͱίʔϧόοΫͷࢦఆ
    ˠBUUSTPO$MJDL'VODUJPO\^
    ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ

    View Slide

  67. ελΠϧΛ͚͍ͭͨ DTTJO,PUMJO

    override fun RBuilder.render() {
    div { +"count: ${state.count}" }
    styledButton {
    css {
    width = 200.px
    height = 100.px
    backgroundColor = Color.aqua
    }
    attrs.onClickFunction = { onClickedButton() }
    +"click!"
    }
    }

    View Slide

  68. ελΠϧΛ͚͍ͭͨ DTTJO,PUMJO

    override fun RBuilder.render() {
    div { +"count: ${state.count}" }
    styledButton {
    css {
    width = 200.px
    height = 100.px
    backgroundColor = Color.aqua
    }
    attrs.onClickFunction = { onClickedButton() }
    +"click!"
    }
    }
    TUZMFE#VUUPOʹ͢Δͱ
    DTT IBOEMFS3VMF4FU
    ϝιου͕ੜ͑Δ

    View Slide

  69. ελΠϧΛ͚͍ͭͨ DTTJO,PUMJO

    override fun RBuilder.render() {
    div { +"count: ${state.count}" }
    styledButton {
    css {
    width = 200.px
    height = 100.px
    backgroundColor = Color.aqua
    }
    attrs.onClickFunction = { onClickedButton() }
    +"click!"
    }
    }
    DTTϒϩοΫͷதͰελΠϧΛهड़
    ˞TUZMFEDPNQPOFOUͷػೳ

    View Slide

  70. ؆୯ͳ8FCϖʔδ͸࡞Εͨ
    ΋ͬͱϦονͳ͜ͱ͍ͨ͠

    View Slide

  71. ,PUMJO+4ΞϓϦ΁ͷ
    +4੡ϥΠϒϥϦͷಋೖ

    View Slide

  72. +4੡ϥΠϒϥϦͷಋೖ
    ಋೖͷखॱ
    w OQN(SBEMFʹ+4੡ϥΠϒϥϦʹର͢Δґଘؔ܎௥ه
    w ΞϊςʔγϣϯΛ࢖͍ɺ+4ϥΠϒϥϦΛ,PUMJOίʔυʹΠϯϙʔτ
    w Ћ
    ܕఆٛɺϝιουఆٛ
    ˠ+4ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ͨΊͷԼ͝͠Β͑
    ,PUMJO+4ಠࣗͷػೳΛۦ࢖͠ɺ࣮ݱ
    ϋϚΓͲ͜Ζʂ

    View Slide

  73. +4੡ϥΠϒϥϦͷಋೖ
    ґଘؔ܎௥Ճ ྫSFBDUTXJQFBCMFWJFXT

    w OQNͷ৔߹ npm install react-swipeable-views --save
    w (SBEMFͷ৔߹CVJMEHSBEMFʹ௥ه
    sourceSets {
    main {
    dependencies {
    ...
    implementation npm("react-swipeable-views")
    ...
    }
    }
    }

    View Slide

  74. +4੡ϥΠϒϥϦͷಋೖ
    !+T.PEVMFΞϊςʔγϣϯ
    w +4ϥΠϒϥϦΛ,PUMJOͷੈքʹΠϯϙʔτ͢ΔΞϊςʔγϣϯ
    @JsModule("react-swipeable-views")
    private external val reactSwipeableViewModule: dynamic
    fun test() {
    reactSwipeableViewModule.default.onChangeIndex = { ... }
    }

    View Slide

  75. +4੡ϥΠϒϥϦͷಋೖ
    !+T.PEVMFΞϊςʔγϣϯ
    w +4ϥΠϒϥϦΛ,PUMJOͷੈքʹΠϯϙʔτ͢ΔΞϊςʔγϣϯ
    @JsModule("react-swipeable-views")
    private external val reactSwipeableViewModule: dynamic
    fun test() {
    reactSwipeableViewModule.default.onChangeIndex = { ... }
    }
    +4ϥΠϒϥϦΛΠϯϙʔτ
    PO$IBOHF*OEFYʹ
    ϥϜμࣜΛ୅ೖ
    Ξϊςʔγϣϯ͚ͭͩʂ
    +4੡ϥΠϒϥϦΛ,PUMJOͷੈքͰ ҰԠ͸
    ѻ͑Δʂ

    View Slide

  76. +4੡ϥΠϒϥϦͷಋೖ
    !+T.PEVMFΞϊςʔγϣϯ
    w +4ϥΠϒϥϦΛ,PUMJOͷੈքʹΠϯϙʔτ͢ΔΞϊςʔγϣϯ
    @JsModule("react-swipeable-views")
    private external val reactSwipeableViewModule: dynamic
    fun test() {
    reactSwipeableViewModule.default.onChangeIndex = { ... }
    }
    EZOBNJDܕʜʁ

    View Slide

  77. +4੡ϥΠϒϥϦͷಋೖ
    EZOBNJDܕ
    w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ
    w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ
    // { foo: 'foo', bar: { hoge: 111 } }をKotlinで扱う場合
    val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }")
    print(jsObject["foo"]) // => 'foo'
    print(jsObject["bar"]["hoge"]) // => 111
    jsObject["aaa"]["bbb"] = "add from kotlin"
    print(jsObject["aaa"]["bbb"]) // => add from kotlin

    View Slide

  78. +4੡ϥΠϒϥϦͷಋೖ
    EZOBNJDܕ
    w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ
    w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ
    // { foo: 'foo', bar: { hoge: 111 } }をKotlinで扱う場合
    val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }")
    print(jsObject["foo"]) // => 'foo'
    print(jsObject["bar"]["hoge"]) // => 111
    jsObject["aaa"]["bbb"] = "add from kotlin"
    print(jsObject["aaa"]["bbb"]) // => add from kotlin
    +4ͷΦϒδΣΫτΛੜ੒

    View Slide

  79. +4੡ϥΠϒϥϦͷಋೖ
    EZOBNJDܕ
    w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ
    w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ
    // { foo: 'foo', bar: { hoge: 111 } }をKotlinで扱う場合
    val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }")
    print(jsObject["foo"]) // => 'foo'
    print(jsObject["bar"]["hoge"]) // => 111
    jsObject["aaa"]["bbb"] = "add from kotlin"
    print(jsObject["aaa"]["bbb"]) // => add from kotlin
    ଘࡏ͢ΔΩʔͷ஋Λऔͬͯ͘Δ

    View Slide

  80. +4੡ϥΠϒϥϦͷಋೖ
    EZOBNJDܕ
    w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ
    w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ
    // { foo: 'foo', bar: { hoge: 111 } }をKotlinで扱う場合
    val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }")
    print(jsObject["foo"]) // => 'foo'
    print(jsObject["bar"]["hoge"]) // => 111
    jsObject["aaa"]["bbb"] = "add from kotlin"
    print(jsObject["aaa"]["bbb"]) // => add from kotlin
    ଘࡏ͠ͳ͍Ωʔͷ஋Λ
    +4ʹ͍ۙจ๏Ͱ୅ೖͰ͖ͯ͠·͏

    View Slide

  81. +4੡ϥΠϒϥϦͷಋೖ
    EZOBNJDܕ
    w ,PUMJOͱ+4ͷڮ౉͠Λ୲͏ܕ
    w ,PUMJOͷੈքͰ+4ͷΦϒδΣΫτΛͦͷ··දݱ͢Δ͜ͱ͕Ͱ͖Δ
    // { foo: 'foo', bar: { hoge: 111 } }をKotlinで扱う場合
    val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: 111 } }")
    print(jsObject.foo) // => 'foo'
    print(jsObject.bar.hoge) // => 111
    jsObject.aaa.bbb = "add from kotlin" // => Error!
    ͜͏ͳͬͯཉ͍͠

    View Slide

  82. FYUFSOBMम০ࢠ
    w ,PUMJOͷܕఆٛͱ+4ͷΦϒδΣΫτΛϚοϐϯά͢Δम০ࢠ
    external interface TestObject {
    var foo: String
    var bar: TestChildObject
    }
    external interface TestChildObject {
    var hoge: String
    }
    val jsObject: TestObject = js("{ foo: 'foo', bar: { hoge: '111' } }")
    print(jsObject.foo) // => 'foo'
    print(jsObject.bar.hoge) // => 111
    5ZQF4DSJQUͷܕఆٛϑΝΠϧͱ
    ໾ׂ͸ಉ͡
    +4੡ϥΠϒϥϦͷಋೖ

    View Slide

  83. +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸
    w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ
    w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ
    +4੡ϥΠϒϥϦͷಋೖ
    @JsModule("react-swipeable-views")
    private external val reactSwipeableViewModule: dynamic
    external interface ReactSwipeableProps : RProps {
    var onChangeIndex: (Int, Int, ChangeReason) -> Unit
    }
    val RBuilder.swipeableViews: RClass
    get() = reactSwipeableViewModule.default as RClass

    View Slide

  84. +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸
    w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ
    w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ
    +4੡ϥΠϒϥϦͷಋೖ
    @JsModule("react-swipeable-views")
    private external val reactSwipeableViewModule: dynamic
    external interface ReactSwipeableProps : RProps {
    var onChangeIndex: (Int, Int, ChangeReason) -> Unit
    }
    val RBuilder.swipeableViews: RClass
    get() = reactSwipeableViewModule.default as RClass
    FYUFSOBMJOUFSGBDFΛఆٛ
    ˠϓϩύςΟʮPO$IBOHF*OEFYʯ͕
    ɹଘࡏ͢Δ͜ͱΛڭ͑ࠐ·ͤΔ

    View Slide

  85. +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸
    w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ
    w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ
    +4੡ϥΠϒϥϦͷಋೖ
    @JsModule("react-swipeable-views")
    private external val reactSwipeableViewModule: dynamic
    external interface ReactSwipeableProps : RProps {
    var onChangeIndex: (Int, Int, ChangeReason) -> Unit
    }
    val RBuilder.swipeableViews: RClass
    get() = reactSwipeableViewModule.default as RClass
    Πϯϙʔτͨ͠ϞδϡʔϧΛ3$MBTT31SPQTʹΩϟετ

    View Slide

  86. +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸
    w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ
    w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ
    +4੡ϥΠϒϥϦͷಋೖ
    fun ReactSwipeableProps.onChangeIndex(block: (Int) -> Unit) {
    onChangeIndex = { index: Int, _, _ -> block(index) }
    }
    // RComponentクラス内
    override fun RBuilder.render() {
    swipeableViews {
    attrs.onChangeIndex { i -> setState { tabNumber = i } }
    }
    }

    View Slide

  87. +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸
    w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ
    w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ
    +4੡ϥΠϒϥϦͷಋೖ
    fun ReactSwipeableProps.onChangeIndex(block: (Int) -> Unit) {
    onChangeIndex = { index: Int, _, _ -> block(index) }
    }
    // RComponentクラス内
    override fun RBuilder.render() {
    swipeableViews {
    attrs.onChangeIndex { i -> setState { tabNumber = i } }
    }
    }
    1SPQTͷ֦ுؔ਺ͱͯ͠
    ηολʔΛఆٛ

    View Slide

  88. +4੡ϥΠϒϥϦΛʮ,PUMJOΒ͘͠ʯѻ͏ʹ͸
    w FYUFSOBMम০ࢠΛ͚ͭͨΠϯλʔϑΣʔεͰܕఆٛ
    w +4ͷؔ਺Λ,PUMJOΒ͘͠ݺ΂ΔΑ͏ʹؔ਺ఆٛ
    +4੡ϥΠϒϥϦͷಋೖ
    fun ReactSwipeableProps.onChangeIndex(block: (Int) -> Unit) {
    onChangeIndex = { index: Int, _, _ -> block(index) }
    }
    // RComponentクラス内
    override fun RBuilder.render() {
    swipeableViews {
    attrs.onChangeIndex { i -> setState { tabNumber = i } }
    }
    }
    BUUSTϓϩύςΟ͔Β1SPQT͕ੜ͑Δ
    ˠ֦ுؔ਺ݺͼग़͠

    View Slide

  89. ܕఆٛɾؔ਺ఆٛʹ͔͔Δ࿑ྗ
    w ϥΠϒϥϦʹ͖ͭɺଟͯ͘΋ߦఔ౓
    w ඞཁͳྔ͚ͩఆٛ͢Ε͹0,
    ͨͩ͠
    w ڊେͳϥΠϒϥϦͷܕఆٛɾؔ਺ఆٛ͸ਖ਼௚͔ͳΓ͠ΜͲ͍
    w 6*ϑϨʔϜϫʔΫ͸ಛʹ τϥΠΞϯυΤϥʔͷαΠΫϧ͕ճͮ͠Β͍

    w ྫ3FBDU#PPUTUSBQɺ.BUFSJBM6*FUD
    +4੡ϥΠϒϥϦͷಋೖ

    View Slide

  90. ,PUMJO.BUFSJBM6*ͷ঺հ
    w (JUIVCTVCSPILPUMJONBUFSJBMVJ
    w ʮ.BUFSJBM6*ʯͷ,PUMJO+4ϥούʔϥΠϒϥϦ
    .BUFSJBM%FTJHOͳ6*࡞Γ͍ͨʂ
    ˠ͜ΕΛ࢖͑͹ղܾʂ
    ɹ,PUMJO+4΁ͷೖ໳͸͔͜͜Βʂ
    ɹϥΠϒϥϦ੡࡞ͷࢀߟʹ΋ʂ

    View Slide

  91. Ͳ͏ͯ͠΋ॻ͖ํ͕෼͔Βͳ͍ʂ
    w KT
    ϝιουҾ਺ʹੜͷ+4ίʔυΛจࣈྻͰ౉͢͜ͱ͕Ͱ͖Δ
    w +4ΛͱΓ͋͑ͣίϐϖˠগͣͭ͠,PUMJOʹม׵
    +4੡ϥΠϒϥϦͷಋೖ
    inline fun Window.initMap()
    = js("""new window.google.maps.Map(
    document.getElementById('googleMap'),
    {
    center: { lat: 34.740864, lng: 139.391152 },
    zoom: 11.3,
    mapTypeId: 'roadmap'
    }
    )""")
    ྫ(PPHMF.BQ"1*ͷॳظԽίʔυ

    View Slide

  92. ·ͱΊ
    ,PUMJO+4ͷ֓ཁɾΞοϓσʔτͷྺ࢙ʹ͍ͭͯղઆ
    w ౰ॳಠཱͨ͠ϓϥάΠϯ
    w ݱࡏ,PUMJO.11ͷҰཌྷΛ୲͏ཱͪҐஔ
    ,PUMJO+4Λ࢖ͬͨ8FCΞϓϦ੡࡞ͷϋϯζΦϯ
    w OQNͱ(SBEMFɺͲͪΒͰ΋Մ
    w EZOBNJDͱFYUFSOBM͕࢖͍͜ͳͤΕ͹ɺ+4੡ϥΠϒϥϦΛࣗ༝ʹѻ͑Δ
    w ࠷ऴखஈͱͯ͠KTϝιου

    View Slide

  93. ·ͱΊ
    ,PUMJO+4ͷ͜Ε͔Β
    w CBDLFOEͱGSPOUFOE͕ͭͷ,PUMJOϓϩδΣΫτʹ·ͱ·Δʂ
    w EBUBDMBTTͷڞ༗ɺ(SBEMFͰͷϏϧυɺ%4-ͰͷIUNMDTTهड़
    ຊ൪ಋೖ΁ͷน
    w ѹ౗తͳࣄྫɾ৘ใෆ଍ʂɹϥΠϒϥϦ΋·ͩ·ͩগͳ͍ʂ
    w ؆୯ͳϞϊͰ΋࡞ͬͯެ։ˠ஌ݟͷ஝ੵɺ,PUMJO+4ͷલਐͷҰॿ
    ,PUMJOͰϑϩϯτΤϯυͷ։ൃ͕Ͱ͖Δͷ͸ָ͍͠ʂ
    ୔ࢁͷਓ͕৮ͬͯ͘ΕΔͱخ͍͠☺

    View Slide

  94. ͓·͚ύʔτ
    ,PUMJO+4ίʔυ୳ࡧ

    View Slide

  95. ίʔυϦʔσΟϯά LPUMJOSFBDU

    ʮ)FMMP 8PSMEʯΛදࣔ͢ΔαϯϓϧΞϓϦ
    class App : RComponent() {
    override fun RBuilder.render() {
    div { +"Hello, World!" }
    }
    }
    3$PNQPOFOUʁ
    3#VJMEFSʁ
    SFOEFSϝιου͸
    ͳ֦ͥுؔ਺ʁ



    View Slide

  96. ίʔυϦʔσΟϯά LPUMJOSFBDU

    LPUMJOSFBDUͷසग़Ϋϥε
    w 3$MBTT
    ˠ3FBDU$MBTTͱରԠɺίϯϙʔωϯτͷܕఆٛ
    w 3$PNQPOFOU3FBDU&MFNFOU
    ˠ$PNQPOFOU3FBDU&MFNFOUͱରԠɺίϯϙʔωϯτͷΠϯελϯε
    w 3#VJMEFS
    ˠ%0.ߏஙɾ$PNQPOFOUͷੜ੒ʹ੹຿Λ࣋ͭLPUMJOSFBDUಠࣗͷΫϥε

    View Slide

  97. ίʔυϦʔσΟϯά LPUMJOSFBDU

    @ReactDsl
    open class RBuilder {
    val childList = mutableListOf()
    fun child(element: ReactElement): ReactElement {
    childList.add(element)
    return element
    }
    operator fun String.unaryPlus() {
    childList.add(this)
    }
    fun child(type: Any, props: P, children: List) =
    child(createElement(type, props, *children.toTypedArray()))
    fun child(type: Any, props: P, handler: RHandler): ReactElement {
    val children = with(RElementBuilder(props)) {
    handler()
    childList
    }
    return child(type, props, children)
    }
    operator fun RClass.invoke(handler: RHandler) =
    child(this, jsObject {}, handler)
    operator fun RProvider.invoke(value: T, handler: RHandler>) =
    child(this, jsObject { this.value = value }, handler)
    operator fun RConsumer.invoke(handler: RBuilder.(T) -> Unit) =
    child(this, jsObject> {
    this.children = { value ->
    buildElements { handler(value) }
    }
    }) {}
    fun RClass.node(
    props: P,
    children: List = emptyList()
    ) = child(this, clone(props), children)
    fun > child(klazz: KClass, handler: RHandler): ReactElement {
    val rClass = klazz.js as RClass
    return rClass(handler)
    }
    inline fun > child(noinline handler: RHandler) =
    child(C::class, handler)
    fun > node(
    klazz: KClass,
    props: P,
    children: List = emptyList()
    ): ReactElement {
    val rClass = klazz.js as RClass
    return rClass.node(props, children)
    }
    inline fun > node(props: P, children: List = emptyList()) =
    node(C::class, props, children)
    fun RProps.children() {
    childList.addAll(Children.toArray(children))
    }
    }
    3#VJMEFSΫϥε

    View Slide

  98. ίʔυϦʔσΟϯά LPUMJOSFBDU

    3#VJMEFSΫϥε
    w %0.ཁૉΛ֨ೲ͢ΔDIJME-JTUϓϩύςΟΛ࣋ͭ
    @ReactDsl
    open class RBuilder {
    val childList = mutableListOf()
    fun child(element: ReactElement): ReactElement {
    childList.add(element)
    return element
    }
    operator fun String.unaryPlus() {
    childList.add(this)
    }
    fun child(type: Any, props: P, children: List) =
    child(createElement(type, props, *children.toTypedArray()))
    fun child(type: Any, props: P, handler: RHandler): ReactElement {
    val children = with(RElementBuilder(props)) {
    handler()
    childList
    }
    return child(type, props, children)
    }
    operator fun RClass.invoke(handler: RHandler) =
    child(this, jsObject {}, handler)
    operator fun RProvider.invoke(value: T, handler: RHandler>) =
    child(this, jsObject { this.value = value }, handler)
    operator fun RConsumer.invoke(handler: RBuilder.(T) -> Unit) =
    child(this, jsObject> {
    this.children = { value ->
    buildElements { handler(value) }
    }
    }) {}
    fun RClass.node(
    props: P,
    children: List = emptyList()
    ) = child(this, clone(props), children)
    fun > child(klazz: KClass, handler: RHandler): ReactElement {
    val rClass = klazz.js as RClass
    return rClass(handler)
    }
    inline fun > child(noinline handler: RHandler) =
    child(C::class, handler)
    fun > node(
    klazz: KClass,
    props: P,
    children: List = emptyList()
    ): ReactElement {
    val rClass = klazz.js as RClass
    return rClass.node(props, children)
    }
    inline fun > node(props: P, children: List = emptyList()) =
    node(C::class, props, children)
    fun RProps.children() {
    childList.addAll(Children.toArray(children))
    }
    }
    @ReactDsl
    open class RBuilder {
    val childList = mutableListOf()

    View Slide

  99. ίʔυϦʔσΟϯά LPUMJOSFBDU

    3#VJMEFSΫϥε
    w %0.ཁૉΛ֨ೲ͢ΔDIJME-JTUϓϩύςΟΛ࣋ͭ
    w DIJMEϝιουΛ࢖ͬͯཁૉΛ௥ՃͰ͖Δ
    @ReactDsl
    open class RBuilder {
    val childList = mutableListOf()
    fun child(element: ReactElement): ReactElement {
    childList.add(element)
    return element
    }
    operator fun String.unaryPlus() {
    childList.add(this)
    }
    fun child(type: Any, props: P, children: List) =
    child(createElement(type, props, *children.toTypedArray()))
    fun child(type: Any, props: P, handler: RHandler): ReactElement {
    val children = with(RElementBuilder(props)) {
    handler()
    childList
    }
    return child(type, props, children)
    }
    operator fun RClass.invoke(handler: RHandler) =
    child(this, jsObject {}, handler)
    operator fun RProvider.invoke(value: T, handler: RHandler>) =
    child(this, jsObject { this.value = value }, handler)
    operator fun RConsumer.invoke(handler: RBuilder.(T) -> Unit) =
    child(this, jsObject> {
    this.children = { value ->
    buildElements { handler(value) }
    }
    }) {}
    fun RClass.node(
    props: P,
    children: List = emptyList()
    ) = child(this, clone(props), children)
    fun > child(klazz: KClass, handler: RHandler): ReactElement {
    val rClass = klazz.js as RClass
    return rClass(handler)
    }
    inline fun > child(noinline handler: RHandler) =
    child(C::class, handler)
    fun > node(
    klazz: KClass,
    props: P,
    children: List = emptyList()
    ): ReactElement {
    val rClass = klazz.js as RClass
    return rClass.node(props, children)
    }
    inline fun > node(props: P, children: List = emptyList()) =
    node(C::class, props, children)
    fun RProps.children() {
    childList.addAll(Children.toArray(children))
    }
    }
    fun child(element: ReactElement): ReactElement {
    childList.add(element)
    return element
    }

    View Slide

  100. @ReactDsl
    open class RBuilder {
    val childList = mutableListOf()
    fun child(element: ReactElement): ReactElement {
    childList.add(element)
    return element
    }
    operator fun String.unaryPlus() {
    childList.add(this)
    }
    fun child(type: Any, props: P, children: List) =
    child(createElement(type, props, *children.toTypedArray()))
    fun child(type: Any, props: P, handler: RHandler): ReactElement {
    val children = with(RElementBuilder(props)) {
    handler()
    childList
    }
    return child(type, props, children)
    }
    operator fun RClass.invoke(handler: RHandler) =
    child(this, jsObject {}, handler)
    operator fun RProvider.invoke(value: T, handler: RHandler>) =
    child(this, jsObject { this.value = value }, handler)
    operator fun RConsumer.invoke(handler: RBuilder.(T) -> Unit) =
    child(this, jsObject> {
    this.children = { value ->
    buildElements { handler(value) }
    }
    }) {}
    fun RClass.node(
    props: P,
    children: List = emptyList()
    ) = child(this, clone(props), children)
    fun > child(klazz: KClass, handler: RHandler): ReactElement {
    val rClass = klazz.js as RClass
    return rClass(handler)
    }
    inline fun > child(noinline handler: RHandler) =
    child(C::class, handler)
    fun > node(
    klazz: KClass,
    props: P,
    children: List = emptyList()
    ): ReactElement {
    val rClass = klazz.js as RClass
    return rClass.node(props, children)
    }
    inline fun > node(props: P, children: List = emptyList()) =
    node(C::class, props, children)
    fun RProps.children() {
    childList.addAll(Children.toArray(children))
    }
    }
    ίʔυϦʔσΟϯά LPUMJOSFBDU

    3#VJMEFSΫϥε
    w %0.ཁૉΛ֨ೲ͢ΔDIJME-JTUϓϩύςΟΛ࣋ͭ
    w DIJMEϝιουɺଞʹͭ
    w 3$PNQPOFOUΛ௥Ճ
    w QSPQTͱࢠཁૉΛݸผͷҾ਺Ͱ௥ՃFUD
    fun child(type: Any, props: P, children: List): ReactElement
    fun child(type: Any, props: P, handler: RHandler): ReactElement
    fun child(klazz: KClass, handler: RHandler): ReactElement
    inline fun child(noinline handler: RHandler): ReactElement

    View Slide

  101. ίʔυϦʔσΟϯά LPUMJOSFBDU

    DSFBUF&MFNFOUϝιου
    w τοϓϨϕϧؔ਺ɺ3FBDUKTͷ3FBDUDSFBUF&MFNFOUͱରԠ
    w 3#VJMEFSDIJME-JTU3FBDU&MFNFOUʹม׵
    w SFOEFSϝιουͰ࠷ऴతʹݺ͹ΕΔ
    external fun createElement(
    type: Any,
    props: P, vararg child: Any?
    ): ReactElement

    View Slide

  102. ίʔυϦʔσΟϯά LPUMJOSFBDU

    EJWϝιου
    w 3#VJMEFSͷ֦ுؔ਺ͱͯ͠ఆٛ
    w Ҿ਺CMPDLͷܕ͸3%0.#VJMEFS%*7
    6OJU
    w CMPDLͷதͰEJW΍ͦͷଞ)UNM5BHͷϝιουΛ࠶ؼతʹهड़Ͱ͖Δʂ
    w )UNM5BH༻ϝιου͸શ֦ͯுؔ਺ˠ3#VJMEFS͕ͪ͝Ό͔ͭͳ͍ʂ
    inline fun RBuilder.div(
    classes: String? = null,
    block: RDOMBuilder.() -> Unit
    ): ReactElement = tag(block) { DIV(attributesMapOf("class", classes), it) }
    3%0.#VJMEFS
    ˠ3#VJMEFSͷࢠΫϥε
    EJW\
    TQBO\
    B\^
    ^
    ^

    View Slide

  103. ίʔυϦʔσΟϯά LPUMJOSFBDU

    UBHϝιου
    w )UNM5BH༻ϝιου͔Β࣮ࡍʹݺͼग़͞ΕΔϝιου
    w 3#VJMEFSDIJME
    ϝιουΛ࣮ߦ
    w ,PUMJO+4Ͱಠࣗ)5.-λάΛѻ͍͍ͨʂˠUBHϝιουͰΑ͠ͳʹ
    inline fun RBuilder.tag(
    block: RDOMBuilder.() -> Unit,
    noinline factory: (TagConsumer) -> T
    ): ReactElement =
    child(RDOMBuilder(factory).apply { block() }.create())

    View Slide

  104. ίʔυϦʔσΟϯά LPUMJOSFBDU

    ,PUMJO+4ͷίʔσΟϯά
    w SFOEFSϝιου͕3#VJMEFSͷ֦ுؔ਺ͱͯ͠ఆٛ
    ˠSFOEFSϝιουͷείʔϓ಺͸ɺUIJT͕3#VJMEFSͷΠϯελϯεͱͳΔ
    ˠ,PUMJOΒ͘͠)5.-ͷݟͨ໨ΛଛͳΘͳ͍ܗͰ)5.-Λॻ͚Δʂ
    class App : RComponent() {
    override fun RBuilder.render() {
    div { +"Hello, World!" }
    }
    }
    ֦ுؔ਺ϥϜμࣜͷޮՌతͳ࣮༻ྫ

    View Slide

  105. ίʔυϦʔσΟϯά LPUMJOSFBDU

    ,PUMJO+4ͷίʔσΟϯά
    w SFOEFSϝιου͕3#VJMEFSͷ֦ுؔ਺ͱͯ͠ఆٛ
    ˠSFOEFSϝιουͷείʔϓ಺͸ɺUIJT͕3#VJMEFSͷΠϯελϯεͱͳΔ
    ˠ,PUMJOΒ͘͠)5.-ͷݟͨ໨ΛଛͳΘͳ͍ܗͰ)5.-Λॻ͚Δʂ
    class App : RComponent() {
    override fun RBuilder.render() {
    div { +"Hello, World!" }
    }
    }
    EJW\DIJME )FMMP 8PSME
    ^ͱಉ͡
    Ͱ΋ݟͨ͜ͱͳ͍ॻ͖ํʜ

    View Slide

  106. ίʔυϦʔσΟϯά LPUMJOSFBDU

    3#VJMEFSΫϥε
    w 4USJOHVOBSZ1MVT
    ϝιου
    w ԋࢉࢠΦʔόʔϩʔυ
    ˠจࣈྻͷ୯߲ԋࢉ IPHF
    Λ্ॻ͖
    w 3#VJMEFSΫϥεͷ಺෦Ͱએݴ
    ɹˠUIJT͕3#VJMEFSͷ࣌ͷΈ༗ޮ
    ɹɹར༻ൣғΛݶఆ͠ɺ෭࡞༻Λ཈͑Δʂ
    @ReactDsl
    open class RBuilder {
    val childList = mutableListOf()
    fun child(element: ReactElement): ReactElement {
    childList.add(element)
    return element
    }
    operator fun String.unaryPlus() {
    childList.add(this)
    }
    fun child(type: Any, props: P, children: List) =
    child(createElement(type, props, *children.toTypedArray()))
    fun child(type: Any, props: P, handler: RHandler): ReactElement {
    val children = with(RElementBuilder(props)) {
    handler()
    childList
    }
    return child(type, props, children)
    }
    operator fun RClass.invoke(handler: RHandler) =
    child(this, jsObject {}, handler)
    operator fun RProvider.invoke(value: T, handler: RHandler>) =
    child(this, jsObject { this.value = value }, handler)
    operator fun RConsumer.invoke(handler: RBuilder.(T) -> Unit) =
    child(this, jsObject> {
    this.children = { value ->
    buildElements { handler(value) }
    }
    }) {}
    fun RClass.node(
    props: P,
    children: List = emptyList()
    ) = child(this, clone(props), children)
    fun > child(klazz: KClass, handler: RHandler): ReactElement {
    val rClass = klazz.js as RClass
    return rClass(handler)
    }
    inline fun > child(noinline handler: RHandler) =
    child(C::class, handler)
    fun > node(
    klazz: KClass,
    props: P,
    children: List = emptyList()
    ): ReactElement {
    val rClass = klazz.js as RClass
    return rClass.node(props, children)
    }
    inline fun > node(props: P, children: List = emptyList()) =
    node(C::class, props, children)
    fun RProps.children() {
    childList.addAll(Children.toArray(children))
    }
    }
    operator fun String.unaryPlus() {
    childList.add(this)
    }

    View Slide

  107. ίʔυ୳ࡧ·ͱΊ
    ,PUMJO+4ͱϥούʔϥΠϒϥϦͷ࣮૷
    w ,PUMJOͷݴޠ࢓༷͕ϑϧ׆༻ʂɹݴޠ࢓༷ͷ্खͳ࢖͍ํΛֶ΂Δʂ
    w ֦ுؔ਺ɾϥϜμࣜɾԋࢉࢠΦʔόʔϩʔυFUD
    w $44#VJMEFS΋ࣅͨΑ͏ͳ࣮૷ɺ೷͍ͯΈΔͱͱͯ΋ָ͍͠☺
    ˠ"OESPJEɾαʔόʔαΠυͷ։ൃʹ΋׆͖Δ஌ݟΛಘΔ͜ͱ͕Ͱ͖Δʂ
    ,PUMJOͰϑϩϯτΤϯυͷ։ൃ͕Ͱ͖Δͷ͸ָ͍͠ʂ
    ୔ࢁͷਓ͕৮ͬͯ͘ΕΔͱخ͍͠☺

    View Slide

  108. એ఻
    גࣜձࣾ#FBS5BJMɹΤϯδχΞืूதʂ
    ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠N @@
    N
    ˢ࠷ۙ(SBEMF͕શͯ,PUMJO%4-ʹͳͬͨ

    View Slide