Kotlin 基础知识总结

lifecycleScope.launchWhenResumed

launchWhenResumed
launchWhenResumed是一个扩展函数,它是LifecycleCoroutineScope的一部分,并且它是在AndroidLifecycle库中引入的。
这个函数的主要目的是在Lifecycle的对应组件(通常是ActivityFragment)处于"resumed"状态时启动协程。
kotlin 复制代码
public fun LifecycleCoroutineScope.launchWhenResumed(
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job

这个函数的参数是:

  • start: 协程的启动模式。
  • block: 协程中要执行的代码块。
kotlin 复制代码
class MainActivity : AppCompatActivity() {
 
    private val mainJob = Job()
    private val uiScope = CoroutineScope(Dispatchers.Main + mainJob)
 
    override fun onResume() {
        super.onResume()
        uiScope.launchWhenResumed {
            // 这里的代码只会在Activity resumed的时候运行
        }
    }
 
    override fun onPause() {
        mainJob.cancel() // 取消所有在uiScope下的协程
        super.onPause()
    }
}

Kotlin savedInstanceState.run

savedInstanceState
在Kotlin中,savedInstanceState 是一个 Bundle 类型的对象,它通常在 ActivityFragmentonCreate 方法中使用。
如果你尝试在 savedInstanceState 上直接调用 run 方法,会出现错误,因为 Bundle 类并没有定义 run 函数。
run 函数是 kotlin.run 的特殊语法糖,它是一个顶层函数,可以在任何表达式中作为闭包使用。
Bundle 类型并没有定义闭包的特性,所以你不能在 savedInstanceState 上直接使用 run
run 函数只有在 savedInstanceState 不为 null 时才会执行,这样就避免了在 Activity 首次创建时执行不必要的恢复逻辑。如果 savedInstanceStatenull,则 run 代码块内的内容将被跳过。
import 复制代码
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
 
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        savedInstanceState?.run {
            // 从Bundle中恢复数据和状态
            val someKey = "some_key"
            val someValue = getString(someKey)
            Log.d("MainActivity", "恢复的值: $someValue")
            // 更多的恢复逻辑...
        }
 
        // 如果savedInstanceState为null,说明是第一次创建Activity
        // 相关的初始化逻辑...
    }
}

Kotlin lifecycleScope launch

lifecycleScope launch
在Kotlin中,lifecycleScopelaunch是来自Kotlin Coroutines库的两个关键函数,它们被广泛用于Android开发中,以简化协程的使用。
lifecycleScope是一个扩展函数,它为Activity或Fragment提供了一个协程范围,在该范围内启动的协程会自动与Activity或Fragment的生命周期绑定,并在Activity或Fragment的DESTROY事件时自动取消。
launch是一个扩展函数,它用于启动新的协程。

当用户点击按钮时,会启动一个新的协程来获取数据,并在1秒后更新TextView的文本。这个协程会在Activity销毁时自动取消。

kotlin 复制代码
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*
 
class MainActivity : AppCompatActivity() {
    private lateinit var textView: TextView
    private lateinit var button: Button
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        textView = findViewById(R.id.textView)
        button = findViewById(R.id.button)
 
        button.setOnClickListener {
            lifecycleScope.launch {
                textView.text = getData()
            }
        }
    }
 
    suspend fun getData(): String {
        delay(1000) // 模拟长时间运行的操作
        return "Hello, Coroutines!"
    }
}

Kotlin LiveData postValue

LiveData postValue
postValue()LiveData 类的一个方法,它用于向主线程异步分发值的改变。
这是对 setValue() 的一个有用补充,因为 setValue() 应该只在主线程中被调用。
如果你正在使用 Kotlin 并且想要使用 postValue() 方法,你需要确保你已经在你的项目中包含了正确版本的 LiveData 库。

在这个例子中,updateNumber() 函数会在一个后台线程中被调用,然后它会异步地更新 _number 的值。这样,无论 updateNumber() 在哪个线程中被调用,_number 的值都会被正确地更新到主线程。

注意:postValue() 方法只能在 MutableLiveData 对象上调用,因为 LiveData 对象是只读的。

kotlin 复制代码
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
 
class MyViewModel : ViewModel() {
 
    // 创建一个 LiveData 对象
    private val _number = MutableLiveData<Int>()
 
    // 对外提供只读的 LiveData 对象
    val number: LiveData<Int> get() = _number
 
    // 定义一个函数来改变 LiveData 的值
    fun updateNumber() {
        // 异步分发值的改变
        _number.postValue(5)
    }
}

Kotlin by inject()

by inject()
by inject() 这样的语法通常与依赖注入(Dependency Injection)框架一起使用,特别是在 Android 开发中。
依赖注入是一种设计模式,它允许你将对象(依赖项)的实例传递给需要它们的代码,而不是让代码自己创建或查找这些实例。
by inject() 这样的语法是 Kotlin 属性委托(Property Delegation)的一个应用。属性委托允许你将属性的 getter 和 setter 方法的实现委托给另一个对象。在依赖注入的上下文中,这通常意味着你委托给某个能够为你提供依赖项的对象。

例如,假设你有一个名为 MyService 的服务,你想在你的 Activity 或 Fragment 中使用它。你可以使用依赖注入框架(如 Dagger、Koin 等)来管理 MyService 的实例,并通过 by inject() 将其注入到你的类中。

java 复制代码
// MyService.kt class MyService { 
    // ... 服务的实现 ...
} 

// AppModule.kt 
import org.koin.dsl.module.module

val appModule = module {
    single { MyService() } 
}

然后,在你的 Activity 或 Fragment 中,你可以使用 by inject() 来注入 MyService 的实例:

kotlin 复制代码
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.component.inject
import androidx.appcompat.app.AppCompatActivity

class MyActivity : AppCompatActivity() {
    private val myService: MyService by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my)

        // 使用 myService ...  
    }
}

Kotlin commitAllowingStateLoss

commitAllowingStateLoss
Android开发中,commitAllowingStateLoss()是FragmentManager类的一个方法,用于提交一个Fragment事务。
这个方法允许在状态丢失(即Activity处于非活跃状态)的情况下提交事务,但是这样做可能会引起状态丢失,所以要谨慎使用。
在这个例子中,我们创建了一个MyFragment的实例,并用fragmentManager将它替换到ID为R.id.fragment_container的容器中。
scss 复制代码
val fragment = MyFragment()
fragmentManager.commitAllowingStateLoss() {
    replace<MyFragment>(R.id.fragment_container)
}
scss 复制代码
val beginTransaction = childFragmentManager.beginTransaction()

beginTransaction.replace(
    R.id.device_fl_main_container,
    fragment,
    fragment.javaClass.simpleName
).apply {
    commitAllowingStateLoss()
   
}

Kotlin setImageResource

setImageResource
setImageResource是一个在ImageView类中定义的方法,用于设置图片资源。

直接使用资源ID设置图片

css 复制代码
val imageView: ImageView = findViewById(R.id.my_image_view)
imageView.setImageResource(R.drawable.my_image)

使用资源名称设置图片

ini 复制代码
val imageView: ImageView = findViewById(R.id.my_image_view)
val packageManager: PackageManager = packageManager
val resId: Int = getPackageName().let { packageName ->
        packageManager.getResourcesForApplication(packageName).getIdentifier("my_image", 
        "drawable", packageName)
}
imageView.setImageResource(resId)

使用资源名称和类型设置图片

ini 复制代码
val imageView: ImageView = findViewById(R.id.my_image_view)
val resId: Int = resources.getIdentifier("my_image", "drawable", packageName)
imageView.setImageResource(resId)

CSDN # APP注册SensorEventListener-Android12

Kotlin also let 区别

alo let 区别
在Kotlin中,letalso都是作用是为了在一个不为null的对象上执行代码块,但它们之间的主要区别在于let可以返回一个新的值,而also则不返回任何值

also的使用方法如下:

在这个例子中,无论text是否为null,代码块都会执行。如果text不为null,则会打印出来。also会返回原始对象,所以println的结果是"Hello"

arduino 复制代码
val text: String? = "Hello"
text?.also {
    println("Text is not null: $it")
}

let的使用方法如下:

在这个例子中,如果text不为null,代码块会执行并打印出来。let不同于also,它可以返回一个新的值,在这个例子中,返回的是字符串"World"。如果text为null,则代码块不会执行,let将返回null。

arduino 复制代码
val text: String? = "Hello"
text?.let {
    println("Text is not null: $it")
    "World"  // 返回一个新的值
}

Kotlin requireContext 深入理解

requireContext
requireContext() 是 Kotlin 在 Android 开发中用于获取当前 FragmentViewModel 所在的 Context 的方法。它是一个扩展函数,可以在 FragmentViewModel 中直接调用。
如果 FragmentViewModel 当前没有关联的 Context,调用 requireContext() 会抛出一个 IllegalStateException
因此,在调用 requireContext() 之前,应该确保 Fragment 已经添加到 Activity 中并且没有被解除关联。
请注意,ViewModel 没有直接的方法来获取 Context,通常推荐使用 getApplication() 或者依赖注入的方式来获取 Context
如果你确实需要在 ViewModel 中使用 Context,请确保你的 ViewModel 不会引起内存泄漏,因为 Context 的引用会使得 ViewModel 持有 Activity 的引用。

以下是一个在 Fragment 中使用 requireContext() 的例子:

kotlin 复制代码
class MyFragment : Fragment() {
 
    fun doSomething() {
        val context = requireContext()
        // 使用 context 进行操作
    }
}

ViewModel 中使用 requireContext() 的例子:

kotlin 复制代码
class MyViewModel : ViewModel() {
 
    fun doSomething() {
        val context = getApplication() // 或者 requireContext()
        // 使用 context 进行操作
    }
}

Kotlin lifecycleScope.launch async

lifecycleScope.launch async
在Kotlin中,lifecycleScope.launchasync都被用于在Android开发中处理协程。
lifecycleScope.launch用于在特定的生命周期内启动协程,而async则用于在协程中执行后台任务,并返回结果。
注意:在实际开发中,应该确保协程在Activity或Fragment的整个生命周期内运行,并在生命周期结束时取消协程。这可以通过lifecycleScope.launch来实现,因为lifecycleScope会在Activity或Fragment的生命周期结束时自动取消其所有协程。
  1. 使用lifecycleScope.launchasync在Coroutine中执行后台任务并更新UI:
kotlin 复制代码
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        lifecycleScope.launch {
            val result = async(Dispatchers.IO) {
                // 模拟长时间运行的任务
                delay(1000L)
                "Hello, World!"
            }
            // 在UI中更新
            textView.text = result.await()
        }
    }
}

2.使用lifecycleScope.launchasync在Coroutine中执行后台任务并捕获异常:

kotlin 复制代码
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        lifecycleScope.launch {
            val deferredResult = async(Dispatchers.IO) {
                // 模拟长时间运行的任务
                delay(1000L)
                "Hello, World!"
            }
 
            try {
                val result = deferredResult.await()
                // 使用结果
            } catch (e: Exception) {
                // 处理异常
            }
        }
    }
}

Kotlin lifecycleScope.launch 和 CoroutineScope.launch 区别

lifecycleScope.launch、CoroutineScope.launch区别
在Kotlin中,lifecycleScope.launchCoroutineScope.launch都用于启动协程。
但是它们之间有一个关键的区别:lifecycleScope.launch自动附加到关联组件的生命周期,当关联的组件(如Activity或Fragment)死亡时,启动的协程也会被取消。
CoroutineScope.launch则不会自动取消协程,需要手动管理协程的生命周期。

在实际应用中,如果你的协程需要和特定的组件(如Activity或Fragment)的生命周期绑定,那么使用lifecycleScope.launch是更安全的选择。这样可以避免因为忘记取消协程而导致的内存泄漏问题。

使用lifecycleScope.launch(在Android中):

kotlin 复制代码
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
 
        lifecycleScope.launch {
            // 这里的协程会在Activity销毁时自动取消
            delay(1000L) // 非阻塞式延迟1秒
            // 更多的协程代码
        }
    }
}

使用CoroutineScope.launch

scss 复制代码
val coroutineScope = CoroutineScope(Dispatchers.Main)
 
coroutineScope.launch {
    // 这里的协程不会自动取消,需要手动管理
    delay(1000L) // 非阻塞式延迟1秒
    // 更多的协程代码
}

Kotlin CoroutineScope.launch join

CoroutineScope.launch join
在Kotlin中,CoroutineScope.launch是一个协程构造器,它会创建一个新的协程并且立即返回一个Job对象。
join方法是Job对象的一个方法,它会阻塞当前线程直到协程执行完毕。

如果你想要在主线程中等待一个协程完成,并且你想要使用CoroutineScope.launchjoin,你可以这样做:

kotlin 复制代码
import kotlinx.coroutines.*
 
fun main() = runBlocking<Unit> {
    val job = launch {
        // 这里是你的协程代码
        delay(1000L) // 非阻塞延迟
        println("World!")
    }
 
    // 等待协程完成
    job.join()
    println("Hello!")
}

在这个例子中,launch创建了一个新的协程,并返回了一个Job对象。join方法被用来阻塞主线程直到协程打印出"World!"并完成。然后主线程继续执行,打印出"Hello!"。

注意:runBlocking创建了一个新的协程范围,并且阻塞了主线程直到协程完成。这是在主线程上等待协程的一种简单方式。

Kotlin CoroutineScope.launch cancel

CoroutineScope.launch cancel
在Kotlin中,CoroutineScope.launch函数会返回一个Job对象,它代表了一个协程的执行。
你可以使用这个Job对象的cancel方法来取消这个协程的执行。

直接取消协程

java 复制代码
val job = CoroutineScope.launch {
    // your coroutine code here
}
 
job.cancel()

使用Job的引用来取消协程

java 复制代码
val job = CoroutineScope.launch {
    // your coroutine code here
}
 
// 在某个时刻,你可以通过job的引用来取消协程
job.cancel()

在协程执行完毕后取消协程

java 复制代码
val job = CoroutineScope.launch {
    // your coroutine code here
}.also {
    // 协程执行完毕后,取消协程
    it.cancel()
}

在协程抛出异常后取消协程

java 复制代码
val job = CoroutineScope.launch {
    // your coroutine code here
    throw Exception()
}.catch {
    // 处理异常
}.also {
    // 协程执行完毕后,取消协程
    it.cancel()
}

使用CoroutineContext.cancelChildren取消所有子协程

javascript 复制代码
val job = CoroutineScope.launch(CoroutineName("parent")) {
    launch(CoroutineName("child")) {
        // your coroutine code here
    }
    // your coroutine code here
}
 
// 取消父协程,同时会取消所有子协程
job.cancelChildren()

以上就是在Kotlin中取消协程的一些常见方法。注意,协程的取消并不一定会立即停止协程的执行,它只是向协程发送了一个取消的信号,协程可能会在任何时候检查这个信号,并据此决定是否停止执行。

Kotlin synchronized this

标题
在Kotlin中,synchronized是一个关键字,用于确保在同一时刻只有一个线程可以进入被同步的代码块。
当使用synchronized(this)时,意味着只有持有当前对象锁的线程可以执行同步代码块。

以下是一个使用synchronized(this)的简单例子:

kotlin 复制代码
class Counter {
    var value = 0
 
    fun increment() {
        synchronized(this) {
            value++
        }
    }
}
 
fun main() {
    val counter = Counter()
    
    val thread1 = thread {
        repeat(1000) {
            counter.increment()
        }
    }
 
    val thread2 = thread {
        repeat(1000) {
            counter.increment()
        }
    }
 
    thread1.join()
    thread2.join()
 
    println(counter.value) // 输出结果应该为2000
}

在这个例子中,Counter类有一个increment方法,它是线程安全的,因为synchronized(this)确保了同一时刻只有一个线程可以调用increment方法。

main函数中创建了两个线程,它们分别对Counter对象的value进行1000次递增操作。

如果没有synchronized,最后输出的值可能小于2000,因为两个线程可能同时访问和修改value

加上synchronized后,可以保证操作的原子性,确保最终输出值是2000。

Kotlin 显示对话框 synchronized this

显示对话框 synchronized this
我们通常使用协程来处理异步操作,并且避免使用像synchronized这样的Java同步原语。但如果你需要在特定的区块中同步访问,你可以使用synchronized关键字。
showDialogSynchronized函数确保在任何时刻只有一个线程可以执行对话框的显示逻辑。如果你在多线程环境中调用showDialogSynchronized,后续的调用将会等待,直到当前的对话框显示和同步块完成。
请注意,这个例子假设DialogManager的实例在应用程序的生命周期中是单例的或者有一个全局唯一的实例。如果不是这样,你可能需要使用其他的同步机制,例如使用synchronized块来同步一个私有的对象,或者使用ReentrantLock
kotlin 复制代码
import android.app.AlertDialog
import android.content.Context
import kotlin.concurrent.synchronized
 
class DialogManager(val context: Context) {
    private var isDialogShowing = false
 
    fun showDialogSynchronized() {
        synchronized(this) {
            if (!isDialogShowing) {
                isDialogShowing = true
                val dialog = AlertDialog.Builder(context)
                    .setMessage("Dialog Message")
                    .setPositiveButton("OK") { dialog, which ->
                        isDialogShowing = false
                        dialog.dismiss()
                    }
                    .create()
                dialog.show()
            }
        }
    }
}

Kotlin FragmentManager findFragmentByTag

findFragmentByTag
findFragmentByTagFragmentManager 类的一个方法,它用于通过一个特定的标签来查找一个已经被添加到 Activity 的 Fragment。
如果你想在 Kotlin 代码中使用这个方法,你需要首先获取 FragmentManager 的实例。

展示了如何使用 findFragmentByTag 方法:

java 复制代码
// 假设你已经有一个 Fragment 并且它已经添加到 Activity 中
// 这个 Fragment 的标签是 "my_fragment_tag"
 
// 获取 FragmentManager
val fragmentManager = supportFragmentManager
 
// 使用 findFragmentByTag 方法查找 Fragment
val myFragment = fragmentManager.findFragmentByTag("my_fragment_tag")
 
// 如果找到了 Fragment 实例,你可以进一步操作它
if (myFragment != null) {
    // 操作你的 Fragment
}

在这个例子中,我们首先通过 supportFragmentManager 获取 FragmentManager 的实例。

然后我们调用 findFragmentByTag 方法并传入一个字符串参数 "my_fragment_tag",该字符串应该与你添加 Fragment 时指定的标签相匹配。

如果 Fragment 被找到,我们可以在 if 语句中对它进行操作。

Kotlin FragmentManager isStateSaved

isStateSaved
isStateSavedFragmentManager 的一个方法,它用于检查FragmentManager关联的Activity是否正在保存其状态(例如,用户正在退出应用程序,或者系统正在关闭应用程序以便进行内存清理)。
如果你在调用一些需要通过FragmentManager进行的操作,例如显示对话框或者导航到另一个Fragment时,你可能需要检查这个方法,以确保不在Activity正在保存状态时进行操作,因为这可能会引发IllegalStateException

以下是一个简单的例子,演示如何在Kotlin中使用isStateSaved

kotlin 复制代码
fun showDialogIfNeeded(fragmentManager: FragmentManager) {
    if (!fragmentManager.isStateSaved) {
        val dialogFragment = MyDialogFragment()
        dialogFragment.show(fragmentManager, "dialog_tag")
    }
}

在这个例子中,如果Activity正在保存状态,showDialogIfNeeded函数就不会尝试显示对话框。

这样可以避免在Activity状态正在保存时对Fragment进行操作,从而避免IllegalStateException异常。

Kotlin mBinding run isAdded

mBinding run isAdded
在Kotlin中,如果你想检查一个Fragment是否已经被添加到它的Activity中,你可以使用isAdded属性。
这个属性是Fragment类的一部分,它会告诉你Fragment是否已经附加到Activity上。
如果你使用的是View Binding,那么你需要在Fragment中保存一个绑定对象的引用,然后在需要检查Fragment是否已经添加的时候使用isAdded
kotlin 复制代码
class MyFragment : Fragment() {
 
    private var _binding: FragmentMyBinding? = null
    private val binding get() = _binding!!
 
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentMyBinding.inflate(inflater, container, false)
        return binding.root
    }
 
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (isAdded) {
            // Fragment已经添加到Activity,可以安全使用binding对象
            binding.myTextView.text = "Fragment is added!"
        }
    }
 
    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

在这个例子中,MyFragment 使用了View Binding来引入布局。

onViewCreated方法中,我们检查了isAdded,以确保在尝试操作binding对象之前Fragment已经被添加到Activity中。

onDestroyView中,我们将_binding设置为null以避免内存泄漏。

掘金 Android AutoService 组件化

CSDN # Google开源库AutoService进行组件化通信

CSDN # Kotlin学习(七)-- java和kotlin混合开发时常用注解的使用@JvmName,@JvmField,@JvmOverloads, @JvmStatic....

CSDN # Kotlin中的object 与companion object的区别

掘金 # Kotlin class、data class、object、companion object区别

CSDN Kotlin中object和companion object区别

kotlin withContext Dispatchers.Main

withContext Dispatchers.Main
withContext 是 Kotlin 协程中的一个函数,它允许你在协程中根据指定的上下文(Context)执行代码。
如果你尝试在 withContext 中使用 Dispatchers.Main,你可能会遇到一个错误,因为 Dispatchers.Main 是与特定平台相关的,并不是在所有环境中都可用,比如在 JVM 上就不存在 Dispatchers.Main,但是存在 Dispatchers.Default
如果你正在开发一个 Android 应用,你可以使用 Dispatchers.Main,因为它是在 Android 中代表主线程的调度器。但是在其他环境,比如在 JVM 上,你应该使用 Dispatchers.Default

以下是一个在 Kotlin 协程中使用 withContextDispatchers.Main 的例子:

kotlin 复制代码
import kotlinx.coroutines.*
 
fun main() = runBlocking {
    launch(Dispatchers.Main) {
        val result = withContext(Dispatchers.Main) {
            // 在这里执行主线程上的代码
            "Hello Main Thread"
        }
        println(result)
    }
}

在这个例子中,launch(Dispatchers.Main) 启动了一个在主线程上运行的协程,而 withContext(Dispatchers.Main) 确保了在这个协程中的代码块在主线程上执行。

请注意,在 JVM 上,你应该替换 Dispatchers.MainDispatchers.Default,以确保代码在默认的调度器上执行。

相关推荐
傻小胖8 分钟前
React 脚手架配置代理完整指南
前端·react.js·前端框架
EterNity_TiMe_20 分钟前
【论文复现】农作物病害分类(Web端实现)
前端·人工智能·python·机器学习·分类·数据挖掘
余生H39 分钟前
深入理解HTML页面加载解析和渲染过程(一)
前端·html·渲染
吴敬悦1 小时前
领导:按规范提交代码conventionalcommit
前端·程序员·前端工程化
ganlanA1 小时前
uniapp+vue 前端防多次点击表单,防误触多次请求方法。
前端·vue.js·uni-app
卓大胖_1 小时前
Next.js 新手容易犯的错误 _ 性能优化与安全实践(6)
前端·javascript·安全
m0_748246351 小时前
Spring Web MVC:功能端点(Functional Endpoints)
前端·spring·mvc
SomeB1oody1 小时前
【Rust自学】6.4. 简单的控制流-if let
开发语言·前端·rust
云只上1 小时前
前端项目 node_modules依赖报错解决记录
前端·npm·node.js
程序员_三木1 小时前
在 Vue3 项目中安装和配置 Three.js
前端·javascript·vue.js·webgl·three.js