0

0

iOS 26语音API:使用Swift实现实时语音转文本

心靈之曲

心靈之曲

发布时间:2026-01-05 09:44:03

|

735人浏览过

|

来源于php中文网

原创

在移动应用开发领域,语音识别技术正变得越来越重要。它不仅提升了用户体验,还为应用增加了新的交互方式。iOS 26 引入了强大的语音 API,为开发者提供了前所未有的便利。本文将深入探讨如何在Swift中使用 iOS 26 的语音 API,实现实时语音转文本功能,并提供详细的代码示例和步骤指南,帮助开发者快速上手。我们将从配置项目、获取权限,到实现核心的语音识别功能进行详细讲解,并分享一些最佳实践,确保你的应用能够以最佳状态使用这项技术。同时,我们也会探讨这一API的优点和局限性,以及如何在实际项目中有效地应用。

iOS 26语音API关键点

利用Swift实现iOS 26的实时语音转文本功能。

详细讲解配置项目和获取语音权限的步骤。

提供核心语音识别功能的代码示例。

分享提高语音识别准确率的最佳实践。

分析iOS 26语音API的优点和局限性。

探讨在实际项目中有效应用语音API的策略。

解释如何使用AudioKit进行音频处理

iOS 26语音API介绍及准备工作

什么是iOS 26语音API?

ios 26 语音 api 是一套强大的工具,允许开发者将语音识别功能集成到他们的应用程序中。通过这个 api,你可以实现实时语音转文本,让用户通过语音与你的应用进行交互。该 api 利用设备上的语音识别引擎,能够提供准确、快速的语音转录服务。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

iOS 26语音API:使用Swift实现实时语音转文本

这个API能够将语音转换为文字,并提供置信度评分,方便开发者了解识别的准确性,根据结果调整应用行为。利用最新的 iOS 26 语音 API,开发者可以为用户打造更加自然、智能的交互体验。

项目配置和权限获取

在使用 iOS 26 语音 API 之前,需要进行一些必要的项目配置和权限获取。首先,确保你的 Xcode 项目支持 iOS 26 或更高版本。然后,需要在 Info.plist 文件中添加麦克风使用权限描述,以便在应用运行时向用户请求麦克风访问权限。

具体步骤如下:

  1. 打开你的 Xcode 项目。
  2. 找到 Info.plist 文件。
  3. 添加 Privacy - Microphone Usage Description 键,并设置一个描述,解释你的应用为什么需要访问麦克风。例如,可以设置为 “允许应用使用麦克风录制音频,用于语音转文本功能”。
  4. 保存 Info.plist 文件。

完成以上步骤后,你的应用就可以在运行时请求麦克风访问权限了。需要注意的是,务必在用户明确需要使用语音功能时再请求权限,并提供清晰的理由,这样可以提高用户授权的可能性。

iOS 26语音API:使用Swift实现实时语音转文本

Swift代码实现语音转文本

音频捕获代码详解

为了捕获用户的语音,你需要使用 AVFoundation 框架。AVFoundation 提供了强大的音频处理功能,可以用来录制、播放和处理音频数据。以下是一个简单的音频捕获代码示例:

import Foundation
import AVFoundation

class AudioCapter {
   @preconcurrency import AVAudio
    nonisolated AudioCapter()
    // 音频引擎和其他变量
    let inputTapEventsStream: AsyncStream<avaudiopcmbuffer avaudiotime>
    let inputTapEventsContinuation: AsyncStream<avaudiopcmbuffer avaudiotime>.Continuation
    private let audioEngine = AVAudioEngine()
    private let audioSession = AVAudioSession.sharedInstance()

    let bufferSize: UInt32 = 1024

    init() throws {
        (self.inputTapEventsStream, self.inputTapEventsContinuation) = AsyncStream.makeStream(of: (AVAudioPCMBuffer, AVAudioTime).self)

        try self.configureAudioSession()
    }

    func configureAudioSession() throws {
        audioSession.setCategory(.playAndRecord, mode: .measurement, options: [.duckOthers, .defaultToSpeaker, .allowBluetoothHFP])
        try audioSession.setActive(true, options: .notifyOthersOnDeactivation)

        guard let availableInputs = audioSession.availableInputs,
              let builtInMicInput = availableInputs.first(where: { $0.portType == .builtInMic }) else {
            throw NSError(domain: "", code: 1)
        }
        try audioSession.setPreferredInput(builtInMicInput)

    }

    func startCapturingInput() async throws {
        // 确保应用获得了录音权限
        await self.checkRecordingPermission()
        self.audioEngine.reset()

        let inputNode = audioEngine.inputNode

        // 在input node安装tap
        let format = inputNode.outputFormat(forBus: 0)
        inputNode.removeTap(onBus: 0)
        inputNode.installTap(onBus: 0, bufferSize: bufferSize, format: format) { (buffer: AVAudioPCMBuffer, time: AVAudioTime) in
            self.inputTapEventsContinuation.yield((buffer, time))
        }

        audioEngine.prepare()
        try audioEngine.start()

    }
}

func checkRecordingPermission() async throws {
    switch AVAudioApplication.shared.recordPermission {
    case .undetermined:
        let result = await AVAudioApplication.requestRecordPermission()
        if !result {
            throw NSError(domain: "", code: 1)
        }
    case .denied:
        throw NSError(domain: "", code: 1)
    case .granted:
        return
    default:
        throw NSError(domain: "", code: 1)
    }
}</avaudiopcmbuffer></avaudiopcmbuffer>

这段代码首先导入了 AVFoundation 框架,并创建了一个 AudioCapter 类。这个类负责配置音频会话、获取麦克风输入,并在输入节点上安装一个 tap。inputTapEventsStreaminputTapEventsContinuation是用于数据捕获的关键点,这里使用了Swift的AsyncStream用于异步处理音频数据流。

iOS 26语音API:使用Swift实现实时语音转文本

其中@preconcurrency import AVAudio这段代码主要用于解决 Swift 并发环境下的兼容性问题,它告诉编译器这是一个旧的框架,在执行并发检查时应该更加宽松。 在ios26以上的版本,使用这个可以兼容老框架,可以理解为对老的框架兼容性更好。

非孤立nonisolated)关键字意味着该类不属于特定的角色(actor),并且可以被多个并发上下文访问。

配置音频会话(configureAudioSession) 配置音频会话是确保音频正确捕获的关键步骤。你需要设置会话的类别(category)、模式(mode)和选项(options),以适应你的应用场景。以下是 configureAudioSession 方法的代码示例:

func configureAudioSession() throws {
    audioSession.setCategory(.playAndRecord, mode: .measurement, options: [.duckOthers, .defaultToSpeaker, .allowBluetoothHFP])
    try audioSession.setActive(true, options: .notifyOthersOnDeactivation)

    guard let availableInputs = audioSession.availableInputs,
          let builtInMicInput = availableInputs.first(where: { $0.portType == .builtInMic }) else {
        throw NSError(domain: "", code: 1)
    }
    try audioSession.setPreferredInput(builtInMicInput)

}

这段代码设置了音频会话的类别为 playAndRecord,模式为 measurement,并添加了一些选项,例如 duckOthers(降低其他应用的音频音量)、defaultToSpeaker(默认使用扬声器)和 allowBluetoothHFP(允许使用蓝牙耳机)。然后,它激活了音频会话,并设置了首选的麦克风输入。

启动音频捕获(startCapturingInput) 启动音频捕获是开始录制音频的关键步骤。你需要确保应用获得了录音权限,然后重置音频引擎,并在输入节点上安装一个 tap。以下是 startCapturingInput 方法的代码示例:

func startCapturingInput() async throws {
    // 确保应用获得了录音权限
    await self.checkRecordingPermission()
    self.audioEngine.reset()

    let inputNode = audioEngine.inputNode

    // 在input node安装tap
    let format = inputNode.outputFormat(forBus: 0)
    inputNode.removeTap(onBus: 0)
    inputNode.installTap(onBus: 0, bufferSize: bufferSize, format: format) { (buffer: AVAudioPCMBuffer, time: AVAudioTime) in
        self.inputTapEventsContinuation.yield((buffer, time))
    }

    audioEngine.prepare()
    try audioEngine.start()

}

这段代码首先检查应用是否获得了录音权限(checkRecordingPermission),然后重置音频引擎,并在输入节点上安装一个 tap。这个 tap 会在每次有新的音频数据到达时被调用,并将音频数据传递给 inputTapEventsContinuation.yield 方法。

检查录音权限(checkRecordingPermission) 检查录音权限是确保应用能够正常录制音频的关键步骤。你需要使用 AVAudioApplication 类来检查和请求录音权限。以下是 checkRecordingPermission 方法的代码示例:

func checkRecordingPermission() async throws {
    switch AVAudioApplication.shared.recordPermission {
    case .undetermined:
        let result = await AVAudioApplication.requestRecordPermission()
        if !result {
            throw NSError(domain: "", code: 1)
        }
    case .denied:
        throw NSError(domain: "", code: 1)
    case .granted:
        return
    default:
        throw NSError(domain: "", code: 1)
    }
}

这段代码首先检查当前的录音权限状态。如果权限状态是 undetermined,则会向用户请求录音权限。如果用户拒绝了权限,则会抛出一个错误。如果权限状态是 granted,则会直接返回。

转录器代码详解

将捕获的音频转换为文本需要使用到Swift的Speech框架

import SwiftUI
import Speech

class Transcriber {
    let transcriptionResults: AsyncSequence<speechtranscriber.result any error>
    let analyzer: SpeechAnalyzer

    var bestAvailableAudioFormat: AVAudioFormat? = nil

    // 用于接受上游传来的buffer的输入
    let inputSteam: AsyncStream<analyzerinput never> = AsyncStream<analyzerinput never>()
    // 用于链接数据,AsyncStream的特性
    let inputContinuation: AsyncStream<analyzerinput never>.Continuation
    let preset: SpeechTranscriber.Preset = .timeIndexedProgressiveTranscription

    let locale: Locale = .autoupdatingCurrent
    let audioConverter: AVAudioConverter?

    typealias AnalyzerInput = SpeechTranscriber.AnalyzerInput

    init() async throws {

        // 创建AsyncStream
        (self.inputSteam, self.inputContinuation) = AsyncStream.makeStream(of: AnalyzerInput.self,bufferingPolicy: .bufferingNewest(1))

        self.analyzer = SpeechAnalyzer(modules: [transcriber], options: .init(priority: .userInitiated, modelRetention: .processLifetime))

        let bestAvailableAudioFormat = await SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith: transcriber)
        self.bestAvailableAudioFormat = bestAvailableAudioFormat
        self.locale = Locale.autoupdatingCurrent
        // 配置转码器
        self.audioConverter = AVAudioConverter(from:self.bestAvailableAudioFormat!, to:AVAudioFormat(commonFormat:.pcmFormatInt16, sampleRate:44100, channels:1, interleaved:false)!)!   // 44100 sample rate, mono
        let isInstalled = await SpeechTranscriber.installedLocales.contains(locale)
        if !isInstalled {
            let installationRequest = try await AssetInventory.assetInstallationRequest(supporting: [transcriber!])
            try await installationRequest.downloadAndInstall()
        }
       transcriptionResults =  transcriber.results

        transcriber.transcriptionOptions = SpeechTranscriber.TranscriptionOptions(locale: self.locale,    // 使用当前语言环境
                                            // 优化识别效果

         reportingOptions: preset.reportingOptions,
 attributeOptions: preset.attributeOptions.union([.transcriptionConfidence])
)

    }

  private let transcriber = SpeechTranscriber()
}</analyzerinput></analyzerinput></analyzerinput></speechtranscriber.result>

Transcriber 类负责将音频转换为文本。

GarbageSort垃圾识别工具箱
GarbageSort垃圾识别工具箱

GarbageSort垃圾识别工具箱是一个基于uni-app开发的微信小程序,使用SpringBoot2搭建后端服务,使用Swagger2构建Restful接口文档,实现了文字查询、语音识别、图像识别其垃圾分类的功能。前端:微信小程序 采用 uni-app 开发框架,uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H5、以及各

下载

iOS 26语音API:使用Swift实现实时语音转文本

transcriptionResults 是一个异步序列,用于发送转录结果。 analyzer 是用于分析音频的工具。 bestAvailableAudioFormat 变量用于存储最佳可用音频格式。 inputSteam 是用于接收音频缓冲区的异步流,其中定义了数据如何流入转录器。 该类会根据用户 intonation 语调实时调整转录结果,并提供置信度评分,开发者可以根据这个评分来调整应用的行为。

iOS 26 语音API使用指南

配置音频会话

音频会话是应用和系统之间进行音频交互的桥梁。合理的音频会话配置可以确保应用能够正确地捕获和播放音频。

  • 设置类别(category):
    • AVAudioSession.Category.playAndRecord:用于同时播放和录制音频。
    • AVAudioSession.Category.playback:用于播放音频。
    • AVAudioSession.Category.record:用于录制音频。
  • 设置模式(mode):
    • AVAudioSession.Mode.default:默认模式,适用于大多数应用场景。
    • AVAudioSession.Mode.measurement:用于精确测量音频数据。
  • 设置选项(options):
    • AVAudioSession.CategoryOptions.duckOthers:降低其他应用的音频音量。
    • AVAudioSession.CategoryOptions.defaultToSpeaker:默认使用扬声器。
    • AVAudioSession.CategoryOptions.allowBluetoothHFP:允许使用蓝牙耳机。

获取麦克风输入

要获取麦克风输入,需要使用 AVAudioSession 类的 availableInputs 属性。这个属性返回一个数组,包含了所有可用的音频输入设备。你需要遍历这个数组,找到类型为 builtInMic 的设备,并将其设置为首选输入设备。

guard let availableInputs = audioSession.availableInputs,
    let builtInMicInput = availableInputs.first(where: { $0.portType == .builtInMic }) else {
    throw NSError(domain: "", code: 1)
}
try audioSession.setPreferredInput(builtInMicInput)

这段代码首先获取所有可用的音频输入设备,然后找到类型为 builtInMic 的设备,并将其设置为首选输入设备。如果找不到内置麦克风,则会抛出一个错误。

在输入节点上安装 Tap

Tap 是 AVAudioNode 类的一个特性,允许你在音频数据流经过节点时拦截数据。你可以在输入节点上安装一个 tap,以便在每次有新的音频数据到达时进行处理。

let inputNode = audioEngine.inputNode
let format = inputNode.outputFormat(forBus: 0)
inputNode.removeTap(onBus: 0)
inputNode.installTap(onBus: 0, bufferSize: bufferSize, format: format) { (buffer: AVAudioPCMBuffer, time: AVAudioTime) in
    self.inputTapEventsContinuation.yield((buffer, time))
}

这段代码首先获取输入节点,然后移除之前安装的 tap(如果存在),并在输入节点上安装一个新的 tap。这个 tap 会在每次有新的音频数据到达时被调用,并将音频数据传递给 inputTapEventsContinuation.yield 方法。

重要提示: 要确保这个参数类型和AsyncStream中的参数相符合

配置和启动音频引擎

配置和启动音频引擎是开始录制音频的最后一步。你需要调用 audioEngine.prepare() 方法来准备音频引擎,然后调用 audioEngine.start() 方法来启动音频引擎。

audioEngine.prepare()
try audioEngine.start()

这段代码首先准备音频引擎,然后启动音频引擎。如果启动失败,则会抛出一个错误。

使用Speech框架进行转录

使用Speech 框架将捕获的音频数据转换为文本需要以下步骤:

  • 创建 SFSpeechRecognizer 实例:
let recognizer = SFSpeechRecognizer(locale: Locale.autoupdatingCurrent)!

这段代码创建了一个 SFSpeechRecognizer 实例,并设置了语言环境为当前设备使用的语言环境。

  • 创建 SFSpeechAudioBufferRecognitionRequest 实例:
let request = SFSpeechAudioBufferRecognitionRequest()

这段代码创建了一个 SFSpeechAudioBufferRecognitionRequest 实例,用于将音频数据传递给语音识别引擎。

  • 将音频数据传递给 SFSpeechAudioBufferRecognitionRequest 实例:
request.append(buffer)

这段代码将音频数据传递给 SFSpeechAudioBufferRecognitionRequest 实例。buffer 是一个 AVAudioPCMBuffer 实例,包含了捕获的音频数据。

  • 启动语音识别任务:
recognizer.recognitionTask(with: request) { (result, error) in
    if let error = error {
        print("语音识别失败:\(error.localizedDescription)\")
    } else if let result = result {
        print("语音识别结果:\(result.bestTranscription.formattedString)\")
    }
}

这段代码启动了一个语音识别任务,并在任务完成后执行一个闭包。如果语音识别失败,则会打印一个错误信息。如果语音识别成功,则会打印语音识别结果。

iOS 26语音API的优缺点分析

? Pros

实时性:提供实时语音转文本功能,用户可以立即看到识别结果。

准确性:利用设备上的语音识别引擎,提供较高的语音识别准确率。

易用性:API设计简洁易懂,开发者可以使用较少的代码实现复杂的语音识别功能。

灵活性:提供丰富的配置选项,允许开发者根据应用场景进行优化。

安全性:在设备上运行,无需将音频数据发送到云端,保护用户隐私。

? Cons

依赖设备性能:语音识别性能依赖于设备的处理能力。

需要用户授权:需要用户授权麦克风访问权限。

语言支持有限:目前只支持部分语言,需要根据应用场景选择合适的语言环境。

常见问题解答

如何提高语音识别的准确率?

提高语音识别准确率的方法有很多,以下是一些常用的技巧: 使用清晰的音频输入:确保麦克风录制到的音频清晰、无噪音。 设置正确的语言环境:确保 SFSpeechRecognizer 实例的语言环境与用户的口语一致。 优化语音识别参数:根据应用场景调整语音识别参数,例如 SFSpeechRecognitionRequest 的 shouldReportPartialResults 属性。 使用设备上的语音识别引擎:设备上的语音识别引擎通常比网络语音识别引擎更准确。

如何处理语音识别失败的情况?

语音识别失败的情况有很多,以下是一些常见的错误类型: SFSpeechRecognizerErrorDomain:语音识别器错误。 AVAudioSessionErrorDomain:音频会话错误。 kCLErrorDomain:定位服务错误。 针对不同的错误类型,可以采取不同的处理方法。例如,如果语音识别器错误,可以尝试重新启动语音识别任务。如果音频会话错误,可以尝试重新配置音频会话。如果定位服务错误,可以提示用户开启定位服务。

如何支持多种语言的语音识别?

要支持多种语言的语音识别,需要创建多个 SFSpeechRecognizer 实例,并为每个实例设置不同的语言环境。然后,根据用户的选择,使用相应的 SFSpeechRecognizer 实例进行语音识别。

相关问题

iOS 26 语音API 与旧版本的语音识别技术相比有哪些优势?

iOS 26 语音API 相较于旧版本的语音识别技术,在性能、准确性和易用性方面都有显著提升。新的 API 利用设备上的语音识别引擎,能够提供更快、更准确的语音转录服务。此外,iOS 26 语音 API 提供了更多的配置选项,允许开发者根据应用场景进行优化。最重要的是,新的 API 更加易于使用,开发者可以使用更少的代码实现复杂的语音识别功能。 以下是 iOS 26 语音 API 相较于旧版本的语音识别技术的一些主要优势: 更高的准确率:iOS 26 语音 API 利用设备上的语音识别引擎,能够提供更高的语音识别准确率。 更快的速度:iOS 26 语音 API 的语音识别速度更快,能够提供更流畅的用户体验。 更多的配置选项:iOS 26 语音 API 提供了更多的配置选项,允许开发者根据应用场景进行优化。 更易于使用:iOS 26 语音 API 更加易于使用,开发者可以使用更少的代码实现复杂的语音识别功能.

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

3

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

12

2026.03.03

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

69

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

59

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

46

2026.02.28

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

24

2026.02.27

Golang 高级特性与最佳实践:提升代码艺术
Golang 高级特性与最佳实践:提升代码艺术

本专题深入剖析 Golang 的高级特性与工程级最佳实践,涵盖并发模型、内存管理、接口设计与错误处理策略。通过真实场景与代码对比,引导从“可运行”走向“高质量”,帮助构建高性能、可扩展、易维护的优雅 Go 代码体系。

20

2026.02.27

Golang 测试与调试专题:确保代码可靠性
Golang 测试与调试专题:确保代码可靠性

本专题聚焦 Golang 的测试与调试体系,系统讲解单元测试、表驱动测试、基准测试与覆盖率分析方法,并深入剖析调试工具与常见问题定位思路。通过实践示例,引导建立可验证、可回归的工程习惯,从而持续提升代码可靠性与可维护性。

4

2026.02.27

漫蛙app官网链接入口
漫蛙app官网链接入口

漫蛙App官网提供多条稳定入口,包括 https://manwa.me、https

348

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML5/CSS3/JavaScript/ES6入门课程
HTML5/CSS3/JavaScript/ES6入门课程

共102课时 | 7.2万人学习

前端基础到实战(HTML5+CSS3+ES6+NPM)
前端基础到实战(HTML5+CSS3+ES6+NPM)

共162课时 | 21.1万人学习

第二十二期_前端开发
第二十二期_前端开发

共119课时 | 13.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号