[Ktor x React] 整合 Ktor 與 React

前不久我寫了一系列 Ktor档泽,Ktjs坦康,KtReact 相關(guān)的內(nèi)容攻泼,用 Kotlin 來統(tǒng)一前后端也許是勢(shì)在必行的吧火架。前不久與群友糍粑大佬聊天鉴象,我們一致認(rèn)為目前 Ktor 與 React 的整合是個(gè)很麻煩的事情,當(dāng)然也是非常有必要解決的問題何鸡,于是就有了這篇纺弊。

參考了網(wǎng)上一些文章,幾乎所有的解決方案都是圍繞著 Kotlin 官方推出的 frontend 插件來進(jìn)行的骡男,關(guān)于該插件的詳情可以參考 Github 上的項(xiàng)目(點(diǎn)擊進(jìn)入)淆游,在此不多講述。個(gè)人意見是隔盛,該插件暫時(shí)還不完善犹菱,特別是對(duì)于 npm 的操作問題很大,經(jīng)常出現(xiàn)沒有權(quán)限或者其他的問題吮炕。經(jīng)過一些大佬的提醒腊脱,認(rèn)為該插件目前適合 Windows 用戶,而不適合 Mac 或 Linux 用戶龙亲。

經(jīng)過一番掙扎我放棄了這個(gè)插件陕凹,換用純 gradle 的方式來進(jìn)行整合,下面詳細(xì)講述整合的方法鳄炉。


一捆姜、創(chuàng)建 Ktor 與 KtReact 項(xiàng)目

創(chuàng)建的過程就不多說了,參考我之前的文章即可(Ktor: 點(diǎn)擊進(jìn)入迎膜,KtReact: 點(diǎn)擊進(jìn)入)泥技,最終我們得到兩個(gè)項(xiàng)目,如果你不嫌麻煩的話磕仅,在兩個(gè)工作空間分別開發(fā)也是可以的珊豹。

二、合并項(xiàng)目

新建一個(gè)目錄榕订,然后在這個(gè)目錄下建立 backend 和 frontend 目錄各一店茶,把 Ktor 項(xiàng)目的內(nèi)容拷到 backend 內(nèi),把 KtReact 項(xiàng)目拷到 frontend 內(nèi)劫恒。最終的目錄結(jié)構(gòu)如下所示:

| - KtorReact
|    | - backend
|    |    | - resources
|    |    | - src
|    |    | - build.gradle
|    | - frontend
|    |    | - node_modules
|    |    | - public
|    |    | - src
|    |    | - package.json
|    |    | - yarn.lock

這里吐槽一下簡(jiǎn)書贩幻,居然不支持 Mermaid 圖表,只能用土辦法來畫圖了两嘴。

三丛楚、改成 gradle 項(xiàng)目

雖然有很多人不太喜歡用 gradle 來管理前端項(xiàng)目,而且用 gradle 來引用 js 庫也顯得蛋疼憔辫,但是為了統(tǒng)一還是不得不用一下趣些,其實(shí)用習(xí)慣了也是“真香”。

就以上面那個(gè)目錄結(jié)構(gòu)贰您,在 KtorReact 下增加 gradle 必要的文件坏平,build.gradle拢操,gradle.propertiessettings.gradle,它們的代碼分別如下:

build.gradle

buildscript {
    repositories {
        mavenCentral()
        mavenLocal()
        jcenter()
        maven { url 'https://kotlin.bintray.com/ktor' }
        maven { url "https://plugins.gradle.org/m2/" }
        maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
        maven { url "http://dl.bintray.com/kotlin/kotlin-dev" }
        maven { url "http://dl.bintray.com/kotlinx/kotlinx" }
        maven { url "http://dl.bintray.com/kotlin/kotlin-js-wrappers" }
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "com.moowork.gradle:gradle-node-plugin:$node_plugin_version"
    }
}

repositories {
    mavenCentral()
    mavenLocal()
    jcenter()
    maven { url 'https://kotlin.bintray.com/ktor' }
    maven { url "https://plugins.gradle.org/m2/" }
    maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
    maven { url "http://dl.bintray.com/kotlin/kotlin-dev" }
    maven { url "http://dl.bintray.com/kotlinx/kotlinx" }
    maven { url "http://dl.bintray.com/kotlin/kotlin-js-wrappers" }
}

task clean(type: Delete) {
    delete new File(rootProject.rootDir, "backend/build")
    delete new File(rootProject.rootDir, "frontend/build")
    delete rootProject.buildDir
}

gradle.properties

ktor_version=1.1.3
kotlin.code.style=official
kotlin_version=1.3.21
logback_version=1.2.1
node_plugin_version=1.3.1
nodejs_version=11.10.0
yarn_version=1.13.0

settings.gradle

rootProject.name = 'KtorReact'
include ':backend', ':frontend'

到這里舶替,gradle 已經(jīng)可以為我們導(dǎo)入兩個(gè)項(xiàng)目了令境,但是這里會(huì)發(fā)生一個(gè)很大的問題,就是 KtReact 的依賴會(huì)被 gradle 覆蓋掉顾瞪,而我們知道的是展父,KtReact 原本就不需要 gradle,它是將 node 模塊變成 Ktjs 模塊并且使用這種依賴方式玲昧。所以我們有必要把 KtReact 的依賴改成 gradle 形式的栖茉。

四、修改 KtReact 依賴

還是按上面的項(xiàng)目結(jié)構(gòu)孵延,在 frontend 目錄內(nèi)添加 build.gradle 文件:

apply plugin: 'kotlin2js'
apply plugin: 'com.moowork.node'

node {
    version = "$nodejs_version"
    yarnVersion = "$yarn_version"
    download = false
    workDir = file(project.projectDir)
}

sourceSets {
    main.kotlin.srcDirs += ['src']
    main.resources.srcDirs += ['resources']
}

kotlin {
    experimental {
        coroutines "enable"
    }
}

repositories {
    mavenCentral()
    mavenLocal()
    jcenter()
    maven { url 'https://kotlin.bintray.com/ktor' }
    maven { url "https://plugins.gradle.org/m2/" }
    maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
    maven { url "http://dl.bintray.com/kotlin/kotlin-dev" }
    maven { url "http://dl.bintray.com/kotlinx/kotlinx" }
    maven { url "http://dl.bintray.com/kotlin/kotlin-js-wrappers" }
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
    compile "org.jetbrains.kotlinx:kotlinx-html-js:0.6.9"
    compile "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.2.0"
    compile "org.jetbrains.kotlinx:kotlinx-io-js:0.1.8"
    compile "org.jetbrains.kotlinx:kotlinx-coroutines-io-js:0.1.8"
    compile 'org.jetbrains:kotlin-react:16.6.0-pre.71-kotlin-1.3.31'
    compile 'org.jetbrains:kotlin-extensions:1.0.1-pre.71-kotlin-1.3.31'
    compile 'org.jetbrains:kotlin-react-dom:16.6.0-pre.71-kotlin-1.3.31'
    compile 'org.jetbrains:kotlin-react-redux:5.0.7-pre.71-kotlin-1.3.31'
}

這里使用了 node 插件吕漂,可以在 gradle 面板看到插件的具體能力:

node 插件

這樣我們就成的把 KtReact 項(xiàng)目用 gradle 管理起來了,現(xiàn)在可以使用 gradle yarn 的各種命令尘应,非常的舒服惶凝,比如說 gradle yarn_start 就可以運(yùn)行起這個(gè) KtReact 項(xiàng)目。

五犬钢、配置代理

這里可能大家會(huì)有一個(gè)疑惑苍鲜,Ktor 作為 backend 項(xiàng)目,運(yùn)行時(shí)監(jiān)聽 80 端口玷犹,而 KtReact 作為前端項(xiàng)目混滔,運(yùn)行時(shí)占用 3000 端口,這時(shí)如果 KtReact 去請(qǐng)求 Ktor 的 API歹颓,會(huì)造成跨域問題坯屿。

這個(gè)問題其實(shí)很好解決,設(shè)置代理即可巍扛,在 package.json 內(nèi)改一下就好了领跛,如下:

{
    "name": "KtorReact-frontend",
    ... ... 
    "proxy": "http://0.0.0.0:80",
    ... ... 
}

這樣一來,KtReact 的請(qǐng)求都會(huì)被代理到 80 端口了撤奸。

六吠昭、一些額外的設(shè)置

為了方便開發(fā)調(diào)試等,還需要做一些額外的設(shè)置胧瓜,比如說 KtReact 部分的構(gòu)建矢棚,運(yùn)行,以及停止服務(wù)贷痪。對(duì)于 node 插件來說幻妓,由它啟動(dòng)的 node 服務(wù)并不能被插件自身終止,會(huì)帶來一定的麻煩劫拢,比如說下一次運(yùn)行時(shí)端口已占用之類的肉津,得有個(gè)辦法去終止它。所以一些額外的 Gradle Task 是非常必要的舱沧。

task stop(type: Exec) {
    commandLine "killall","node","\n","killall","java","\n"
}

task frontBuild(type: Exec) {
    commandLine "yarn", "build"
}

frontBuild.doLast() {
    copy {
        includeEmptyDirs = true
        from new File("build")
        into "../backend/resources/web"
    }
    delete new File("build")
}

task frontRun(type: Exec) {
    commandLine "yarn", "start"
}

這里同樣也實(shí)現(xiàn)了把 KtReact 的編譯結(jié)果直接扔進(jìn) Ktor 的 resources 內(nèi)妹沙,方便做一站式發(fā)布。最后也就是發(fā)布了熟吏,寫個(gè)發(fā)布腳本然后用 gradle 調(diào)用之:

#!/bin/sh
gradle :frontend:frontBuild
gradle :backend:build
task release(type: Exec) {
    commandLine "sh", "release.sh"
}

七距糖、踩坑

目前發(fā)現(xiàn)兩個(gè)很大的坑,在 KtReact 內(nèi)牵寺,如果配置了代理悍引,則不能使用 Ktor 的 Compression 插件,否則請(qǐng)求會(huì)報(bào)錯(cuò)帽氓。

另一個(gè)坑是趣斤,如果把 Ktor 配成 https 的,代理請(qǐng)求也會(huì)出錯(cuò)黎休,這個(gè)時(shí)候需要在 package.json 內(nèi)加入 "secure":false浓领。


最后說一句橡淆,我正在編寫一個(gè)通用的 Ktor + KtReact 開發(fā)模板缘揪,應(yīng)該不會(huì)太久就能正式面世了棘利,到時(shí)候再寫一個(gè)簡(jiǎn)單的使用手冊(cè)吧随橘。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疲恢,一起剝皮案震驚了整個(gè)濱河市剪验,隨后出現(xiàn)的幾起案子惊豺,更是在濱河造成了極大的恐慌馒过,老刑警劉巖署照,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件座菠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡藤树,警方通過查閱死者的電腦和手機(jī)浴滴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岁钓,“玉大人升略,你說我怎么就攤上這事÷畔蓿” “怎么了品嚣?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)钧大。 經(jīng)常有香客問我翰撑,道長(zhǎng),這世上最難降的妖魔是什么啊央? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任眶诈,我火速辦了婚禮涨醋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逝撬。我一直安慰自己浴骂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布宪潮。 她就那樣靜靜地躺著溯警,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狡相。 梳的紋絲不亂的頭發(fā)上梯轻,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音尽棕,去河邊找鬼喳挑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛萄金,可吹牛的內(nèi)容都是我干的蟀悦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼氧敢,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼日戈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起孙乖,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤浙炼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后唯袄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弯屈,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年恋拷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了资厉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蔬顾,死狀恐怖宴偿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诀豁,我是刑警寧澤窄刘,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站舷胜,受9級(jí)特大地震影響娩践,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一翻伺、第九天 我趴在偏房一處隱蔽的房頂上張望材泄。 院中可真熱鬧,春花似錦穆趴、人聲如沸脸爱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至空入,卻和暖如春络它,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背歪赢。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工化戳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人埋凯。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓点楼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親白对。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掠廓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容