脚本加载顺序
ESM 脚本
ESM 没有显式加载顺序,不应依赖特定先后,而应通过模块导入声明依赖。
经典脚本
通常在应用启动前加载,顺序由项目设置决定,可从主菜单或场景设置访问:

面板列出标记为 preload 的经典脚本及加载执行顺序。

拖动脚本即可调整。
脚本首次加载后立即执行,因此顶层代码按加载顺序运行。但加载顺序不影响组件内脚本方法顺序。例如同一实体的 initialize 按实体脚本列表顺序调用。
预加载
脚本与其他资源一样默认 preload。关闭后,通常不会自动加载,可保留在项目中但不启动。需要时通过常规资源 API 动态加载,见 AssetRegistry#load。
可以订阅脚本注册表的动态变化:
this.app.scripts.on('add', (name, scriptType) => {
console.log('script', name, 'has been loaded');
});
发布
发布或下载 static 时,经典与 ESM 分别处理。Concatenate Scripts 和 Minify Scripts 控制处理程度,见发布新构建。输出文件见下载项目。
npm 下载格式不同,生成保留源码的 Vite 项目,因此下述处理和选项不适用。
经典脚本
默认将所有预加载经典脚本拼接成单文件,减少请求并加快加载。
ESM 脚本
ESM 项目作为 ES 模块应用构建,入口 js/index.mjs。默认启用 Concatenate Scripts,会将脚本与引擎打包:
- 打包输出:引擎和启动代码进入
js/index.mjs,预加载 ESM 进入由其在启动时加载的共享模块。 - Tree shaking:排除未使用引擎代码,但用户模块保持完整,因为项目中每个模块资源都要能按路径加载。
- 代码分割:仅通过动态
import()加载且关闭 Preload 的模块进入独立文件,首次调用时下载。预加载模块无论静态或动态引用,都进入共享模块。 - 压缩:启用 Minify Scripts 后,压缩打包代码以减小体积。
代码分割适合延后菜单、可选工具和过场动画等内容。下例 cutscene.mjs 关闭 Preload,因此首次 play() 时才下载:
import { Script } from 'playcanvas';
export class Intro extends Script {
static scriptName = 'intro';
async play() {
const { Cutscene } = await import('./cutscene.mjs');
new Cutscene(this.app).play();
}
}
关闭 Concatenate Scripts 后,各 ESM 独立输出,引擎作为独立模块加载,不执行压缩。
混合 ESM 与经典脚本
两者可共存,但分别处理:
- ESM 按上文打包。
- 经典脚本保持在 ESM 包外;启用拼接后,合并到自己的独立文件。
- 经典脚本通过全局
pc访问引擎,因此构建包含整个引擎,不进行引擎 tree shaking。
注意
不要在经典脚本中用 import() 加载 ESM。构建会移动 ESM 路径,只更新 ESM 内部的导入,因此编辑器可运行的调用发布后会失败。代码编辑器会为经典脚本中的 import() 警告。