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)
    }
}

这段代码首先导入了 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()
}

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

Mokker AI
Mokker AI

AI产品图添加背景

下载

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

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

豆包大模型
豆包大模型

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

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

0

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

39

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

139

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

47

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

90

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

226

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

504

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

170

2026.03.04

热门下载

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

精品课程

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

共102课时 | 7.3万人学习

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

共162课时 | 21.7万人学习

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

共119课时 | 13.3万人学习

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

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