CesiumJS 更新日志 1.96 与 1.97 - 新构建工具 esbuild 体验及 Model API 更替完成
CesiumJS 更新日志 1.96 与 1.97 - 新构建工具 esbuild 体验及 Model API 更替完成
截止发文,1.97 还未发布,但已经在源码仓库完成了 Model API 的替换,文章会跟进。本文着重介绍新的构建指令的用法(配套 esbuild 的使用),见第三节。
首先介绍 1.96 和 1.97 两个大版本的更新内容。
1.96 更新情况
1.96 已于 2022 年 8 月初更新;重大更新如下:
- 改用 esbuild 完成库程序的构建,并更新了一批 npm script(与之前的很不一样);
Model.boundingSphere
属性现在返回 ECEF(地心地固)坐标系下的坐标值,也就是世界坐标(EPSG:4978)而不是模型原来的局部坐标;- 具备
CESIUM_primitive_outline
扩展的 glTF 模型或 Cesium 3DTiles 数据,现在可以通过控制showOutline
属性来显示外廓线了;将来有可能加入outlineColor
属性控制颜色; - 使用模型 API 新架构(1.97将正式替代旧版
Model API
,下文不再重复描述)制作的点云类型 3DTiles 现在支持了样式化; - 升级 earcut 库至 2.2.4 版本,提升了 10% 到 15% 的性能;
- 修复新 Model API 的若干 bug,包括但不限于 draco 点云示例崩溃的问题、cmpt 瓦片数据示例缓存不正常的问题、具有透明度的模型示例不更新的问题、逐要素后处理(per-feature post-processing)不生效的问题、具备量化坐标的 i3dm 瓦片数据不能正确加载的问题等;
1.96 有两项过期 API 消息:
-
ModelInstanceCollection
模块中 CPU 端的实例化技术(于 1.97 废除); -
requestAnimationFrame
和cancelAnimationFrame
两项兼容性库实现(于 1.99 废除),因为基本上所有的浏览器都实现了这个 API。
1.97 更新情况
1.97 主要更新如下(持续更新中):
-
把
ModelExperimental
转正,重命名为Model
,位于Source/Scene/Model/
目录下,完全替代旧版的Source/Scene/Model.js
等较为糅杂、耦合的旧模型加载架构; -
正式支持
CustomShader API
-
正式支持
EXT_structural_metadata
,EXT_mesh_features
和EXT_instance_features
三项未来 3DTiles 1.1(当前被称作 3DTiles Next,以 1.0 的扩展存在)的特性,极大增强 3DTiles 各分层的数据语义能力; -
正式支持
EXT_mesh_gpu_instancing
这项未来 3DTiles 1.1(同上,不赘述)的特性,此特性为 glTF 规范专门设计、扩展,以逐步替代旧版 i3dm 瓦片格式的实例化技术; -
正式支持
EXT_meshopt_compression
这项 glTF 的网格优化压缩扩展,与 Draco 相比各有优劣; -
支持了跨瓦片文件的纹理贴图缓存技术;
-
enableModelExperimental
配置移除,所有的 glTF 模型、3DTiles 新旧版本均使用上述新版模型架构
新版的 Model API
在公共 API 的文档和调用几乎没有差距,做到了对上层应用的无缝兼容(待这两个版本测试),因此开发者几乎不需要更改原来的 API 调用代码。
此外,还有几个极为重大的改变:
-
glTF 1.0 版本的支持已移除,请尽快转换你的数据到 glTF 2.0 版本;
-
glTF 的一项扩展
KHR_techniques_webgl
已移除,如果你有自定义着色需求,请使用CustomShader API
-
ModelInstanceCollection
这个私有类使用的 CPU 端实例化技术已移除; -
ModelMesh
和ModelMaterial
两个私有类已移除; -
单一模型的分类分级着色已支持,使用
classificationType
参数配置Model
类即可,详见帮助文档
总结下来就是下一代的 3DTiles 越来越近了。
新构建工具 esbuild 和大换血的构建指令使用
本段大部分材料参考自官方博客:
Build Tooling Updates Coming to CesiumJS – Cesium
1. 官方自述构建工具更新的原因
近几个月,官方团队致力于改进开发者体验,最近已经把成果合并到主分支,那就是使用新的代码构建工具。在 CesiumJS 的源代码开发时,已经能获得更小体积的发行版 CesiumJS 库文件(未压缩版本体积小了 33%,压缩版小了 16%),而且速度有质的飞跃。发行版变小,使得网络加载时间变短,速度更快。
由于精简了代码资源、移除无关的空格和注释,得到了更小的构建版本的库文件,这也有利于提高浏览器加载速度
这样做还有额外的好处,就是解决了一个在 Linux 系统中 Chrome 浏览器长期存在的问题
2. 选择使用 esbuild
CesiumJS 项目启动之初,它所用的辅助开发工具基本上都是同类项目中的“新鲜玩意儿”,比如,RequireJS 就是当时最突出的依赖管理工具,1.62
版本之前的模块机制都是异步模块定义(AMD)。后来自 1.63
版本完全为 ESModule 后,就改用 Rollup 来构建了。
到目前为止,CesiumJS 在构建时是存在没有轻量化、最小化等毛病的。CesiumJS 最重要的环节,即把所有源代码模块合并到一个主文件(以方便分发),官方并没有在这个过程中对代码进行重大转换,因为需要考虑到兼容性问题。
由 ESModule 多个模块文件合并到单一文件,这个单一文件我一般叫它库文件
现在的 JavaScript 工具已经越来越快、越来越好用了,开发者们更习惯走打包的路线,所以是时候评估构建工具的更新了。
官方选择使用 esbuild 来代替 Rollup 来完成 “ESModule -> 库文件” 这个最重要的环节。
-
首先,esbuild 足够快(译者注,参考 vite,速度有目共睹),这能满足快速开发的要求;
-
其次,Rollup 虽然有很多插件,但是相比较之下这些插件很多 esbuild 都是开箱支持的,例如从 node_modules 中自动打包第三方库、代码转换、代码压缩等;
-
最后,esbuild 还能进一步减小构建出来的主库程序文件的大小(无论压缩版还是未压缩版本),更有利于浏览器加载提速。
3. 关于 WebWorker 遗留问题
WebWorker 是浏览器后台运行的独立脚本,在 CesiumJS 中,它们广泛用于创建几何图形和数据解码任务。
Firefox 仍未在 worker 中支持 ESModule,这意味着必须使用 iife 或者 amd 模块化机制来加载 worker 文件;但 esbuild 只支持 ESModule 的代码分割,如果不进行代码分割,worker 文件大小就会显著增大。
为了避免这个问题,只能暂时继续用 Rollup 和 RequireJS 来解决 worker 的问题。直到 Firefox 支持之后,才能完全切换到 esbuild 来。
4. 重头戏之旧构建指令移除与新指令用法
除了用上 esbuild 之外,官方还借此机会从全局重新考虑了构建过程,包括构建脚本负责的任务的粒度问题。
最大的变化是开发时的构建方法,由于 esbuild 的加速(尤其是快速增量构建),现在已经不需要提前构建(即旧版的 combine 指令)才能进行本地开发了(npm run build
)。现在,源代码的开发调试只需要安装依赖,就可以直接启动服务器:
npm run install && npm run start
CesiumJS 多年的积累,相关工具增加、配置的增加,导致 gulp 中添加了很多指令,他们命名似乎有点乱。现在官方重新评估了这些指令,重新设计如下(主要):
-
移除
combine
、combineRelease
、minify
、minifyRelease
、build-specs
、convertToModules
指令; -
重命名
startPublic
为start-public
,功能不变; -
保留
build
指令,但与之前意义不同,下文细说; -
保留
release
指令,但与之前意义不同,下文细说; -
重命名
generateDocumentation
指令为build-docs
,下文细说; -
重命名
makeZipFile
指令为make-zip
指令,功能不变; -
重命名
buildApps
指令为build-apps
指令,需要构建版本的示例应用(而不是使用源代码的版本)可调用此指令; -
保留
build-ts
指令,但与之前意义不同,下文细说;
移除的指令不再赘述,请读者自行查阅互联网上的资料。下面着重介绍 build
、build-ts
、build-docs
、release
四个最重要的新增/变化指令。
我使用了两台电脑对比运行时间,一台标记为 A,使用桌面 i7 12700 CPU;一台标记为 B,使用 AMD r7 4800U。
指令 | A | B | 做了什么 |
---|---|---|---|
build | 约 5s | 约 9s | 转译 glsl 文件为 ESModule,导出着色器代码字符串常量;创建出 Source/Cesium.js ESModule 格式的入口文件,并生成 Build/CesiumUnminified 版本的三类库文件和 source-map 映射文件;处理测试文件等 |
build-docs | 约 19s | 约 40s | 调用 generateDocumentation 任务生成离线文档页面 |
build-ts | 约 14s | 约 24s | 根据 jsdoc 注释创建 TypeScript 类型定义文件 |
release | 约 46s | 约 85s | 先执行生成 Build/CesiumUnminified 版本和生成 Build/Cesium 版本库文件的 build 任务;然后调用 build-ts 任务生成类型文件;最后调用 generateDocumentation 任务生成离线文档页面 |
其中,build
指令内部会调用 esbuild 来完成 ESModule、iife、commonjs 三种格式的库文件的打包。表格不够详细的,见下文:
-
build
:做了 4 件事,①转换Source/Shaders
下所有.glsl
格式的文件成 ESModule 文件,导出着色器代码字符串常量;②生成Source/Cesium.js
这个 ESModule 出口文件,导出所有 CesiumJS 的模块、常量、类等,无论公开或私有;③生成Build/CesiumUnminified
文件夹,并调用 gulp 配置文件中的 build 任务,生成 ESModule、iife、commonjs 三种格式的单文件 CesiumJS 库,含 source-map 映射文件;④复制Source/
目录下的四个静态文件夹到Build/CesiumUnminified
下;这个指令有 esbuild 的参与,也有 rollup 的参与(解决 Firefox 仍旧无法在 worker 中使用 ESModule 的问题) -
build-ts
:调用 jsdoc,把源码中的 jsdoc 注释生成Source/Cesium.d.ts
-
build-docs
:调用 jsdoc,生成离线帮助文档页面,以便开发者页面或部署的 API 帮助文档能够使用; -
release
:这是个复合指令,会顺序执行 ①调用 gulp 配置文件(gulpfile.cjs
)中的 build 任务,build
指令实际上用的就是 build 任务(一个 JavaScript 函数,被 gulp 称作任务),生成无 TypeScript 类型定义文件和源代码映射文件的Build/Cesium
和Build/CesiumUnminified
两个版本的库程序,前者会压缩,后者与build
指令实际差不多;②调用build-ts
指令;③调用 gulp 配置文件内的 generateDocumentation 任务,实际上也就是build-docs
,生成离线帮助文档
注意,
build-apps
依赖于build
,有先后顺序
有两个小技巧,
-
根据 npm 指令的规则,想为某个指令的内部调用继续传递参数的,需要接两个横线,例如生成离线文档时想生成私有模块的文档,则使用
npm run build-docs -- --private
,那么--private
就会传递给build-docs
内部实际调用的 jsdoc -
想查看开发者示例程序必须再次调用
build
指令,release
指令一旦执行会清掉开发者示例程序,也就是 Sandcastle 中的 Development 分页的示例
5. 收益
就官方的说辞和笔者自己的体验来看,build
指令比起之前的速度,简直就是做了火箭,原本同一台电脑进行 minifyRelease
可能要 4 分多钟,现在 release
只需 1 分半左右。
而且最显著的,无论是压缩版(Build/Cesium
)还是未压缩版(Build/CesiumUnminified
)的三种格式的库程序,其库文件大小均比原来小不少,加上 gzip 的加持还能更小(请读者自测),基本上全库加载能做到从黑场景到出地球只需 2 秒左右。
-
压缩版,4.3MB → 3.3MB(基于 1.97,GZIP 后 900+ KB)
-
未压缩版,12.5MB → 8.0MB(基于 1.97)
CDN 上的 Cesium.js 主文件传输体积,基于 HTTP2 甚至能做到 700+ KB,显著提升场景加载速度。
6. 之后的改进方向
可能之后有考虑转向 TypeScript;一旦 Firefox 的 worker 可以加载 ESM,那么理想状态下可以完全移除 RequireJS 和 Rollup,进一步加速构建速度和减小发布版本的库代码。
7. 开发者如何使用新的构建成果
我没有直接翻译官方的表述,这里我给一个更符合思考逻辑的建议:
-
如果你打算在你的工程中用打包器加载 CesiumJS 源代码模块,那么你直接使用
import {} from 'cesium'
即可,这种用法将会增加你的工程打包时间,但是可以利用 ESModule 的Tree-Shaking
特性减小构建产物的大小; -
如果你打算使用官方构建出来的 ESModule 单库文件,请使用
Build/Cesium/index.js
或Build/CesiumUnminified/index.js
,看你需要压缩或未压缩的版本; -
如果你在 CommonJS 模块中使用 CesiumJS,那么继续使用
const cesium = require('cesium')
即可导入,默认会指向Build/Cesium/index.cjs
或Build/CesiumUnminified/index.cjs
; -
如果你打算使用官方构建出来的 iife 风格的单库文件,请继续使用
Build/Cesium/Cesium.js
或Build/CesiumUnminified/Cesium.js
以后有兴趣可以写文讲讲如何最优地在你的项目中引入 CesiumJS,我认为 CDN 引入是一个不错的加速手段,尽量避免让应用打包器再次打包 CesiumJS,Tree-Shaking
对于这种级别的 3D 地球库来说可能收益甚微。