前不久我寫了一系列 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.properties
和 settings.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 面板看到插件的具體能力:
這樣我們就成的把 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è)吧随橘。