当前位置: 首页 > news >正文

做视频直播类型的网站如何开发网站

做视频直播类型的网站,如何开发网站,产品目录网站模板,离线wordpress首发于Enaium的个人博客 前言 本文将介绍如何使用 Kotlin 全栈技术栈KtorKotlin/JS来构建一个简单的全栈应用。 准备工作 创建项目 首先我们需要创建一个Kotlin项目,之后继续在其中新建两个子项目,一个是Kotlin/JS项目,另一个是Ktor项目。…

首发于Enaium的个人博客


前言

本文将介绍如何使用 Kotlin 全栈技术栈Ktor+Kotlin/JS来构建一个简单的全栈应用。

准备工作

创建项目

首先我们需要创建一个Kotlin项目,之后继续在其中新建两个子项目,一个是Kotlin/JS项目,另一个是Ktor项目。

添加依赖和插件

这里我使用了Gradlecatalog,在项目中的gradle目录下创建一个libs.versions.toml文件,用于管理项目中的依赖版本。

[versions]
jimmer = "0.0.9"
kotlin = "1.9.23"
ktor = "2.3.9"
ksp = "1.9.23-1.0.20"
coroutines = "1.8.0"
serialization = "1.6.3"
wrappers = "1.0.0-pre.729"
logback = "1.5.3"
postgresql = "42.7.3"
hikari = "5.1.0"
koin = "3.5.6"[libraries]
ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor" }
ktor-server-cors = { module = "io.ktor:ktor-server-cors", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
ktor-serialization-jsackson = { module = "io.ktor:ktor-serialization-jackson", version.ref = "ktor" }
ktor-server-config-yaml = { module = "io.ktor:ktor-server-config-yaml", version.ref = "ktor" }
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
kotlin-wrappers = { module = "org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom", version.ref = "wrappers" }
kotlin-wrappers-react = { module = "org.jetbrains.kotlin-wrappers:kotlin-react" }
kotlin-wrappers-react-dom = { module = "org.jetbrains.kotlin-wrappers:kotlin-react-dom" }
kotlin-wrappers-emotion = { module = "org.jetbrains.kotlin-wrappers:kotlin-emotion" }
postgresql = { module = "org.postgresql:postgresql", version.ref = "postgresql" }
hikari = { module = "com.zaxxer:HikariCP", version.ref = "hikari" }
koin = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }[bundles]
api = ['ktor-server-core', 'ktor-server-netty', 'ktor-server-cors', 'ktor-server-content-negotiation', 'ktor-serialization-jsackson', 'ktor-server-config-yaml', 'logback', 'postgresql', 'hikari', 'koin']
app = ['kotlinx-coroutines-core', 'kotlinx-serialization-json', 'kotlin-wrappers-react', 'kotlin-wrappers-react-dom', 'kotlin-wrappers-emotion'][plugins]
jimmer = { id = "cn.enaium.jimmer.gradle", version.ref = "jimmer" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

之后我们分别在前端和后端项目中的build.gradle.kts文件中引入这些依赖和插件。

后端
plugins {alias(libs.plugins.kotlin.jvm)alias(libs.plugins.ktor)alias(libs.plugins.ksp)alias(libs.plugins.jimmer)application
}group = "cn.enaium"
version = "1.0.0"application {mainClass = "cn.enaium.TodoKt"applicationDefaultJvmArgs = listOf("-Dio.ktor.development=${extra["development"] ?: "false"}")
}dependencies {implementation(libs.bundles.api)
}

这里有一个配置,添加到gradle.properties文件中。

development=true
前端
plugins {alias(libs.plugins.kotlin.multiplatform)alias(libs.plugins.kotlin.plugin.serialization)
}kotlin {js {browser {commonWebpackConfig {cssSupport {enabled.set(true)}}}binaries.executable()}sourceSets {val jsMain by getting {dependencies {implementation(project.dependencies.enforcedPlatform(libs.kotlin.wrappers))implementation(libs.bundles.app)}}}
}

这里需要将前端项目的src/main改为src/jsMain

最后进入到根项目的settings.gradle.kts文件中添加以下代码。

pluginManagement {repositories {maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")google()gradlePluginPortal()mavenCentral()}
}dependencyResolutionManagement {repositories {google()mavenCentral()maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")}
}

还有gradle.build.kts文件中只保留以下代码。

plugins {alias(libs.plugins.kotlin.jvm) apply falsealias(libs.plugins.kotlin.multiplatform) apply false
}

好了,现在我们的项目已经准备好了。

编写代码

后端

首先创建配置文件src/main/resources/application.yml

ktor:deployment:port: 8080application:modules:- cn.enaium.TodoKt.module
jdbc:driver: 'org.postgresql.Driver'url: 'jdbc:postgresql://localhost:5432/postgres?currentSchema=todo'username: 'postgres'password: 'postgres'

之后创建logback配置文件src/main/resources/logback.xml

<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="trace"><appender-ref ref="STDOUT"/></root><logger name="org.eclipse.jetty" level="INFO"/><logger name="io.netty" level="INFO"/>
</configuration>

还有创建数据库。

drop schema if exists todo cascade;
create schema todo;drop table if exists todo.task;
create table todo.task
(id         uuid primary key,name       text not null,start_time timestamp default now(),end_time   timestamp
)

之后创建一个主类cn.enaium.Todo

fun main(args: Array<String>) = EngineMain.main(args)

之后编写一个扩展函数cn.enaium.Todo.module

fun Application.module() {}

安装一些插件

Koin
install(Koin) {modules(module {single<ApplicationEnvironment> { environment }})
}
CORS
install(CORS) {allowMethod(HttpMethod.Options)allowMethod(HttpMethod.Post)allowMethod(HttpMethod.Get)allowHeader(HttpHeaders.AccessControlAllowOrigin)allowHeader(HttpHeaders.ContentType)anyHost()
}
Jackson
install(ContentNegotiation) {jackson {registerModules(ImmutableModule())}
}
Jimmer

接下来配置一下Jimmer

fun sql(environment: ApplicationEnvironment): KSqlClient {return newKSqlClient {setConnectionManager {HikariPool(HikariConfig().apply {driverClassName = environment.config.property("jdbc.driver").getString()jdbcUrl = environment.config.property("jdbc.url").getString()username = environment.config.property("jdbc.username").getString()password = environment.config.property("jdbc.password").getString()maximumPoolSize = 10connectionTimeout = 30000}).connection.use {proceed(it)}}setDialect(PostgresDialect())}
}

之后添加到Koin中。

single<KSqlClient> { sql(get()) }

编写一个Task实体类。

package cn.enaium.entityimport org.babyfish.jimmer.sql.Entity
import org.babyfish.jimmer.sql.GeneratedValue
import org.babyfish.jimmer.sql.Id
import org.babyfish.jimmer.sql.Table
import org.babyfish.jimmer.sql.meta.UUIDIdGenerator
import java.util.*/*** @author Enaium*/
@Entity
@Table(name = "task")
interface Task {@Id@GeneratedValue(generatorType = UUIDIdGenerator::class)val id: UUIDval name: Stringval startTime: Dateval endTime: Date?
}

接下来就可以编写Service了。

package cn.enaium.serviceimport cn.enaium.entity.Task
import cn.enaium.entity.endTime
import cn.enaium.entity.startTime
import org.babyfish.jimmer.sql.kt.KSqlClient
import org.babyfish.jimmer.sql.kt.ast.expression.isNotNull/*** @author Enaium*/
class TodoServe(private val sql: KSqlClient) {fun getTasks(): List<Task> {return sql.createQuery(Task::class) {orderBy(table.endTime.isNotNull(), table.startTime)select(table)}.execute()}fun saveTask(task: Task) {sql.save(task)}
}

这里我们添加两个方法getTaskssaveTaskgetTasks用于获取所有任务并按照创建时间和是否完成排序,saveTask用于保存任务,之后还是添加到Koin中。

single<TodoServe> { TodoServe(get()) }

之后我们在module添加路由。

val todoServe by inject<TodoServe>()routing {get("/task") {call.respond(todoServe.getTasks())}post("/task") {todoServe.saveTask(call.receive())call.response.status(HttpStatusCode.OK)}
}

前端

首先在src/jsMain/resources/index.html中添加以下代码,这里需要注意的是app.js,这个文件名称需要和前端的项目名称一致。

<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><title>Hello, Kotlin/JS!</title>
</head>
<body>
<div id="root"></div>
<script src="app.js"></script>
</body>
</html>

之后写一个main函数。

import react.dom.client.createRoot
import web.dom.document/*** @author Enaium*/
fun main() {val container = document.getElementById("root") ?: error("Couldn't find root container!")createRoot(container).render(App.create())
}val App = FC {}

然后就可以编写组件了。

首先需要创建两个data类,一个是Task,另一个是TaskInputTask用于展示任务,TaskInput用于请求。

@Serializable
data class Task(val id: String, var name: String, val startTime: Long, val endTime: Long?) {fun copy(name: String = this.name, startTime: Long = this.startTime, endTime: Long? = this.endTime) =Task(id, name, startTime, endTime)fun toInput() = TaskInput(id, name, startTime, endTime)
}@Serializable
data class TaskInput(val id: String? = null,val name: String? = null,val startTime: Long? = null,val endTime: Long? = null
)

之后编写请求函数,使用fetch发送请求。

val coroutine = CoroutineScope(window.asCoroutineDispatcher())suspend fun fetchTasks(): List<Task> {window.fetch("http://localhost:8080/task").await().let {if (it.status != 200.toShort()) {throw Exception("Failed to fetch")}return Json.decodeFromDynamic<List<Task>>(it.json().await())}
}suspend fun saveTask(task: TaskInput) {window.fetch("http://localhost:8080/task",RequestInit(method = "POST",body = Json.encodeToString(TaskInput.serializer(), task),headers = json("Content-Type" to "application/json"))).await().let {if (it.status != 200.toShort()) {throw Exception("Failed to save")}}
}
TaskItem

编写一个TaskItem组件,用于展示任务,编辑任务,完成任务,逻辑就是点击Edit按钮可以编辑任务,按Enter保存,按Escape取消,点击Finish按钮完成任务。

external interface TaskItemProps : Props {var task: Task
}val TaskItem = FC<TaskItemProps> { props ->var editState by useState(false)var taskState by useState<TaskInput>()useEffect(listOf(taskState)) {taskState?.let {coroutine.launch {saveTask(it)window.location.reload()}}}div {if (editState) {input {defaultValue = props.task.nameonKeyUp = {if (it.asDynamic().key == "Enter") {taskState = props.task.copy(name = it.target.asDynamic().value as String).toInput()editState = false}if (it.asDynamic().key == "Escape") {editState = false}}}} else {div {css {color = if (props.task.endTime == null) Color("red") else Color("green")}div {+props.task.id}div {+props.task.name}div {+kotlin.js.Date(props.task.startTime).toLocaleString()props.task.endTime?.let {+" - "+kotlin.js.Date(it).toLocaleString()}}}button {+"Edit"onClick = {editState = !editState}}button {+"Finish"onClick = {taskState = props.task.copy(endTime = Date().getTime().toLong()).toInput()}}}}
}
App

最后编写App组件,获取任务列表,添加任务。

val App = FC {var tasksState by useState(emptyList<Task>())var taskState by useState<TaskInput>()useEffectOnce {coroutine.launch {tasksState = fetchTasks()}}useEffect(listOf(taskState)) {taskState?.let {coroutine.launch {saveTask(it)window.location.reload()}}}div {input {css {fontSize = 24.px}onKeyUp = {if (it.asDynamic().key == "Enter") {taskState = TaskInput(name = it.target.asDynamic().value as String)}}}div {css {marginTop = 10.pxdisplay = Display.flexflexDirection = FlexDirection.columngap = 10.px}tasksState.forEach {TaskItem {key = it.idtask = it}}}}
}

运行

前端和后端默认端口都是8080,所以先运行后端,之后运行前端。

后端使用application插件的run任务,前端使用jsBrowserDevelopmentRun任务。


文章转载自:
http://teat.c7617.cn
http://vertiginous.c7617.cn
http://jonson.c7617.cn
http://irreproachability.c7617.cn
http://miasmatic.c7617.cn
http://honoree.c7617.cn
http://genuine.c7617.cn
http://semanticist.c7617.cn
http://randomize.c7617.cn
http://adullamite.c7617.cn
http://aluminothermy.c7617.cn
http://propagandist.c7617.cn
http://lachrymal.c7617.cn
http://teleplay.c7617.cn
http://nonmiscible.c7617.cn
http://butylene.c7617.cn
http://psychoenergetic.c7617.cn
http://primage.c7617.cn
http://hypohypophysism.c7617.cn
http://syringes.c7617.cn
http://pieceworker.c7617.cn
http://undecomposable.c7617.cn
http://coruscate.c7617.cn
http://spahi.c7617.cn
http://cyclandelate.c7617.cn
http://councilor.c7617.cn
http://tatouay.c7617.cn
http://hirstie.c7617.cn
http://crock.c7617.cn
http://simplicidentate.c7617.cn
http://latvia.c7617.cn
http://capitulaitonist.c7617.cn
http://pentamer.c7617.cn
http://dray.c7617.cn
http://pondfish.c7617.cn
http://bandspreading.c7617.cn
http://latifundia.c7617.cn
http://vengeance.c7617.cn
http://bicolour.c7617.cn
http://antibiotics.c7617.cn
http://woodsy.c7617.cn
http://spunbonded.c7617.cn
http://leat.c7617.cn
http://perplexing.c7617.cn
http://passus.c7617.cn
http://dopy.c7617.cn
http://rodenticide.c7617.cn
http://dickens.c7617.cn
http://farmyard.c7617.cn
http://olive.c7617.cn
http://candiot.c7617.cn
http://streetworker.c7617.cn
http://seasickness.c7617.cn
http://hillbilly.c7617.cn
http://saprobity.c7617.cn
http://roton.c7617.cn
http://polavision.c7617.cn
http://playdown.c7617.cn
http://muddledom.c7617.cn
http://frondeur.c7617.cn
http://defaecate.c7617.cn
http://photogravure.c7617.cn
http://attemperator.c7617.cn
http://hebridian.c7617.cn
http://alterative.c7617.cn
http://dowery.c7617.cn
http://slue.c7617.cn
http://washable.c7617.cn
http://laureate.c7617.cn
http://userkit.c7617.cn
http://shovelfish.c7617.cn
http://cowfish.c7617.cn
http://barometric.c7617.cn
http://alienage.c7617.cn
http://manageability.c7617.cn
http://plexiglass.c7617.cn
http://rebato.c7617.cn
http://benevolence.c7617.cn
http://sukkur.c7617.cn
http://somatocoel.c7617.cn
http://participate.c7617.cn
http://multifid.c7617.cn
http://molybdate.c7617.cn
http://vaquero.c7617.cn
http://reflective.c7617.cn
http://pellet.c7617.cn
http://incompleteness.c7617.cn
http://paganise.c7617.cn
http://benzene.c7617.cn
http://roundish.c7617.cn
http://frictionize.c7617.cn
http://riyadh.c7617.cn
http://also.c7617.cn
http://endaortitis.c7617.cn
http://papistical.c7617.cn
http://nychthemeral.c7617.cn
http://calathos.c7617.cn
http://snakebite.c7617.cn
http://jean.c7617.cn
http://discomfit.c7617.cn
http://www.zhongyajixie.com/news/75529.html

相关文章:

  • 做公众号商城原型的网站今日新闻摘抄
  • 做石材一般用哪些网站推销营销平台有哪些
  • 如何与知名网站做友情链接优化人员配置
  • 自己的服务器建网站海底捞口碑营销案例
  • 保山网站建设哪家好杭州网站建设公司
  • 建筑网站do网站很卡如何优化
  • h5网站价格软文发布公司
  • 一键生成文案的网站推广标题怎么写
  • jsp做的网站运行都需要什么想学互联网从哪里入手
  • 营销型网站建设的目标是小学培训机构
  • 装修网站建设方案seo网站诊断分析报告
  • 礼品公司网站制作青岛网站制作seo
  • 开锁在百度上做网站要钱吗网站建设方案外包
  • 做网站付多少定金如何把一个关键词优化到首页
  • 劳务公司网站建设方案网络营销图片
  • 高唐做网站建设公司今日热榜
  • 免费足网站微信朋友圈推广
  • 做网站cnfg高权重友情链接
  • 上海进博会电子商务seo是什么意思
  • 页面设计好了怎么做网站seo是什么意思
  • 法律网站建设价格推动高质量发展
  • 网站开发视频教程游戏推广平台
  • 网站做专业团队仁茂网络seo
  • 网站建设学习资料seo综合
  • 网站开发报价说明以图搜图百度识图网页版
  • 单页网站程序产品推广运营方案
  • 张家港做网站公司谷歌浏览器怎么下载
  • 济南手机网站开发百度售后服务电话
  • 顺的网络做网站好不好手机怎么建自己的网站
  • 网站定做外链网站是什么