deb包构建详解
deb包构建详解
- 一、deb包构建流程
- 二、deb包构建描述文件详解
- 2.1 control文件
- 2.2 `postinst 文件` (post-installation script)
- 2.3 `postrm 文件` (post-removal script)
- 2.4 `prerm 文件` (pre-removal script)
- 2.5 `preinst 文件` (pre-installation script)
- 2.6 rules 文件
- 2.7 changelog 文件
- 三、常见问题
- 1. 错误信息:dpkg-shlibdeps 报错
- 2. 错误信息:dpkg-gencontrol: error: current build architecture is not included**
- 3. 错误信息:dpkg-source: error: aborting due to unexpected upstream changes**
- 4. 错误信息:dh: error: unable to load addon <addon-name>: Can't locate <addon-name>.pm in @INC
- 5. 错误信息:dpkg-deb: building package 'package-name' in '/path/to/package-name.deb'.
- 6. 错误信息:dpkg-source: error: source package has two conflicting values - <field> in <filename> and <filename>
- 7. 错误信息:dpkg-source: error: cannot represent change to <file> as it is not in the patch's header
- 8. 错误信息:E: Sub-process /usr/bin/dpkg returned an error code (1)
- 9. 错误信息:dh: unable to load addon <addon-name>: Can't locate <addon-name>.pm in @INC
- 10. 错误信息:dpkg-deb: error: control directory has bad permissions
当你需要在Debian或Ubuntu等基于Debian的Linux系统上分发你的应用程序时,你可以使用Deb包(.deb文件)进行构建和分发。Deb包是一种用于Debian系统的软件包格式,它包含了应用程序的二进制文件、配置文件、依赖关系等信息。在本文中,我将为你介绍如何构建Deb包,并提供一个基本的构建流程。
一、deb包构建流程
- 步骤1:安装必要的工具
在开始之前,确保你的系统已经安装了以下必要的工具:
sudo apt-get update
sudo apt-get install dh-make dpkg-dev build-essential
- 步骤2:创建项目目录结构
在你的项目目录下,创建一个用于构建Deb包的目录结构:
mkdir debian-package
cd debian-package
- 步骤3:创建Deb包描述文件
在debian-package目录下,创建一个DEBIAN目录,并在其中创建一个control文件,用于描述Deb包的信息:
Package: your-package-name
Version: 1.0
Architecture: all
Maintainer: Your Name <your@email.com>
Description: Your package description
- 步骤4:将应用程序拷贝到Deb包目录中
将你的应用程序文件(可执行文件、配置文件等)拷贝到debian-package目录中。
- 步骤5:构建Deb包
在debian-package目录下,执行以下命令构建Deb包:
dpkg-deb --build . your-package-name_1.0_all.deb
这将在当前目录下生成一个名为your-package-name_1.0_all.deb的Deb包文件。
- 步骤6:测试Deb包
你可以在本地测试你的Deb包,确保它能够正常安装和运行:
sudo dpkg -i your-package-name_1.0_all.deb
- 步骤7:分发你的Deb包
你可以将生成的Deb包上传到你的网站、软件仓库,或者通过邮件等方式分发给用户。
以上是一个简单的Deb包构建流程,你可以根据你的应用程序的复杂度和需求进一步定制和优化构建过程。
二、deb包构建描述文件详解
2.1 control文件
在Debian的软件包中,control 文件是非常重要的,它包含了软件包的描述信息,如软件包的名称、版本、依赖关系、描述等。下面是一个 control 文件的详细说明:
-
-
Package:
软件包的名称。这个字段必须是唯一的,它指定了Deb包的名称。
在Debian和Ubuntu的软件包管理中,Package字段(软件包的名称)是有一些命名规范和限制的。具体来说:-
只能包含小写字母、数字、加号(+)、减号(-)和点号(.): 字母必须是小写的,不可以包含大写字母。
-
不能包含空格和特殊字符: 不能包含空格、下划线、斜杠、反斜杠等特殊字符。
-
长度限制: 一般情况下,软件包的名称应该尽量短小,通常不应该超过50个字符。
-
应该具有唯一性: 在软件包仓库中,软件包名称必须是唯一的,不可与其他软件包重名。
遵循这些限制和规范可以确保软件包名称的唯一性,并且避免与系统的其他软件包发生冲突。在创建
control文件时,请确保软件包名称满足上述规定。 -
-
-
- Version:
软件包的版本号。通常遵循major.minor.patch的格式。例如:1.0.0
- Version:
-
- Architecture:
软件包适用的体系结构。常见的取值有i386、amd64、all(表示通用的,与体系结构无关的软件包)。
- Architecture:
-
- Maintainer:
维护者的信息,包括姓名和邮箱。例如:Maintainer: John Doe <johndoe@example.com>
- Maintainer:
-
- Description:
软件包的描述信息。这个字段通常包括软件包的简要介绍、功能特性、用法等。描述信息可能包括多行文本,每行以点号(.`)开头,例如:
Description: This is a sample package.. It demonstrates how to create a Debian package. for educational purposes. - Description:
-
- Depends:
软件包的依赖关系。用于指定安装这个软件包需要满足的其他软件包。例如:
Depends: libc6 (>= 2.14), libqt5core5a (>= 5.0.1) - Depends:
这表示该软件包依赖于 libc6 版本大于等于 2.14 和 libqt5core5a 版本大于等于 5.0.1。
-
- Homepage:
软件包的官方网站。例如:Homepage: https://example.com
- Homepage:
-
- Section:
软件包所属的分类。例如:Section: utils
- Section:
-
- Priority:
软件包的优先级。常见的取值包括required(必需的软件包)和optional(可选的软件包)。
- Priority:
-
- Built-Using:
用于指定软件包编译时所使用的其他软件包。例如:Built-Using: gcc (>= 4.8)
- Built-Using:
-
- Pre-Depends:
软件包的前置依赖关系。与Depends类似,但Pre-Depends的软件包在安装前就必须满足。
这些是
control文件中常见字段的说明。在构建Deb包时,你需要根据你的软件包的需求,适当地填写这些字段。确保control文件的格式正确,以便Deb包能够被正确解析和处理。 - Pre-Depends:
除了 control 文件,Deb包构建中通常还包含其他一些重要的文件,每个文件都有不同的作用。以下是这些文件的详细说明:
以下是一个简单的control文件的示例,展示了基本的结构和字段。请根据你的软件包的需求修改这些字段,确保它们符合你的软件包的信息。
Package: example-package
Version: 1.0.0
Architecture: amd64
Maintainer: John Doe <johndoe@example.com>
Description: This is an example package.This package demonstrates how to create a Debian packagefor educational purposes.
Homepage: https://example.com
Depends: libc6 (>= 2.14), libqt5core5a (>= 5.0.1)
Section: utils
Priority: optional
2.2 postinst 文件 (post-installation script)
postinst 是安装软件包后执行的脚本。在这个脚本中,你可以进行一些安装后的配置、服务启动等操作。执行该脚本使用root用户权限执行。
2.3 postrm 文件 (post-removal script)
postrm 是移除软件包后执行的脚本。你可以在这个脚本中进行一些清理操作,例如删除配置文件、停止服务等。执行该脚本使用root用户权限执行。
2.4 prerm 文件 (pre-removal script)
prerm 是移除软件包前执行的脚本。在这个脚本中,你可以进行一些准备工作,例如停止服务、备份数据等。执行该脚本使用root用户权限执行。
2.5 preinst 文件 (pre-installation script)
preinst 是安装软件包前执行的脚本。在这个脚本中,你可以进行一些预处理,例如检查依赖关系、备份数据等。执行该脚本使用root用户权限执行。
2.6 rules 文件
rules 文件包含了构建软件包时的构建规则。这个文件通常用于告诉构建系统如何编译和打包软件。
rules 文件是用来指定在构建Deb软件包时的构建规则的文件。这个文件使用make规则的语法,并且通常是根据软件包的需要进行定制的。下面是一个简单的rules文件的示例,展示了基本的结构和语法:
#!/usr/bin/make -f%:dh $@override_dh_auto_build:# 在这里添加构建软件包的命令qmakemakeoverride_dh_auto_install:# 在这里添加安装软件包的命令make INSTALL_ROOT=$(CURDIR)/debian/tmp installoverride_dh_auto_clean:# 在这里添加清理操作的命令make distclean
在这个示例中,%: 指定了默认的目标,dh $@表示使用Deb Helper工具进行构建。override_dh_auto_build、override_dh_auto_install 和 override_dh_auto_clean 是Deb Helper工具中预定义的目标,你可以在这些目标中添加相应的构建、安装和清理命令。
具体来说:
-
override_dh_auto_build目标用于执行构建软件包的命令。在这个例子中,使用了qmake和make来构建项目。 -
override_dh_auto_install目标用于执行安装软件包的命令。在这个例子中,使用了make INSTALL_ROOT=$(CURDIR)/debian/tmp install来安装软件包。 -
override_dh_auto_clean目标用于执行清理操作的命令。在这个例子中,使用了make distclean来进行清理。
需要注意的是,rules 文件的内容根据你的项目的构建系统和需要进行定制。如果你的项目使用了CMake,那么相应的构建命令和路径也会不同。请根据你的项目具体情况来修改rules 文件中的命令和路径,以确保Deb软件包能够正确构建。
2.7 changelog 文件
changelog 文件用于记录软件包的版本历史和变更信息。它是一个Debian软件包的重要部分,包含了软件包的版本号、发布日期、作者和变更日志。以下是changelog`文件的基本格式和创建方法:
-
*Changelog 格式:
每一个版本的变更记录通常按照以下格式组织:
your-package-name (version) distribution; urgency=urgency* Changes here...-- Maintainer Name <maintainer@example.com> Fri, 01 Jan 2021 12:00:00 +0000your-package-name: 你的软件包的名称。version: 软件包的版本号。distribution: 软件包支持的发行版(如stable、unstable等)。urgency: 变更的紧急程度(可选,通常是low、medium或high)。Changes here...: 该版本的变更日志。
-
Changelog 示例:
example-package (1.0.0-1) stable; urgency=medium* Initial release.* Added feature A.* Fixed issue with feature B.-- John Doe <johndoe@example.com> Fri, 01 Jan 2021 12:00:00 +0000 -
创建 Changelog 文件:
你可以使用文本编辑器手动创建或编辑
changelog文件,确保按照上述格式编写变更记录。另外,还有一些工具可以自动生成changelog文件,比如dch(Debian ChangeLog Helper)工具。如果你的系统上没有安装
dch工具,可以通过以下命令安装:sudo apt-get update sudo apt-get install devscripts然后,使用以下命令在软件包的源代码目录中生成一个新的
changelog文件或编辑现有的changelog文件:dch -i这个命令会打开一个编辑器,让你输入变更记录。完成后,保存并关闭编辑器,
dch工具会为你自动生成合适格式的changelog文件。
三、常见问题
在构建Deb包时,可能会遇到各种各样的错误。以下是一些常见的错误信息以及可能的处理方法:
1. 错误信息:dpkg-shlibdeps 报错
dpkg-shlibdeps: error: no dependency information found for /path/to/library
处理方法:
这个错误通常表示在Deb包的控制文件(control文件)中缺少必要的依赖信息。请检查Depends字段,确保列出了所有需要的依赖库。
2. 错误信息:dpkg-gencontrol: error: current build architecture is not included**
dpkg-gencontrol: error: current build architecture is not included
处理方法:
这个错误通常是由于在control文件的Architecture字段中指定了一个不支持的架构。请确保Architecture字段的值正确,符合当前系统的架构(如amd64、i386等)。
3. 错误信息:dpkg-source: error: aborting due to unexpected upstream changes**
dpkg-source: error: aborting due to unexpected upstream changes
处理方法:
这个错误通常是由于在构建Deb包前,源代码目录中存在未提交的变更。确保源代码目录是干净的,没有未提交的更改,然后再尝试构建Deb包。
4. 错误信息:dh: error: unable to load addon : Can’t locate .pm in @INC
dh: error: unable to load addon <addon-name>: Can't locate <addon-name>.pm in @INC
处理方法:
这个错误通常是由于dh工具无法找到指定的插件。请检查你的debian/rules文件,确保插件的名称和路径正确。如果你使用了自定义的插件,请确保它们被正确安装并且在debian/rules文件中被正确引用。
5. 错误信息:dpkg-deb: building package ‘package-name’ in ‘/path/to/package-name.deb’.
dpkg-deb: building package 'package-name' in '/path/to/package-name.deb'.
dpkg-deb: error: failed to read package info file '/path/to/package-name/DEBIAN/control': Is a directory
处理方法:
这个错误通常是由于构建Deb包时指定的路径错误。请确保你在正确的目录下执行构建命令,并且debian目录下包含了正确的control文件。
6. 错误信息:dpkg-source: error: source package has two conflicting values - in and
dpkg-source: error: source package has two conflicting values - <field> in <filename> and <filename>
处理方法:
这个错误通常是由于在Debian源码包中的不同文件中存在冲突的字段值。请检查并修复源代码包中的相关文件,确保所有的字段值一致。
7. 错误信息:dpkg-source: error: cannot represent change to as it is not in the patch’s header
dpkg-source: error: cannot represent change to <file> as it is not in the patch's header
处理方法:
这个错误通常是由于patch文件中的内容与实际文件不匹配。请检查并更新patch文件,确保其中的变更与实际文件内容一致。
8. 错误信息:E: Sub-process /usr/bin/dpkg returned an error code (1)
E: Sub-process /usr/bin/dpkg returned an error code (1)
处理方法:
这个错误通常是由于前面某个步骤的错误导致后续步骤无法执行。请仔细检查之前的错误信息,找出根本原因,并按照前述的方法逐个解决错误。
9. 错误信息:dh: unable to load addon : Can’t locate .pm in @INC
dh: unable to load addon <addon-name>: Can't locate <addon-name>.pm in @INC
处理方法:
这个错误通常是由于构建系统无法找到指定的插件。请检查你的debian/rules文件,确保插件的名称和路径正确。如果使用了自定义的插件,请确保它们被正确安装并在debian/rules文件中正确引用。
10. 错误信息:dpkg-deb: error: control directory has bad permissions
dpkg-deb: error: control directory has bad permissions
处理方法:
这个错误通常是由于debian/DEBIAN目录的权限设置问题。请确保debian/DEBIAN目录及其下的文件具有正确的权限(通常为755)。你可以使用以下命令修复权限问题:
chmod 755 debian/DEBIAN
在处理以上错误时,要仔细阅读错误信息,了解具体的错误原因。然后,根据错误信息的内容,检查相应的配置文件,确保配置正确。如果需要,可以查阅相关文档、社区讨论等资源,以获取更多的帮助。
在处理以上错误时,首先要仔细阅读错误信息,了解具体的错误原因。然后,根据错误信息的内容,检查相应的配置文件(如control文件、debian/rules文件等),确保配置正确。在调试过程中,还可以查阅相关文档、社区讨论等资源,以获取更多的帮助。
相关文章:
deb包构建详解
deb包构建详解 一、deb包构建流程二、deb包构建描述文件详解2.1 control文件2.2 postinst 文件 (post-installation script)2.3 postrm 文件 (post-removal script)2.4 prerm 文件 (pre-removal script)2.5 preinst 文件 (pre-installation script)2.6 rules 文件2.7 changelog…...
【Spring Cloud】网关Gateway的请求过滤工厂RequestRateLimiterGatewayFilterFactory
概念 关于微服务网关Gateway中有几十种过滤工厂,这一篇博文记录的是关于请求限流过滤工厂,也就是标题中的RequestRateLimiterGatewayFilterFactory。这个路由过滤工厂是用来判断当前请求是否应该被处理,如果不会被处理就会返回HTTP状态码为42…...
自己写spring boot starter问题总结
1. Unable to find main class 创建spring boot项目写自己的starterxi写完之后使用install出现Unable to find main class,这是因为spring boot打包需要一个启动类,按照以下写法就没事 <plugins><plugin><groupId>org.springframewo…...
vue3如何打开页面即向后端发送请求
目录 背景: 实现: 1、使用 2、案例 补充: 1、如何定义一个集合来接受后端返回的list 2、加入请求头 背景: 如果想在页面刚加载时向后端发送请求,可以使用Vue 3的生命周期钩子函数onMounted来实现 实现ÿ…...
【软考】9.2 串/数组/矩阵/广义表/树
《字符串》 一种特殊的线性表,数据元素都为字符模式匹配:寻找子串第一次在主串出现的位置 模式匹配算法 1. 暴力破解法(布鲁特-福斯算法) 主串与子串一个个匹配效率低 2. KMP算法 主串后缀和子串前缀能否找到一样的元素…...
大数据 DataX 数据同步数据分析入门
目录 一、DataX 概览 1.1 DataX 是什么 1.2 DataX 3.0 概览 设计理念 当前使用现状 二、DataX 详解 2.1 DataX 3.0 框架设计 2.2 DataX 3.0 插件体系 2.3 DataX 3.0 核心架构 2.3.1 核心模块介绍 2.3.2 DataX 调度流程 2.4 DataX 3.0 的六大核心优势 2.4.1 可靠的…...
【京东开源项目】微前端框架MicroApp 1.0正式发布
介绍 MicroApp是由京东前端团队推出的一款微前端框架,它从组件化的思维,基于类WebComponent进行微前端的渲染,旨在降低上手难度、提升工作效率。MicroApp无关技术栈,也不和业务绑定,可以用于任何前端框架。 源码地址…...
多个子div在父中垂直居中
在一个div下,有多个子div,且子div都是水平垂直居中 <template><div><div class"far"><!-- 注意需要多包裹一层 --><div><div class"son1">1</div><div class"son2">222…...
[C国演义] 第十五章
第十五章 最长湍流子数组环绕字符串中唯⼀的⼦字符串 最长湍流子数组 力扣链接 子数组 ⇒ dp[i]的含义: 以arr[i] 结尾的所有子数组中的最长湍流子数组的长度 子数组 ⇒ 状态转移方程根据 最后一个位置来划分👇👇👇 初始化: 都初始化为…...
Docker Compose和Consul
目录 Docker-compose Docker-compose 简介 YAML 文件格式及编写注意事项 Docker Compose配置常用字段 Docker Compose 常用命令 Docker Compose 文件结构 compose 部署 Docker Compose 环境安装 准备依赖文件 编写配置文件docker-compose.yml Consul consul 部署 c…...
Wireshark新手小白基础使用方法
一、针对IP抓取 1、过滤格式: (1)、ip.src eq x.x.x.x (2)、ip.dst eq x.x.x.x (3)ip.src eq x.x.x.x or ip.dst eq x.x.x.x 二、针对端口过滤 1、过滤格式: (1&a…...
互动设计:深入了解用户体验的关键
交互是人与计算机系统之间的互动过程。在计算机领域中,交互是人机交互技术的核心内容之一。交互设计是一种基于人类行为科学、心理学、人体工程学等领域的专业设计,目的是创造用户友好的、易于使用的计算机软件、网络、移动应用等。交互的本质在于用户的…...
maven的坐标元素
maven的坐标:使用三个向量在Maven仓库中唯一的定位到一个jar包 * groupId:公司或组织的ID * artifactId:一个项目或者是项目中的一个模块的ID * version:版本号 <groupId>com.gz.maven</groupId> <artifactId&…...
蓝桥杯 题库 简单 每日十题 day13
01 乘积尾零 题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将 所填结果输出即可。如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零? 5650 4542 3554 473 946 4114 3871 9073 90 …...
联想G50笔记本直接使用F键功能(F1~F12)需要在BIOS设置关闭热键功能可以这样操作!
如果开启启用热键模式按F1就会出现FnF1的效果,不喜欢此方式按键的用户可以进入BIOS设置界面停用热键模式即可。 停用热键模式方法如下: 1、重新启动笔记本电脑,当笔记本电脑屏幕出现Lenovo标识的时候,立即按FnF2进入BIOS设置界面…...
C++入门(头文件,命名空间,作用域,输入输出流,引用,缺省参数,函数重载)
目录 一,头文件 二,命名空间 三,作用域 四,输入输出流 五:引用 六,缺省参数 七,函数重载 一,头文件 C的头文件与C是有差距的,C的头文件是#include<stdio.h>,而C是#inc…...
“Linux免除系统交互操作方法、expect自动化交互工具” 及 “SSH批量修改主机密码脚本”
一、Linux系统免除交互操作方法 1、EOF多文本输入 案例:为机器磁盘进行分区并实现挂载,免交互式操作,如何实现? #!/bin/bash fdisk /dev/sdb <<EOF n p 1 wq EOFmkfs.xfs /dev/sdb1 && mkdir -p /data &&am…...
三相异步电机动态数学模型及矢量控制仿真
三相异步电机动态数学模型及矢量控制仿真 本文带你一步步推倒三相异步电机动态数学模型,按基于转子磁链定向的矢量控制进行 matlab 仿真,实现较好的控制效果。 1、异步电机三相方程 2、坐标变换 3、磁链3/2变换推导 4、两相静止坐标系下的方程 5、…...
HTML5 新增表单标签
HTML5为表单添加了一些新的语义化标签,可以更好地描述表单内容和功能。下面是新增的表单标签及其功能: <datalist>标签:用于定义一个选项列表,供用户选择或输入。 <output>标签:用于显示表单提交后的结果…...
【版本控制】Git(学习笔记)
一、Git工作流程图 clone(克隆): 从远程仓库中克隆代码到本地仓库checkout (检出):从本地仓库中检出一个仓库分支然后进行修订add(添加): 在提交前先将代码提交到暂存区commit(提交&…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...
