Pro Yearly is on sale from $80 to $50! »

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

7c3b3366947123ba6772698b09edf4e2?s=47 subroh_0508
August 24, 2019

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

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

7c3b3366947123ba6772698b09edf4e2?s=128

subroh_0508

August 24, 2019
Tweet

Transcript

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

  2. "CPVU.F w ʹ͜͠Γ͞ͿΖʙ ຊ໊ࡔ্੖৴  w ౦ژ౎ɾҏ౾େౡग़਎ w גࣜձࣾ#FBS5BJM w

    "OESPJEΤϯδχΞ ,PUMJO+BWB ˑ w 8FCΤϯδχΞ 3BJMT3FBDU  w झຯͰ,PUMJO+4
  3. "HFOEB ,PUMJO+4ͷ֓ཁ w ྺ࢙ɺࣄྫ঺հCZTVCSPI@ ,PUMJO+4 3FBDUKTͰʮ)FMMP 8PSMEʯ w OQN ,PUMJO+4

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

  5. ,PUMJO+4ͷ֓ཁ

  6. rެࣜϦϑΝϨϯεݪจ<IUUQTLPUMJOMBOHPSHEPDTSFGFSFODFKTPWFSWJFXIUNM> l,PUMJOQSPWJEFTUIFBCJMJUZUPUBSHFU+BWB4DSJQU*UEPFTTPCZ USBOTQJMJOH,PUMJOUP+BWB4DSJQUz ,PUMJO+4ͷ֓ཁ ,PUMJO+4ͱ͸ʜ ,PUMJOΛ+4ͷίʔυ΁τϥϯεύΠϧ͢Δػೳɺ͓ΑͼϑϨʔϜϫʔΫ܈

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

    w Ϟδϡʔϧόϯυϥʔ 8FCQBDL  w ෳ਺ͷ+4ϞδϡʔϧΛͭʹ·ͱΊɺ)5.-Ͱѻ͍΍͍͢ܗʹ͢Δ w Ϟδϡʔϧؒͷґଘؔ܎ղܾɺNJOJGZɺίʔυɾϦιʔεͷม׵ ྫ54ˠ+4ɺ4BTTˠ$44
  8. ,PUMJO+4ͷ֓ཁ w +4ίʔυʹม׵Մೳͳ,PUMJOඪ४ϥΠϒϥϦ w ,PUMJOˠ+4΁ͷม׵Λ࣮ߦ͢Δ(SBEMFϓϥάΠϯ w OQN8FCQBDLͷίϚϯυΛ࣮ߦ͢Δ(SBEMFϓϥάΠϯ ʴIUNMDTTͷ%4-ϥΠϒϥϦɺ֤छ+4 ϥούʔ ϥΠϒϥϦ

    ,PUMJO+4͕ఏڙ͢ΔϞϊ ,PUMJO͔Β+4΁ͷม׵ɺOQNʹΑΔόʔδϣϯ؅ཧɺ8FCQBDLʹΑΔόϯυϧ શͯΛ,PUMJOͷੈքͰ׬݁ͤ͞ɺϑϩϯτΤϯυ։ൃΛ࣮ݱ͢Δʂ
  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ͷઃఆ߲໨௥Ճɺόάमਖ਼
  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ͷҰ෦ͱͯ͠ ֤छϓϥάΠϯ͕·ͱ·ΔྲྀΕ
  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ͷྺ࢙ ͜ͷ࣌ظͷ৘ใ͕݁ߏଟ͍ ࠓޙݹ͘ͳΔͷͰ஫ҙʂ
  12. ݁ہͲΜͳϞϊ͕࡞ΕΔͷ w ࠷ऴతͳ੒Ռ෺ˠ+4ϑΝΠϧɺ8FCQBDLͷόϯυϧϑΝΠϧ w +4Ͱ࡞ΕΔϞϊ͸શͯ,PUMJO+4Ͱ࡞Δ͜ͱ͕Մೳʂ ཧ࿦্  w ࣄྫ঺հ w

    ࣗ෼ͷϙʔτϑΥϦΦαΠτ w খௗ͞Μͷࣗशࣨ ϒϥ΢β্Ͱ,PUMJOΛֶ΂ΔֶशαΠτ
  13. ࣄྫ  ϙʔτϑΥϦΦαΠτ (JUIVCTVCSPIQPSUGPMJP ࢖༻ϥΠϒϥϦɾ"1* w ,PUMJO+4 W  w

    3FBDUKT w .BUFSJBM6* ˠ'JSFCBTF)PTUJOHͰϗεςΟϯά ɹ-JOLTVCSPINF w (PPHMF.BQ"1* w SFBDUTXJQFBCMFWJFXT
  14. ࣄྫ  খௗ͞Μͷࣗशࣨ (JUIVCTVCSPIPUPOBTIJLPUMJOEFW ػೳ w ϒϥ΢β্Ͱ,PUMJO,PBOTͷ՝୊͕Ͱ͖Δ w ՝୊ͷΫϦΞɺίϯύΠϧΤϥʔ౳ʹ ɹ߹ΘͤͯϨεϙϯεΛฦ͢

    ˠখௗ͞ΜͱϖΞϓϩ͠ͳ͕Β,PUMJOΛֶश w ,PUMJOͷ3&1-LPUMJOQMBZHSPVOE w ϖʔδભҠ3FBDI3PVUFS খௗ͞Μ
  15. None
  16. ࣄྫ  খௗ͞Μͷࣗशࣨ (JUIVCTVCSPIPUPOBTIJLPUMJOEFW ࢓༷ϥΠϒϥϦ w ,PUMJO+4 W  w

    3FBDUKT w .BUFSJBM6* ˠϧʔςΟϯάϥΠϒϥϦΛ࢖ͬͨ41" ɹ খௗ͞Μ w 3FBDI3PVUFS w LPUMJOQMBZHSPVOE
  17. ,PUMJO+4ɺҙ֎ͱͰ͖Δࢠ Կ͔࡞Γͨ͘ͳ͖ͬͯͨ

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

  19. ,PUMJO+4Ͱ3FBDUΞϓϦΛ࡞Δ  ,PUMJOͰॻ͍ͨίʔυΛ௚઀OQN 8FCQBDLͰϏϧυ w OQNɺ8FCQBDLͷίϚϯυΛ௚઀࣮ߦ +4ͷੈքʹ,PUMJOΛಋೖ͢Δ  w ϝϦοτ+4Ͱॻ͔ΕͨطଘͷϓϩδΣΫτʹಋೖ͠΍͍͢

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

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

     ˠ,PUMJO+4ʹΑΔ3FBDUΞϓϦͷ࣮૷ΠϝʔδΛ௫Έ΍͍͢
  22.  ,PUMJO+4 OQN w ϞδϡʔϧͷΠϯετʔϧɾϓϩδΣΫτੜ੒ɾ࣮ߦ # インストール $ npm install

    -g create-react-kotlin-app # プロジェクト生成 $ create-react-kotlin-app my-app # 実行 $ cd my-app/ $ npm start
  23. w MPDBMIPTUʹΞΫηε͢Δͱʜ  ,PUMJO+4 OQN

  24. σΟϨΫτϦߏ੒

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

  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", … }
  27. ,PUMJO+4ͷίʔυ DTT ϦιʔεϑΝΠϧ

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

  29. package index import … fun main(args: Array<String>) { requireAll(require.context("src", true,

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

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

    js("/\\.css$/"))) render(document.getElementById("root")) { child(App::class) { } } } "QQίϯϙʔωϯτΛSFOEFS
  32. package app import … class App : RComponent<RProps, RState>() {

    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 } } } }
  33. package app import … class App : RComponent<RProps, RState>() {

    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ఆٛ
  34. package app import … class App : RComponent<RProps, RState>() {

    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-Ͱهड़
  35. package app import … class App : RComponent<RProps, RState>() {

    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
  36. package app import … class App : RComponent<RProps, RState>() {

    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
  37. package app import … class App : RComponent<RProps, RState>() {

    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 } } } } ճస͢Δϩΰͷදࣔίϯϙʔωϯτ
  38. package app import … class App : RComponent<RProps, RState>() {

    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 } } } } ܦա࣌ؒදࣔͷίϯϙʔωϯτ
  39. interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(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." } }
  40. interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(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 ˠ਌ίϯϙʔωϯτ͔Β ɹड͚औΔ஋ͷఆٛ
  41. interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(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ίϯϙʔωϯτͷঢ়ଶ
  42. interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(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ؔ਺Ͱܦա࣌ؒͷॳظԽ
  43. interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(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ϝιουͷݺͼग़͠
  44. interface TickerProps : RProps { var startFrom: Int } interface

    TickerState : RState { var secondsElapsed: Int } class Ticker(props: TickerProps) : RComponent<TickerProps, TickerState>(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." } } ܦա࣌ؒͷදࣔ
  45. ίʔυൺֱ class App extends React.Component { render() { return (

    <div className='App-header'> <Logo/> <h2>Welcome to React with Kotlin</h2> <p className='App-intro'> To get started, edit <code>app/App.kt</code> and save to reload. </p> <p className='App-ticker'> <Ticker startFrom={ 0 }/> </p> </div> ); } } class App : RComponent<RProps, RState>() { 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
  46. w +4ϥΠϒϥϦΛ௥Ճ͍ͨ͠ʂ w npm install hoge --save w ௥Ճͨ͠+4ϥΠϒϥϦΛ,PUMJOͰѻ͍͍ͨʂ w

    ޙड़ w 8FCQBDLͷઃఆΛΧελϚΠζ͍ͨ͠ʂ w npm run eject ͰઃఆϑΝΠϧΛग़ྗՄ  ,PUMJO+4 OQN
  47. *OUFMMJ+*%&"6MUJNBUFͰʮ)FMMP 8PSMEʯ·Ͱ  ,PUMJO+4 (SBEMF $SFBUF/FX1SPKFDU ˠ(SBEMF ˠ,PUMJO+4GPSCSPXTFS ˠ/FYU

  48.  ,PUMJO+4 (SBEMF w (SPVQ*E w "SUJGBDU*E w 1SPKFDUOBNF w

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

    JOEFYIUNMͷ࡞੒ 4BNQMF3FQPTVCSPILPUMJOSFBDUTBNQMF
  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/' } }
  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" } } }
  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ͷઃఆΛهड़
  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ʹࢦఆ
  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 ˠόϯυϧޙͷϑΝΠϧ໊
  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") } } }
  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ͷඪ४ϥΠϒϥϦ
  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ͷϥούʔϥΠϒϥϦ
  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  Ͱࢦఆ
  59. ,PUMJO+4ͷίʔσΟϯά  ,PUMJO+4 (SBEMF // main.kt fun main() { render(document.getElementById("root"))

    { child(App::class) { } } } // App.kt class App : RComponent<RProps, RState>() { override fun RBuilder.render() { div { +"Hello, World!" } } }
  60. JOEFYIUNMͷ࡞੒  ,PUMJO+4 (SBEMF <!DOCTYPE html> <html lang="en"> <head> <meta

    charset="UTF-8"> <title>Title</title> </head> <body> <div id="root"></div> <script src="kotlin-react-sample.js"></script> </body> </html> ˡIUNMϑΝΠϧΛ SFTPVSDFTҎԼʹ഑ஔ
  61.  ,PUMJO+4 (SBEMF ࣮ߦʂ $ ./gradlew run ,PUMJOͷ8FCΞϓϦɺ׬੒ʂ

  62. ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ interface AppState : RState { var count: Int

    } class App : RComponent<RProps, AppState>() { 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!" } } }
  63. interface AppState : RState { var count: Int } class

    App : RComponent<RProps, AppState>() { 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ͷܕఆٛ ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ
  64. interface AppState : RState { var count: Int } class

    App : RComponent<RProps, AppState>() { 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ͷॳظԽ ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ
  65. interface AppState : RState { var count: Int } class

    App : RComponent<RProps, AppState>() { 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ϝιουͰΧ΢ϯτΛ૿΍͢ ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ
  66. interface AppState : RState { var count: Int } class

    App : RComponent<RProps, AppState>() { 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\^ ಈ͖Λ͚͍ͭͨ ྫΧ΢ϯλʔ
  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!" } }
  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 ϝιου͕ੜ͑Δ
  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ͷػೳ
  70. ؆୯ͳ8FCϖʔδ͸࡞Εͨ ΋ͬͱϦονͳ͜ͱ͍ͨ͠

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

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

    ,PUMJO+4ಠࣗͷػೳΛۦ࢖͠ɺ࣮ݱ ϋϚΓͲ͜Ζʂ
  73. +4੡ϥΠϒϥϦͷಋೖ ґଘؔ܎௥Ճ ྫSFBDUTXJQFBCMFWJFXT  w OQNͷ৔߹ npm install react-swipeable-views --save

    w (SBEMFͷ৔߹CVJMEHSBEMFʹ௥ه sourceSets { main { dependencies { ... implementation npm("react-swipeable-views") ... } } }
  74. +4੡ϥΠϒϥϦͷಋೖ !+T.PEVMFΞϊςʔγϣϯ w +4ϥΠϒϥϦΛ,PUMJOͷੈքʹΠϯϙʔτ͢ΔΞϊςʔγϣϯ @JsModule("react-swipeable-views") private external val reactSwipeableViewModule: dynamic

    fun test() { reactSwipeableViewModule.default.onChangeIndex = { ... } }
  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ͷੈքͰ ҰԠ͸ ѻ͑Δʂ
  76. +4੡ϥΠϒϥϦͷಋೖ !+T.PEVMFΞϊςʔγϣϯ w +4ϥΠϒϥϦΛ,PUMJOͷੈքʹΠϯϙʔτ͢ΔΞϊςʔγϣϯ @JsModule("react-swipeable-views") private external val reactSwipeableViewModule: dynamic

    fun test() { reactSwipeableViewModule.default.onChangeIndex = { ... } } EZOBNJDܕʜʁ
  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
  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ͷΦϒδΣΫτΛੜ੒
  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 ଘࡏ͢ΔΩʔͷ஋Λऔͬͯ͘Δ
  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ʹ͍ۙจ๏Ͱ୅ೖͰ͖ͯ͠·͏
  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! ͜͏ͳͬͯཉ͍͠
  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੡ϥΠϒϥϦͷಋೖ
  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<ReactSwipeableProps> get() = reactSwipeableViewModule.default as RClass<ReactSwipeableProps>
  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<ReactSwipeableProps> get() = reactSwipeableViewModule.default as RClass<ReactSwipeableProps> FYUFSOBMJOUFSGBDFΛఆٛ ˠϓϩύςΟʮPO$IBOHF*OEFYʯ͕ ɹଘࡏ͢Δ͜ͱΛڭ͑ࠐ·ͤΔ
  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<ReactSwipeableProps> get() = reactSwipeableViewModule.default as RClass<ReactSwipeableProps> Πϯϙʔτͨ͠ϞδϡʔϧΛ3$MBTT31SPQTʹΩϟετ
  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 } } } }
  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ͷ֦ுؔ਺ͱͯ͠ ηολʔΛఆٛ
  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͕ੜ͑Δ ˠ֦ுؔ਺ݺͼग़͠
  89. ܕఆٛɾؔ਺ఆٛʹ͔͔Δ࿑ྗ w ϥΠϒϥϦʹ͖ͭɺଟͯ͘΋ߦఔ౓ w ඞཁͳྔ͚ͩఆٛ͢Ε͹0, ͨͩ͠ w ڊେͳϥΠϒϥϦͷܕఆٛɾؔ਺ఆٛ͸ਖ਼௚͔ͳΓ͠ΜͲ͍ w 6*ϑϨʔϜϫʔΫ͸ಛʹ

    τϥΠΞϯυΤϥʔͷαΠΫϧ͕ճͮ͠Β͍  w ྫ3FBDU#PPUTUSBQɺ.BUFSJBM6*FUD +4੡ϥΠϒϥϦͷಋೖ
  90. ,PUMJO.BUFSJBM6*ͷ঺հ w (JUIVCTVCSPILPUMJONBUFSJBMVJ w ʮ.BUFSJBM6*ʯͷ,PUMJO+4ϥούʔϥΠϒϥϦ .BUFSJBM%FTJHOͳ6*࡞Γ͍ͨʂ ˠ͜ΕΛ࢖͑͹ղܾʂ ɹ,PUMJO+4΁ͷೖ໳͸͔͜͜Βʂ ɹϥΠϒϥϦ੡࡞ͷࢀߟʹ΋ʂ

  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*ͷॳظԽίʔυ
  92. ·ͱΊ ,PUMJO+4ͷ֓ཁɾΞοϓσʔτͷྺ࢙ʹ͍ͭͯղઆ w ౰ॳಠཱͨ͠ϓϥάΠϯ w ݱࡏ,PUMJO.11ͷҰཌྷΛ୲͏ཱͪҐஔ ,PUMJO+4Λ࢖ͬͨ8FCΞϓϦ੡࡞ͷϋϯζΦϯ w OQNͱ(SBEMFɺͲͪΒͰ΋Մ w

    EZOBNJDͱFYUFSOBM͕࢖͍͜ͳͤΕ͹ɺ+4੡ϥΠϒϥϦΛࣗ༝ʹѻ͑Δ w ࠷ऴखஈͱͯ͠KTϝιου
  93. ·ͱΊ ,PUMJO+4ͷ͜Ε͔Β w CBDLFOEͱGSPOUFOE͕ͭͷ,PUMJOϓϩδΣΫτʹ·ͱ·Δʂ w EBUBDMBTTͷڞ༗ɺ(SBEMFͰͷϏϧυɺ%4-ͰͷIUNMDTTهड़ ຊ൪ಋೖ΁ͷน w ѹ౗తͳࣄྫɾ৘ใෆ଍ʂɹϥΠϒϥϦ΋·ͩ·ͩগͳ͍ʂ w

    ؆୯ͳϞϊͰ΋࡞ͬͯެ։ˠ஌ݟͷ஝ੵɺ,PUMJO+4ͷલਐͷҰॿ ,PUMJOͰϑϩϯτΤϯυͷ։ൃ͕Ͱ͖Δͷ͸ָ͍͠ʂ ୔ࢁͷਓ͕৮ͬͯ͘ΕΔͱخ͍͠☺
  94. ͓·͚ύʔτ ,PUMJO+4ίʔυ୳ࡧ

  95. ίʔυϦʔσΟϯά LPUMJOSFBDU ʮ)FMMP 8PSMEʯΛදࣔ͢ΔαϯϓϧΞϓϦ class App : RComponent<RProps, RState>() {

    override fun RBuilder.render() { div { +"Hello, World!" } } } 3$PNQPOFOUʁ 3#VJMEFSʁ SFOEFSϝιου͸ ͳ֦ͥுؔ਺ʁ
  96. ίʔυϦʔσΟϯά LPUMJOSFBDU LPUMJOSFBDUͷසग़Ϋϥε w 3$MBTT ˠ3FBDU$MBTTͱରԠɺίϯϙʔωϯτͷܕఆٛ w 3$PNQPOFOU3FBDU&MFNFOU ˠ$PNQPOFOU3FBDU&MFNFOUͱରԠɺίϯϙʔωϯτͷΠϯελϯε w

    3#VJMEFS ˠ%0.ߏஙɾ$PNQPOFOUͷੜ੒ʹ੹຿Λ࣋ͭLPUMJOSFBDUಠࣗͷΫϥε
  97. ίʔυϦʔσΟϯά LPUMJOSFBDU @ReactDsl open class RBuilder { val childList =

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

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

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

    child(element: ReactElement): ReactElement { childList.add(element) return element } operator fun String.unaryPlus() { childList.add(this) } fun <P : RProps> child(type: Any, props: P, children: List<Any>) = child(createElement(type, props, *children.toTypedArray())) fun <P : RProps> child(type: Any, props: P, handler: RHandler<P>): ReactElement { val children = with(RElementBuilder(props)) { handler() childList } return child(type, props, children) } operator fun <P : RProps> RClass<P>.invoke(handler: RHandler<P>) = child(this, jsObject {}, handler) operator fun <T> RProvider<T>.invoke(value: T, handler: RHandler<RProviderProps<T>>) = child(this, jsObject { this.value = value }, handler) operator fun <T> RConsumer<T>.invoke(handler: RBuilder.(T) -> Unit) = child(this, jsObject<RConsumerProps<T>> { this.children = { value -> buildElements { handler(value) } } }) {} fun <P : RProps> RClass<P>.node( props: P, children: List<Any> = emptyList() ) = child(this, clone(props), children) fun <P : RProps, C : Component<P, *>> child(klazz: KClass<C>, handler: RHandler<P>): ReactElement { val rClass = klazz.js as RClass<P> return rClass(handler) } inline fun <P : RProps, reified C : Component<P, *>> child(noinline handler: RHandler<P>) = child(C::class, handler) fun <P : RProps, C : Component<P, *>> node( klazz: KClass<C>, props: P, children: List<Any> = emptyList() ): ReactElement { val rClass = klazz.js as RClass<P> return rClass.node(props, children) } inline fun <P : RProps, reified C : Component<P, *>> node(props: P, children: List<Any> = 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<Any>): ReactElement fun child(type: Any, props: P, handler: RHandler<P>): ReactElement fun child(klazz: KClass<C>, handler: RHandler<P>): ReactElement inline fun child(noinline handler: RHandler<P>): ReactElement
  101. ίʔυϦʔσΟϯά LPUMJOSFBDU DSFBUF&MFNFOUϝιου w τοϓϨϕϧؔ਺ɺ3FBDUKTͷ3FBDUDSFBUF&MFNFOUͱରԠ w 3#VJMEFSDIJME-JTU3FBDU&MFNFOUʹม׵ w SFOEFSϝιουͰ࠷ऴతʹݺ͹ΕΔ external

    fun <P : RProps> createElement( type: Any, props: P, vararg child: Any? ): ReactElement
  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<DIV>.() -> Unit ): ReactElement = tag(block) { DIV(attributesMapOf("class", classes), it) } 3%0.#VJMEFS ˠ3#VJMEFSͷࢠΫϥε EJW\ TQBO\ B\^ ^ ^
  103. ίʔυϦʔσΟϯά LPUMJOSFBDU UBHϝιου w )UNM5BH༻ϝιου͔Β࣮ࡍʹݺͼग़͞ΕΔϝιου w 3#VJMEFSDIJME  ϝιουΛ࣮ߦ w

    ,PUMJO+4Ͱಠࣗ)5.-λάΛѻ͍͍ͨʂˠUBHϝιουͰΑ͠ͳʹ inline fun <T : Tag> RBuilder.tag( block: RDOMBuilder<T>.() -> Unit, noinline factory: (TagConsumer<Unit>) -> T ): ReactElement = child(RDOMBuilder(factory).apply { block() }.create())
  104. ίʔυϦʔσΟϯά LPUMJOSFBDU ,PUMJO+4ͷίʔσΟϯά w SFOEFSϝιου͕3#VJMEFSͷ֦ுؔ਺ͱͯ͠ఆٛ ˠSFOEFSϝιουͷείʔϓ಺͸ɺUIJT͕3#VJMEFSͷΠϯελϯεͱͳΔ ˠ,PUMJOΒ͘͠)5.-ͷݟͨ໨ΛଛͳΘͳ͍ܗͰ)5.-Λॻ͚Δʂ class App :

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

    RComponent<RProps, RState>() { override fun RBuilder.render() { div { +"Hello, World!" } } } EJW\DIJME )FMMP 8PSME ^ͱಉ͡ Ͱ΋ݟͨ͜ͱͳ͍ॻ͖ํʜ
  106. ίʔυϦʔσΟϯά LPUMJOSFBDU 3#VJMEFSΫϥε w 4USJOHVOBSZ1MVT ϝιου w ԋࢉࢠΦʔόʔϩʔυ ˠจࣈྻͷ୯߲ԋࢉ IPHF

    Λ্ॻ͖ w 3#VJMEFSΫϥεͷ಺෦Ͱએݴ ɹˠUIJT͕3#VJMEFSͷ࣌ͷΈ༗ޮ ɹɹར༻ൣғΛݶఆ͠ɺ෭࡞༻Λ཈͑Δʂ @ReactDsl open class RBuilder { val childList = mutableListOf<Any>() fun child(element: ReactElement): ReactElement { childList.add(element) return element } operator fun String.unaryPlus() { childList.add(this) } fun <P : RProps> child(type: Any, props: P, children: List<Any>) = child(createElement(type, props, *children.toTypedArray())) fun <P : RProps> child(type: Any, props: P, handler: RHandler<P>): ReactElement { val children = with(RElementBuilder(props)) { handler() childList } return child(type, props, children) } operator fun <P : RProps> RClass<P>.invoke(handler: RHandler<P>) = child(this, jsObject {}, handler) operator fun <T> RProvider<T>.invoke(value: T, handler: RHandler<RProviderProps<T>>) = child(this, jsObject { this.value = value }, handler) operator fun <T> RConsumer<T>.invoke(handler: RBuilder.(T) -> Unit) = child(this, jsObject<RConsumerProps<T>> { this.children = { value -> buildElements { handler(value) } } }) {} fun <P : RProps> RClass<P>.node( props: P, children: List<Any> = emptyList() ) = child(this, clone(props), children) fun <P : RProps, C : Component<P, *>> child(klazz: KClass<C>, handler: RHandler<P>): ReactElement { val rClass = klazz.js as RClass<P> return rClass(handler) } inline fun <P : RProps, reified C : Component<P, *>> child(noinline handler: RHandler<P>) = child(C::class, handler) fun <P : RProps, C : Component<P, *>> node( klazz: KClass<C>, props: P, children: List<Any> = emptyList() ): ReactElement { val rClass = klazz.js as RClass<P> return rClass.node(props, children) } inline fun <P : RProps, reified C : Component<P, *>> node(props: P, children: List<Any> = emptyList()) = node(C::class, props, children) fun RProps.children() { childList.addAll(Children.toArray(children)) } } operator fun String.unaryPlus() { childList.add(this) }
  107. ίʔυ୳ࡧ·ͱΊ ,PUMJO+4ͱϥούʔϥΠϒϥϦͷ࣮૷ w ,PUMJOͷݴޠ࢓༷͕ϑϧ׆༻ʂɹݴޠ࢓༷ͷ্खͳ࢖͍ํΛֶ΂Δʂ w ֦ுؔ਺ɾϥϜμࣜɾԋࢉࢠΦʔόʔϩʔυFUD w $44#VJMEFS΋ࣅͨΑ͏ͳ࣮૷ɺ೷͍ͯΈΔͱͱͯ΋ָ͍͠☺ ˠ"OESPJEɾαʔόʔαΠυͷ։ൃʹ΋׆͖Δ஌ݟΛಘΔ͜ͱ͕Ͱ͖Δʂ ,PUMJOͰϑϩϯτΤϯυͷ։ൃ͕Ͱ͖Δͷ͸ָ͍͠ʂ

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