WebGPU全面解析:新一代Web图形与计算API

摘要

WebGPU是W3C GPU for the Web工作组设计的全新Web图形与计算API,旨在为浏览器提供接近原生GPU性能的高效渲染能力。相比上一代WebGL,WebGPU基于现代GPU架构设计,支持计算着色器、多线程渲染和更细粒度的资源管理。本文将从技术原理、安装部署、使用方法、优劣势分析以及与竞品的全面对比等多个维度,带你深入了解这项将改变Web图形编程格局的前沿技术。

一、引言

在Web图形领域,WebGL自2011年发布以来一直是事实上的标准。然而,随着现代GPU架构的快速演进,WebGL的设计局限性日益明显:其基于OpenGL ES的"即时模式"架构难以充分发挥最新硬件的性能潜力,计算能力受限且缺乏对现代图形特性的原生支持。

2023年,经过多年开发的WebGPU正式进入建议推荐阶段,Chrome 113版本率先默认启用这一API。WebGPU的诞生标志着Web图形技术进入了一个全新的时代,它不仅继承了Vulkan、Metal和DirectX 12等现代原生GPU API的设计理念,还专门针对Web平台的特性进行了优化,为开发者提供了在浏览器中实现高性能图形渲染和通用计算的能力。

二、技术原理

2.1 WebGPU的核心架构

WebGPU的核心设计理念是提供一种"显式控制"的编程模型,这与WebGL的"隐式状态机"模式形成了鲜明对比。在WebGPU中,开发者需要显式定义渲染管线的各个阶段、资源绑定方式和同步机制,这种设计虽然增加了编程复杂度,但换来了更高的性能和更可预测的行为。

WebGPU的架构主要由以下几个核心组件构成:适配器(Adapter)代表物理GPU设备,负责查询设备能力和创建逻辑设备;设备(Device)是与GPU交互的主要接口,用于创建命令缓冲区、渲染管线和资源;命令编码器(Command Encoder)记录渲染和计算命令;渲染管线(Render Pipeline)定义顶点处理、图元组装、光栅化和片段处理等渲染阶段;计算管线(Compute Pipeline)则用于执行通用GPU计算任务。

2.2 渲染管线与计算管线

WebGPU支持两种主要的管线类型:渲染管线和计算管线。渲染管线用于图形渲染任务,包括顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)两个必需阶段,以及可选的几何着色器、网格着色器和光栅化等阶段。每个渲染管线需要明确指定颜色附件的格式、深度 stencil 格式、混合状态等渲染目标配置。

计算管线是WebGPU区别于WebGL的重要特性之一。它允许开发者使用计算着色器在GPU上执行通用并行计算任务,而不仅限于图形渲染。计算着色器特别适合处理大规模数据并行任务,如图像处理、物理模拟、机器学习推理等。计算管线的工作方式是将数据存储在缓冲区或纹理中,通过工作组(Workgroup)组织并行执行,开发者可以控制工作组的数量和维度。

2.3 资源管理与绑定机制

WebGPU引入了更加精细的资源管理机制。资源类型包括:缓冲区(Buffer)用于存储顶点数据、索引数据、统一变量和存储数据;纹理(Texture)用于存储图像数据;采样器(Sampler)定义纹理采样方式;绑定组(BindGroup)将资源绑定到着色器阶段。

资源绑定通过绑定组布局(BindGroupLayout)和绑定组(BindGroup)两层结构完成。绑定组布局定义了资源的访问类型(只读、读写)、纹理维度、采样器类型等元数据;绑定组则实际指定要绑定的具体资源实例。这种设计使得WebGPU能够在创建管线时验证资源绑定的合法性,避免运行时的绑定错误。

2.4 WGSL着色器语言

WebGPU采用WGSL(WebGPU Shading Language)作为着色器编程语言。WGSL是一种基于Rust风格语法的现代着色器语言,设计目标是提供类型安全、内存安全且易于学习的编程体验。WGSL支持丰富的类型系统,包括标量类型(i32、u32、f32、bool)、向量类型(vec2、vec3、vec4)、矩阵类型(mat2x2、mat4x4)和纹理类型。

WGSL的函数分为入口函数和辅助函数两类。入口函数使用特定属性标注,如vertex、fragment或compute,分别对应顶点着色器、片元着色器和计算着色器的入口点。统一变量通过uniform块声明,存储缓冲区通过storage块声明,工作组内存通过workgroup关键字声明。

三、安装与部署

3.1 浏览器支持与启用方式

截至2025年3月,WebGPU已在主流浏览器中获得良好支持。Chrome浏览器从113版本开始默认启用WebGPU,无需额外配置;Safari在macOS 14和iOS 17版本中提供了WebGPU支持;Firefox通过about:config中的dom.webgpu.enabled选项启用WebGPU支持。需要注意的是,使用WebGPU需要确保浏览器更新到最新版本。

在开发环境方面,WebGPU完全运行在浏览器中,不需要额外的SDK或工具链。开发者只需要一个支持WebGPU的现代浏览器即可开始开发。为了获得最佳开发体验,推荐使用Chrome或Edge浏览器,它们的WebGPU实现最为完善,且开发者工具提供了WebGPU调试支持。

3.2 开发环境检测

在编写WebGPU代码前,首先需要检测浏览器是否支持WebGPU。以下是检测代码:

javascript 复制代码
async function checkWebGPUSupport() {
    if (!navigator.gpu) {
        console.error('浏览器不支持WebGPU API');
        return false;
    }
    
    const adapter = await navigator.gpu.requestAdapter();
    if (!adapter) {
        console.error('无法获取GPU适配器');
        return false;
    }
    
    const device = await adapter.requestDevice();
    console.log('WebGPU支持正常,设备已获取');
    return true;
}

这段代码首先检查navigator.gpu是否存在,然后尝试请求GPU适配器,最后请求逻辑设备。如果任何一步失败,都会输出相应的错误信息。在实际应用中,建议在WebGPU不可用时提供降级方案,如回退到WebGL渲染。

3.3 基础代码结构

一个完整的WebGPU渲染程序通常包含以下步骤:首先获取适配器和设备,然后创建着色器模块,接着定义渲染管线和资源绑定,最后编码和提交命令缓冲区。以下是一个简化的示例:

javascript 复制代码
// 获取GPU适配器和设备
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

// 创建着色器模块
const shaderModule = device.createShaderModule({
    code: `
        @vertex
        fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4f {
            const positions = array<vec2f, 3>(
                vec2f(0.0, 0.5),
                vec2f(-0.5, -0.5),
                vec2f(0.5, -0.5)
            );
            return vec4f(positions[vertexIndex], 0.0, 1.0);
        }
        
        @fragment
        fn fragmentMain() -> @location(0) vec4f {
            return vec4f(1.0, 0.0, 0.0, 1.0);
        }
    `
});

// 创建渲染管线
const pipeline = device.createRenderPipeline({
    layout: 'auto',
    vertex: {
        module: shaderModule,
        entryPoint: 'vertexMain'
    },
    fragment: {
        module: shaderModule,
        entryPoint: 'fragmentMain',
        targets: [{ format: 'bgra8unorm' }]
    },
    primitive: {
        topology: 'triangle-list'
    }
});

// 获取canvas上下文并配置
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({ device, format, alphaMode: 'premultiplied' });

// 创建命令编码器并提交渲染命令
function frame() {
    const commandEncoder = device.createCommandEncoder();
    const textureView = context.getCurrentTexture().createView();
    
    const renderPass = commandEncoder.beginRenderPass({
        colorAttachments: [{
            view: textureView,
            clearValue: { r: 0, g: 0, b: 0, a: 1 },
            loadOp: 'clear',
            storeOp: 'store'
        }]
    });
    
    renderPass.setPipeline(pipeline);
    renderPass.draw(3);
    renderPass.end();
    
    device.queue.submit([commandEncoder.finish()]);
    requestAnimationFrame(frame);
}

frame();

这段代码创建了一个简单的三角形渲染程序,展示了WebGPU编程的基本流程。从代码量来看,WebGPU比WebGL更加简洁,开发者无需编写冗长的状态设置代码。

四、WebGPU的优势

4.1 显著的性能提升

WebGPU相比WebGL在性能方面有显著提升,主要体现在以下几个方面:首先,WebGPU的驱动层开销更低,它采用现代GPU API的设计理念,避免了WebGL中不必要的抽象层;其次,WebGPU支持更高效的内存管理,开发者可以直接控制GPU内存的分配和释放,减少内存分配带来的性能开销;第三,WebGPU提供了更细粒度的管线状态控制,减少了状态切换带来的性能损耗。

根据官方测试数据,WebGPU在典型工作负载下比WebGL快20%到50%。在一些特定场景下,如大规模粒子系统渲染和复杂材质处理,性能提升更加明显。此外,WebGPU的计算着色器在机器学习推理任务中展现出优异性能,相比纯JavaScript实现可获得3倍以上的加速效果。

4.2 强大的计算着色器支持

计算着色器是WebGPU区别于WebGL的核心特性之一。它允许开发者在GPU上执行通用并行计算任务,而不仅限于图形渲染。这一特性为Web应用开辟了全新的应用领域:图像处理滤镜可以完全在GPU上执行,实现实时处理;物理模拟可以使用计算着色器模拟流体、粒子和布料效果;机器学习推理可以在浏览器中利用GPU加速神经网络的前向传播。

计算着色器的编程模型基于工作组概念。开发者可以将计算任务划分为多个工作组,每个工作组内的线程可以共享workgroup内存并进行同步。这种编程模型与现代GPU架构高度契合,能够充分发挥GPU的并行计算能力。

4.3 多线程支持

WebGPU原生支持多线程渲染,这是WebGL无法实现的重要特性。在WebGL中,所有GPU命令必须在主线程中提交,这可能导致复杂场景渲染时主线程阻塞,影响用户体验。WebGPU通过Web Workers支持多线程,开发者可以在后台线程中创建命令缓冲区,然后在主线程中提交执行。

多线程支持对于复杂应用特别重要。例如,在游戏开发中,可以在后台线程预处理场景数据、构建命令缓冲区,主线程仅负责提交最终命令,从而保持流畅的帧率。数据可视化应用也可以利用多线程并行处理多个数据集的渲染命令。

4.4 现代API设计与开发体验

WebGPU的API设计更加现代和一致。相比WebGL需要调用大量函数来设置渲染状态,WebGPU采用声明式的渲染管线描述方式,开发者可以在创建管线时一次性指定所有状态,运行时无需反复设置。WebGPU还提供了更好的错误处理机制,每个API调用都会返回清晰的错误信息,便于开发者定位问题。

WGSL着色器语言也提升了开发体验。它提供了比GLSL更清晰的语法和更强的类型系统,减少了着色器编程中的常见错误。WGSL的内存模型更加安全,能够在编译时检测出一些潜在的内存访问问题。

4.5 跨平台一致性

WebGPU作为Web标准,实现了出色的跨平台一致性。开发者编写一次代码,即可在Windows、macOS、Linux、ChromeOS、Android和iOS等平台上运行。WebGPU底层适配了不同的原生图形API(DirectX 12、Vulkan、Metal),开发者无需关心底层差异。

这种跨平台能力对于需要覆盖多端的应用尤为重要。WebGPU使得在浏览器中开发高质量3D应用成为可能,这些应用可以无缝运行在桌面和移动设备上,降低了开发成本和维护成本。

五、劣势与挑战

5.1 浏览器兼容性问题

尽管主流浏览器已支持WebGPU,但覆盖率仍不及WebGL。根据Can I Use的数据,WebGPU的全球支持率约为70%左右,落后于WebGL的98%。部分用户仍在使用旧版本浏览器,这些用户无法体验WebGPU带来的性能优势。

在浏览器版本方面,Chrome 113以下版本、Safari 17以下版本以及未启用WebGPU标志的Firefox都无法使用WebGPU。移动端浏览器的支持情况更加参差不齐,Android Chrome和Safari iOS虽然支持WebGPU,但部分功能可能存在差异。开发者需要准备WebGL降级方案,以确保在不支持WebGPU的浏览器上应用仍能正常运行。

5.2 学习曲线较陡

WebGPU虽然API设计更加现代,但对于从WebGL迁移的开发者来说,学习曲线仍然较陡。WebGL的编程模式基于全局状态机,开发者可以逐步设置状态并立即看到效果;WebGPU则要求开发者一次性定义完整的渲染管线,需要对GPU渲染流程有更深入的理解。

计算着色器和多线程编程对大多数Web开发者来说是全新的领域。编写高效的GPU代码需要理解并行计算模型、工作组组织、内存访问模式等概念,这些知识通常只有图形程序员或高性能计算开发者才具备。建议开发者在学习WebGPU之前,先了解现代GPU架构和并行编程基础。

5.3 生态与工具链成熟度

WebGPU生态系统的成熟度还需要时间积累。虽然Three.js、Babylon.js等主流3D框架已开始支持WebGPU,但WebGL生态中的丰富资源和工具(如各种效果插件、调试工具)在WebGPU中还不够完善。一些WebGL中常见的渲染技术在WebGPU中需要重新实现或寻找替代方案。

在调试工具方面,Chrome开发者工具提供了WebGPU调试支持,但功能相比WebGL调试还不够丰富。第三方工具和可视化库也在逐步完善中。对于生产级应用,开发者可能需要自行构建一些基础设施,如性能监控工具和资源加载系统。

5.4 碎片化风险

WebGPU规范的不同实现之间可能存在细微差异,这可能导致"在我机器上能运行"的问题。例如,不同浏览器对某些可选特性的支持程度可能不同,着色器编译行为可能存在微小差异。开发者需要在多个浏览器和设备上进行充分测试,确保应用的行为一致性。

此外,WebGPU的不同版本之间也存在API变化。虽然W3C努力保持API的稳定性,但在WebGPU成熟的过程中,部分API可能会进行调整。开发者需要关注规范的演进,及时更新代码以适配新版本。

六、与竞品对比

6.1 WebGPU与WebGL

WebGL是WebGPU最直接的 predecessor,两者的对比有助于理解WebGPU的革新之处。在架构设计上,WebGL基于OpenGL ES的"即时模式",每次绘制调用都需要重新设置相关状态;WebGPU采用声明式管线描述,状态在创建时固定,运行时更加高效。

在功能方面,WebGL仅支持图形渲染,缺乏计算着色器支持;WebGPU同时支持图形渲染和通用计算,可以执行复杂的并行计算任务。多线程支持是另一个重要差异:WebGL完全依赖主线程,WebGPU可以利用Web Workers在后台线程处理渲染命令。

性能方面,WebGPU相比WebGL有显著提升。根据Google的测试数据,WebGPU在复杂场景渲染中比WebGL快20%到50%,在机器学习推理任务中可达到3倍以上的加速。然而,WebGL拥有更好的浏览器兼容性,几乎所有现代浏览器都支持WebGL,而WebGPU的支持率约为70%。

6.2 WebGPU与原生GPU API

WebGPU与Vulkan、Metal、DirectX 12等原生GPU API相比,主要区别在于抽象层级和目标平台。原生API直接操作GPU硬件,提供极致的性能和底层控制,适合对性能有极致要求的桌面和移动应用;WebGPU运行在浏览器沙箱中,受限于Web安全模型,提供了相对较高层的抽象。

WebGPU的优势在于跨平台一致性和开发效率。编写一份WebGPU代码,即可在Windows、macOS、Linux等桌面平台以及移动端浏览器中运行。原生API则需要分别为不同平台编写适配代码。此外,WebGPU简化了资源管理和内存分配,降低了编程复杂度。

然而,在性能敏感的应用场景中,原生API仍然是首选。对于需要极致画质和性能的3A游戏、专业级图形应用,原生API能够提供更精细的控制和更高的执行效率。WebGPU的性能虽然优于WebGL,但与最优化的原生代码相比仍有一定差距。

6.3 WebGPU与WebGPU以外的选择

除了WebGPU,Web开发者还有其他图形API选择。对于需要兼容旧版浏览器的应用,WebGL仍然是最佳选择,其98%的全球支持率几乎可以覆盖所有用户。对于简单的2D图形需求,Canvas API提供了更简单的编程接口。

WebGPU之外,新兴的Web标准也在发展。WebNeuralNetwork API致力于为Web提供硬件加速的机器学习能力,可与WebGPU互补;WebXR API专注于虚拟现实和增强现实体验。这些API可以与WebGPU结合使用,构建更丰富的Web应用。

七、应用场景与实践案例

7.1 游戏开发

WebGPU为浏览器游戏开发带来了新的可能性。其高性能渲染能力使得在Web端开发3A级别画质游戏成为可能。计算着色器允许实现复杂的物理效果,如流体模拟、粒子系统和布料模拟。多线程支持确保了游戏在复杂场景下仍能保持流畅的帧率。

目前,已有多款基于WebGPU的浏览器游戏发布,展示了其强大的图形能力。这些游戏充分利用了WebGPU的现代渲染特性,实现了高质量的视觉效果,同时保持了良好的性能表现。对于独立游戏开发者和小型团队,WebGPU使得Web成为与原生平台同等重要的游戏发布目标平台。

7.2 数据可视化

在数据可视化领域,WebGPU可以处理大规模数据的实时渲染任务。对于需要同时展示数百万数据点的可视化应用,WebGPU的并行渲染能力可以显著提升性能。开发者可以利用计算着色器进行数据预处理,在GPU上完成聚类、过滤等计算任务。

交互式数据可视化是另一个重要应用场景。WebGPU支持多窗口和多画布渲染,可以实现复杂的数据探索界面。用户可以同时操作多个视图,每个视图独立渲染而不会相互干扰,这种能力在WebGL中实现较为困难。

7.3 机器学习推理

浏览器端机器学习推理是WebGPU的重要应用方向。TensorFlow.js已经支持使用WebGPU加速模型推理,在支持的硬件上可以获得相比WebGL 20到30倍的性能提升。这意味着可以在浏览器中运行更复杂的神经网络模型,实现实时图像识别、自然语言处理等AI功能。

端侧AI推理具有隐私保护和低延迟的优势。用户数据无需上传到服务器,在本地设备上即可完成处理,既保护了隐私,又降低了服务器成本。WebGPU使得这些能力可以在Web端实现,拓宽了端侧AI的应用范围。

7.4 图像与视频处理

WebGPU的计算着色器特别适合图像和视频处理任务。实时滤镜、视频特效、图像增强等功能可以完全在GPU上执行,无需服务器参与。这对于视频会议、直播、图像编辑等应用具有重要意义。

WebGPU还支持高效的视频编解码处理。结合WebCodecs API,开发者可以实现硬件加速的视频解码和编码,利用GPU进行视频处理管道中的各个环节。这种能力使得在浏览器中开发专业级视频编辑应用成为可能。

7.5 虚拟现实与增强现实

WebXR API与WebGPU的结合为Web端的沉浸式体验提供了技术基础。WebGPU的高性能渲染能力可以满足VR/AR应用对帧率和画质的要求。多线程支持确保了在处理用户输入和渲染复杂场景时保持低延迟。

目前,已有实验性的WebXR应用开始探索WebGPU的潜力。虽然WebXR生态还需要进一步完善,但WebGPU为未来的Web端沉浸式应用奠定了技术基础。

八、框架支持与生态发展

8.1 主流3D框架的WebGPU支持

主流3D框架已经开始全面支持WebGPU。Three.js从版本152开始引入WebGPU渲染器,提供与原有WebGL渲染器兼容的API,让现有项目可以渐进式迁移到WebGPU。Babylon.js在6.0版本中引入了WebGPU引擎,提供了更底层的WebGPU访问能力。

PlayCanvas和Engine.js等框架也在积极支持WebGPU。这些框架的WebGPU支持使得开发者可以在不了解底层API细节的情况下,利用WebGPU的高性能特性。框架通常会提供自动降级机制,在不支持WebGPU的环境下回退到WebGL。

8.2 机器学习框架的集成

TensorFlow.js是最早支持WebGPU的机器学习框架之一。其WebGPU后端利用WebGPU的计算着色器加速张量运算,在支持的设备上可以显著提升模型推理速度。对于常用的模型架构,如卷积神经网络和Transformer,WebGPU后端都提供了优化实现。

ONNX Runtime Web也支持WebGPU加速,使得在浏览器中运行ONNX格式的模型成为可能。开发者可以训练模型并导出为ONNX格式,然后在Web端利用WebGPU进行高性能推理。这种工作流程为端侧AI应用提供了灵活的部署方案。

8.3 工具与调试支持

Chrome开发者工具提供了WebGPU调试支持,包括管线状态检查、着色器编译错误查看、帧捕获和分析等功能。开发者可以检查渲染管线的配置,追踪GPU命令的执行,识别性能瓶颈。

WebGPU验证层(Validation Layer)在开发构建中默认开启,可以在API调用时检测错误并提供清晰的错误信息。这大大加速了开发阶段的问题定位。开发者还可以利用WGSL的着色器验证功能,在编译时检测着色器代码的问题。

九、迁移指南与最佳实践

9.1 从WebGL迁移到WebGPU

对于已有WebGL项目的开发者,迁移到WebGPU需要一定的工作量。以下是迁移的主要步骤:首先,需要添加WebGPU能力检测和降级逻辑,确保在不支持WebGPU的浏览器上仍能正常工作;其次,需要将着色器代码从GLSL转换为WGSL;第三,需要重新组织资源管理和管线创建逻辑;最后,需要调整渲染命令的编码方式。

建议采用渐进式迁移策略。可以先在关键渲染路径上引入WebGPU,同时保留WebGL作为降级方案。随着WebGPU生态的成熟和浏览器覆盖率的提升,逐步增加WebGPU的使用比例。

9.2 性能优化技巧

充分发挥WebGPU的性能优势需要注意以下几点:在资源创建时尽量预分配所需的缓冲区,避免运行时频繁分配;合理组织绑定组布局,减少管线切换开销;利用渲染传递(Render Pass)的子通道(Subpass)优化依赖渲染;使用异步操作处理资源加载和命令提交,避免阻塞主线程。

计算着色器的优化需要关注内存访问模式。尽量使用合并的内存访问,避免随机的分散-聚集操作。合理使用workgroup内存,减少对全局内存的访问。根据GPU的SIMD宽度组织工作组大小,可以获得更好的执行效率。

9.3 错误处理与降级策略

稳健的WebGPU应用需要完善的错误处理机制。首先,应用启动时应检测WebGPU支持情况,提供友好的降级提示;其次,在API调用失败时应捕获错误信息,记录日志用于调试;第三,应设置定时器检测GPU设备是否被丢失,并在设备丢失时尝试恢复。

降级策略的实现可以使用Feature Detection模式:定义需要使用的WebGPU特性集合,检测适配器是否支持所有必需特性,如果不支持则回退到WebGL。这种方式可以确保应用在各种环境下都能正常运行。

十、总结与展望

WebGPU代表了Web图形技术的未来发展方向。它通过现代化的API设计、强大的计算能力和高效的多线程支持,为Web开发者提供了接近原生性能的GPU编程能力。从游戏开发到数据可视化,从机器学习到虚拟现实,WebGPU正在开启Web图形应用的新篇章。

然而,WebGPU的发展也面临挑战。浏览器兼容性的提升、开发者生态的培养、工具链的完善都需要时间。在过渡期内,WebGL仍将保持重要地位,开发者需要同时维护两套渲染路径。随着浏览器更新的推进和WebGPU技术的成熟,我们有理由相信WebGPU将成为Web图形的新标准。

对于Web开发者而言,现在正是学习和布局WebGPU的最佳时机。早期采用者可以积累技术优势,在未来的竞争中占据有利位置。建议开发者从简单的示例开始,逐步深入理解WebGPU的设计理念和编程模型,为即将到来的Web图形新时代做好准备。

参考资料

  1. WebGPU官方文档
  2. Chrome WebGPU开发者文档
  3. WebGPU MDN Web文档
  4. Three.js WebGPU渲染器文档
  5. WebGPU基础入门教程
  6. WGSL着色器语言规范
相关推荐
捕捉一只前端小白2 小时前
cpolar内网穿透以及微信小程序域名设置
前端·vue.js·微信小程序·小程序
wuhen_n2 小时前
ESLint + Prettier + Husky + lint-staged:建立自动化的高效前端工作流
前端·javascript·vue.js
小同志002 小时前
HTML 基础
前端·javascript·html
wuhen_n4 小时前
网络请求在Vite层的代理与Mock:告别跨域和后端依赖
前端·javascript·vue.js
用户69371750013848 小时前
Google 正在“收紧侧加载”:陌生 APK 安装或需等待 24 小时
android·前端
蓝帆傲亦9 小时前
Web 前端搜索文字高亮实现方法汇总
前端
用户69371750013849 小时前
Room 3.0:这次不是升级,是重来
android·前端·google