p5.js 友好错误系统(FES)贡献指南:架构解析、函数参考与开发笔记
2026/9/13 19:51:12 网站建设 项目流程

p5.js 友好错误系统(FES)贡献指南:架构解析、函数参考与开发笔记

【免费下载链接】p5.jsp5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. Looking for p5.js 2.0? http://beta.p5js.org项目地址: https://gitcode.com/GitHub_Trending/p5/p5.js

导读

p5.js 的友好错误系统(Friendly Error System,简称 FES)会在控制台输出以 "🌸 p5.js says:" 开头的友好错误消息,作为浏览器原生错误信息的补充,帮助初学者快速定位代码问题。本文以 contributor_docs/zh-Hans/fes_contribution_guide.md 为主线,系统梳理 FES 的整体架构、各个核心函数的职责与调用链、翻译键体系、性能取舍以及已知限制,并结合当前仓库源码(src/friendly_errors/ 与 translations/en/translation.json)给出源码级验证。读完本文,你将理解 FES 如何收集错误、如何生成与打印友好消息、如何参与参数验证与全局错误监控,并掌握为 FES 做贡献所需的关键入口与测试方法。

FES 概览:它从哪里收集错误

core/friendly_errors(当前仓库中为 src/friendly_errors/)包含了 p5.js 的友好错误系统代码。FES 从多个位置收集错误:

  • 文件加载错误与自动播放错误的错误处理;
  • 库内的参数检查(如函数调用时参数数量或类型不正确);
  • p5.js 贡献者实现的其他自定义错误处理。

生成友好错误消息的主要入口函数有四个:

  • p5._friendlyError():将输入消息格式化并打印(经由_report())为友好错误;
  • p5._validateParameters():验证接收到的输入值是否存在错误类型或缺少值;
  • p5._friendlyFileLoadError():指导用户解决与文件加载函数相关的错误;
  • p5._friendlyAutoplayError():指导用户解决与浏览器自动播放策略相关的错误。

在新版源码中,FES 的模块划分如下(与旧版相比已有演进,下文会逐一说明):

  • src/friendly_errors/fes_core.js:核心逻辑,包含_report()_friendlyError()fesErrorMonitor()checkForUserDefinedFunctions()helpForMisusedAtTopLevelCode()等;
  • src/friendly_errors/param_validator.js:参数验证,包含validate()friendlyParamError()findClosestSchema()等(对应旧版validate_params.js_validateParameters());
  • src/friendly_errors/browser_errors.js:浏览器错误分类表(errorTable)与生命周期入口点列表(entryPoints);
  • src/friendly_errors/stacktrace.js:解析错误堆栈的代码(借用自 stacktrace.js);
  • src/friendly_errors/fes.js:FES 消息打印基础设施(FES.log等)与国际化翻译加载。

以下示意图展示了 FES 中各文件及函数的连接关系:

📚 FES 函数参考

_report():控制台输出的最终出口

_report()是直接将错误辅助消息输出到控制台的主要函数。

注意:如果设置了p5._fesLogger(即正在运行测试),则使用它代替console.log。这在通过 Mocha 运行测试时非常有用——此时_fesLogger会让_report()将错误消息作为字符串交给测试框架,与断言的字符串进行比对。

语法
_report(message); _report(message, func); _report(message, func, color);
参数
@param {String} message 要打印的消息 @param {String} [func] 函数名称 @param {Number|String} [color] CSS颜色代码

[func]输入用于在错误消息末尾附加参考文档链接。从源码看(src/friendly_errors/fes_core.js 中mapToReference()),链接按函数名自动构造:普通函数追加(https://p5js.org/reference/p5/函数名)形式的链接,类方法则解析为p5.ClassName形式;而以load开头的加载函数不追加参考链接。[color]输入用于设置错误消息的颜色属性,在当前版本的友好错误消息中未被使用。

位置

src/friendly_errors/fes_core.js(旧版文档中为core/friendly_errors/fes_core.js)。

_friendlyError():通用友好错误入口

_friendlyError()创建并打印友好错误消息,任何 p5 函数都可以调用它来提供友好错误消息。源码中其实现非常简洁(src/friendly_errors/fes_core.js):

p5._friendlyError = function (message, func) { if (p5.disableFriendlyErrors) return; p5._report(message, func); };

值得注意的是:当p5.disableFriendlyErrors为真时它直接返回;在压缩版(minified)构建中,_friendlyError_checkForUserDefinedFunctions会被替换为空函数(源码中通过IS_MINIFIED判断),这正是"p5.min.js 中禁用 FES"的实现方式。

_friendlyFileLoadError():文件加载错误引导

_friendlyFileLoadError()位于以下加载函数内部:

  • image/loading_displaying/loadImage()
  • io/files/loadFont()
  • io/files/loadTable()
  • io/files/loadJSON()
  • io/files/loadStrings()
  • io/files/loadXML()
  • io/files/loadBytes()

其调用序列为:

_friendlyFileLoadError _report
语法
_friendlyFileLoadError(errorType, filePath);
参数
@param {Number} errorType 文件加载错误类型的编号 @param {String} filePath 导致错误的文件路径

errorType对应文件加载错误的具体分类,目的是针对不同错误场景给出精确、信息丰富的提示——例如"无法读取字体文件数据"与"文件过大无法读取"会显示不同的错误信息。当前仓库中,加载函数(如 src/image/loading_displaying.js 中的loadImage)通过图片元素onerror回调捕获失败;翻译键位于translations/en/translation.jsonfes.fileLoadError下,包括imagefontjsontablestringsxmlbytesgiflarge等细分条目,以及通用建议suggestion(提示检查文件路径、在线托管或运行本地服务器)。

示例

文件加载错误示例:

/// 缺少字体文件 let myFont; function preload() { myFont = loadFont('assets/OpenSans-Regular.ttf'); } function setup() { fill('#ED225D'); textFont(myFont); textSize(36); text('p5*js', 10, 50); } function draw() {}

除了浏览器的"不支持"错误外,FES 还将在控制台中生成以下消息:

🌸 p5.js says: 看起来加载字体时出现了问题。请检查文件路径(assets/OpenSans-Regular.ttf)是否正确,尝试在线托管文件,或运行本地服务器。 + 更多信息:https://github.com/processing/p5.js/wiki/Local-server
位置

旧版位于core/friendly_errors/file_errors.js;在当前仓库中,文件加载错误的文案已迁移到 translations/en/translation.json 的fes.fileLoadError键下,而加载函数内部保留了调用占位(如 src/image/loading_displaying.js、src/io/files.js),新构建已改为基于加载回调与翻译键生成消息。

_friendlyAutoplayError():浏览器自动播放策略

如果存在与播放媒体(例如视频)相关的错误,_friendlyAutoplayError()会在内部被调用——这很可能是由于浏览器的自动播放策略所致。它生成并打印友好错误消息,对应翻译键为fes.autoplay(见 translations/en/translation.json 第 3 行),消息会说明媒体未被浏览器允许播放,并附上更多信息的链接。

位置

src/friendly_errors/fes_core.js。

_validateParameters():参数数量与类型校验

_validateParameters()通过将输入参数与函数文档中的参数信息进行匹配来运行参数验证,检查函数调用是否包含正确数量和正确类型的参数。它生成并打印友好错误消息,对应翻译键为fes.friendlyParamError.*(见 translations/en/translation.json,包含type_TOO_FEW_ARGUMENTStype_TOO_MANY_ARGUMENTStype_WRONG_TYPEtype_EMPTY_VAR四个模板)。

可以通过p5._validateParameters(FUNCT_NAME, ARGUMENTS)p5.prototype._validateParameters(FUNCT_NAME, ARGUMENTS)在需要参数验证的函数内部调用。建议将静态版本p5._validateParameters用于一般用途;p5.prototype._validateParameters(FUNCT_NAME, ARGUMENTS)主要用于调试和单元测试。

_validateParameters()位于以下模块的函数内:

  • accessibility/outputscolor/creating_readingcolor/setting
  • core/environmentcore/renderingcore/shape/2d_primitivescore/shape/attributescore/shape/curvescore/shape/vertexcore/transform
  • data/p5.TypedDictdom/dom
  • events/accelerationevents/keyboard
  • image/imageimage/loading_displayingimage/p5.Imageimage/pixel
  • io/files
  • math/calculationmath/random
  • typography/attributestypography/loading_displaying
  • utilities/string_functions
  • webgl/3d_primitiveswebgl/interactionwebgl/lightwebgl/loadingwebgl/materialwebgl/p5.Camera

文档给出的调用序列为:

validateParameters buildArgTypeCache addType lookupParamDoc scoreOverload testParamTypes testParamType getOverloadErrors _friendlyParamError ValidationError report friendlyWelcome

从当前源码(src/friendly_errors/param_validator.js)看,这一机制已升级为基于 Zod 的 schema 验证,核心流程为:validate()依据 docs/parameterData.json(由函数内联文档生成)中的重载(overloads)信息,通过generateZodSchemasForFunc()动态构造 Zod schema 并缓存到schemaRegistry;校验失败时用findClosestSchema()找出与用户实参最接近的重载 schema(对参数个数不匹配的项给予更高权重),再用friendlyParamError()生成友好消息。此外,param_validator.js还通过p5.registerDecoratorp5.prototype上的方法自动注入参数校验(跳过_isUserCall标记的内部调用),并提供fn._internal()包装器用于包裹await之后的内部调用。支持的参数类型覆盖基础类型(NumberStringBooleanFunctionArrayIntegerObject等)、常量(通过constantsMap映射到具体值)、p5 对象(p5.Vector等,通过p5Constructors匹配)、Web API 对象(HTMLElementMouseEvent等)、元组与联合类型。

语法
_validateParameters(func, args);
参数
@param {String} func 被调用的函数的名称 @param {Array} args 用户输入参数
示例

缺少参数的示例:

arc(1, 1, 10.5, 10);

FES 将在控制台中生成以下消息:

🌸 p5.js says: [sketch.js, line 13] arc()至少需要6个参数,但只收到了4个。 (https://p5js.org/reference/p5/arc)

类型不匹配的示例:

arc(1, ',1', 10.5, 10, 0, Math.PI);

FES 将在控制台中生成以下消息:

🌸 p5.js says: [sketch.js, line 14] arc()的第一个参数需要Number类型,但收到了string类型。 (https://p5js.org/reference/p5/arc)
位置

旧版为core/friendly_errors/validate_params.js;当前仓库中为 src/friendly_errors/param_validator.js。

fesErrorMonitor():监控浏览器全局错误

fesErrorMonitor()监控浏览器错误消息,猜测错误的来源并为用户提供额外指导,包括堆栈跟踪——即程序中直到抛出错误点为止所调用函数的顺序列表。堆栈跟踪对于判断错误是内部错误还是由用户直接调用的代码引起非常有用。

它生成并打印友好错误消息,使用的翻译键包括:

  • fes.globalErrors.syntax.*fes.globalErrors.reference.*fes.globalErrors.type.*(见 translations/en/translation.json 的fes.globalErrors节点,涵盖notDefinedcannotAccessinvalidTokenunexpectedTokenredeclaredVariablemissingInitializerbadReturnOrYieldnotfuncnotfuncObjreadFromNullreadFromUndefinedconstAssign等细分类型);
  • 通过processStack()使用的"内部库"错误消息:fes.wrongPreloadfes.libraryError
  • 通过printFriendlyStack()使用的堆栈跟踪消息:fes.globalErrors.stackTopfes.globalErrors.stackSubseq
  • 通过handleMisspelling()使用的拼写检查消息(来自引用错误):fes.misspelling

_fesErrorMonitor()window上的error事件和未处理的 Promise 拒绝(unhandledrejection事件)自动触发(见 src/friendly_errors/fes_core.js 末尾的事件绑定)。也可以在 catch 块中手动调用:

try { someCode(); } catch (err) { p5._fesErrorMonitor(err); }

该函数目前适用于ReferenceErrorSyntaxErrorTypeError的子集。完整列表位于 src/friendly_errors/browser_errors.js 的errorTable中,其中按浏览器差异(Chrome、Firefox、Safari)为同一类错误登记了不同的消息模板(例如变量未定义,Chrome 报 "X is not defined",Safari 报 "Can't find variable: X")。

_fesErrorMonitor的调用序列大致如下:

_fesErrorMonitor processStack printFriendlyError (if type of error is ReferenceError) _handleMisspelling computeEditDistance _report _report printFriendlyStack (if type of error is SyntaxError、TypeError, etc) _report printFriendlyStack
语法
fesErrorMonitor(event);
参数
@param {*} e 错误事件
示例

内部错误示例 1:

function preload() { // 由于在preload中调用background() // 导致错误 background(200); }

FES 将在控制台中生成以下消息:

🌸 p5.js says: [sketch.js, line 8] 当调用"background"时,p5js库内部发生了一个错误,错误信息为"无法读取未定义的属性(正在读取'background')"。如果没有特别说明,这可能是由于从preload调用"background"导致的。preload函数中除了加载调用(loadImage、loadJSON、loadFont、loadStrings等)之外,不应该有任何其他内容。 (https://p5js.org/reference/p5/preload)

内部错误示例 2:

function setup() { cnv = createCanvas(200, 200); cnv.mouseClicked(); }

FES 将在控制台中生成以下消息:

🌸 p5.js says: [sketch.js, line 12] 当调用mouseClicked时,p5js库内部发生了一个错误,错误信息为"无法读取未定义的属性(正在读取'bind')"。如果没有特别说明,这可能是传递给mouseClicked的参数问题。 (https://p5js.org/reference/p5/mouseClicked)

错误示例(作用域):

function setup() { let b = 1; } function draw() { b += 1; }

FES 将在控制台中生成以下消息:

🌸 p5.js says: [sketch.js, line 5] 当前作用域中未定义"b"。如果您已在代码中定义它,应检查其作用域、拼写和大小写(JavaScript区分大小写)。 + 更多信息:https://p5js.org/examples/data-variable-scope.html

错误示例(拼写):

function setup() { xolor(1, 2, 3); }

FES 将在控制台中生成以下消息:

🌸 p5.js says: [sketch.js, line 2] 您可能不小心写了"xolor"而不是"color"。如果您希望使用p5.js中的函数,请将其更正为color。 (https://p5js.org/reference/p5/color)
位置

src/friendly_errors/fes_core.js。

从源码看,fesErrorMonitor()的工作流程是:先从事件对象中提取Error(兼容ErrorEventPromiseRejectionEvent),调用errorStackParser.parse()解析堆栈,再由 src/friendly_errors/stacktrace.js 的processStack()判断错误是否源于库内部——其做法是向上回溯堆栈,一旦遇到entryPointssetupdrawmousePressedkeyPressed等用户生命周期入口,见 src/friendly_errors/browser_errors.js)就截断并判定顶层帧是否来自 p5 库文件;随后用errorTable中的消息模板({{}}占位符替换为捕获符号)匹配浏览器原始错误文本,再按错误类型分支输出对应翻译模板。拼写检查handleMisspelling()使用 Levenshtein 编辑距离(阈值EDIT_DIST_THRESHOLD = 2)在所有公开 p5 符号中寻找最接近的候选,命中则给出"您可能不小心写了 X 而不是 Y"的提示;helpForMisusedAtTopLevelCode()则识别在setup()/draw()之外误用 p5 符号的情况。

checkForUserDefinedFunctions():检查用户函数的大小写

检查是否有任何用户定义的函数(setup()draw()mouseMoved()等)带有大小写错误。它生成并打印友好错误消息,对应翻译键为fes.checkUserDefinedFns

语法
checkForUserDefinedFunctions(context);
参数
@param {*} context 当前默认上下文。 在"全局模式"下设置为window, 在"实例模式"下设置为p5实例
示例
function preload() { loadImage('myimage.png'); }

FES 将在控制台中生成以下消息:

🌸 p5.js says: 您可能不小心写了preLoad而不是preload。如果这不是有意的,请更正它。 (https://p5js.org/reference/p5/preload)

从源码看,该函数会先判断是否实例模式,再以entryPoints列表建立"小写名 -> 正确名"映射,遍历上下文对象的所有属性,若某属性的小写形式命中映射、正确大小写的函数不存在、且该属性本身是函数,则提示用户写错了大小写。此外,在 p5.js 2.0 中若检测到用户定义了preload(),会额外提示该函数已在 2.0 移除,建议改用async/await或回调在setup()中加载资源。

位置

src/friendly_errors/fes_core.js。

helpForMisusedAtTopLevelCode():顶层代码误用检测

helpForMisusedAtTopLevelCode()在窗口加载时由fes_core.js调用,以检查在setup()draw()之外使用 p5.js 函数的情况。它生成并打印友好错误消息,对应翻译键为fes.misusedTopLevel

参数
@param {*} err 错误事件 @param {Boolean} log false
位置

src/friendly_errors/fes_core.js。

源码实现中,misusedAtTopLevelCode列表是惰性构建的:defineMisusedAtTopLevelCode()会遍历 p5 的公开符号(排除下划线开头),按"函数 / 常量 / 变量"分类,并按名称长度降序排序,从而保证命中时能给出最具体的符号提示(例如误用HALF_PI时提示HALF_PI而非PI)。由于不同浏览器对同一错误的措辞不同(Edge 报 "PI is undefined",Firefox 报 "ReferenceError: PI is undefined",Chrome 报 "Uncaught ReferenceError: PI is not defined"),匹配采用正则\W?symbol\W形式,并在命中后建议用户将代码移入setup()

💌 开发笔记

已知限制

假阳性与假阴性情况

在 FES 中可能会遇到两类问题:假阳性(false positive)像虚假警报,即 FES 警告有错误但代码实际是正确的;假阴性(false negative)则是代码中有错误但 FES 没有提醒。识别并修复它们很重要,因为能节省调试时间、减少困惑,并让修复实际问题变得更容易。在某些不理想的情况下,错误处理的设计可能需要在消除假阳性与消除假阴性之间取舍;如果必须选择,通常应优先消除假阳性,避免生成分散注意力或误导用户的不正确警告。

fes.globalErrors相关的限制

FES 只能检测到使用constvar声明的被覆盖的全局变量,使用let声明的变量不会被检测到。这一限制源于let处理变量实例化的特定方式,目前无法解决。

fesErrorMonitor()下描述的功能目前仅在 Web 编辑器上或在本地服务器上运行时有效(相关内容可参见历史 pull request #4730)。

FES 的性能问题

默认情况下,p5.js 启用 FES,而在p5.min.js中禁用,以防止 FES 函数拖慢进程。错误检查系统可能会显著减慢代码(在某些情况下最多慢 10 倍)。可以在草图顶部用一行代码禁用 FES:

p5.disableFriendlyErrors = true; // 禁用FES function setup() { // 进行设置操作 } function draw() { // 进行绘制操作 }

请注意,此操作只禁用已知会影响性能的 FES 功能,例如参数检查;不影响性能的友好错误消息仍保持启用,这包括文件加载失败时的详细错误消息,以及尝试覆盖全局空间中的 p5.js 函数时的警告。

从源码与测试可以进一步印证这一设计:

  • src/core/main.js 中disableFriendlyErrors属性文档明确指出:FES 会在幕后做额外工作(例如检查传入函数的参数),禁用后 sketch 绘制性能可显著提升;示例中circle(50, 50)本会触发参数数量提示,禁用后静默失败。
  • src/friendly_errors/fes_core.js 中_friendlyError()checkForUserDefinedFunctions()fesErrorMonitor()均在开头检查if (p5.disableFriendlyErrors) return;;src/friendly_errors/param_validator.js 的validate()与注册的装饰器也同样受该开关与p5.disableParameterValidator控制。
  • 在压缩构建中,_friendlyError_checkForUserDefinedFunctions会被直接替换为空函数(IS_MINIFIED判断),test/unit/core/main.js 中分别用_friendlyErrorStub验证了非压缩版会警告"globals already exist"、压缩版不会调用_friendlyError

未来工作的想法

  • 解耦 FES(issue #5629);
  • 消除假阳性情况;
  • 识别假阴性情况;
  • 添加更多单元测试以获得全面的测试覆盖;
  • 更直观、清晰且可翻译的消息(友好错误国际化的更多讨论可参见 p5-fes-i18n 手册);
  • 识别更多常见错误类型并使用 FES 进行泛化,例如bezierVertex()quadraticVertex()必需对象未初始化,以及检查nf()nfc()nfp()nfs()的 Number 参数是否为正。

结论

本文基于贡献指南梳理了core/friendly_errors(当前为 src/friendly_errors/)的整体组织与用途,并对每个核心函数提供了参考说明:_report()是控制台输出的最终出口,_friendlyError()提供通用入口,_friendlyFileLoadError()_friendlyAutoplayError()分别覆盖文件加载与自动播放场景,_validateParameters()负责参数校验(新版基于 Zod schema 与 docs/parameterData.json),fesErrorMonitor()监控浏览器全局错误并配合 src/friendly_errors/stacktrace.js 解析堆栈与 src/friendly_errors/browser_errors.js 的错误分类表,checkForUserDefinedFunctions()helpForMisusedAtTopLevelCode()则处理用户代码的常见误用。所有消息文案均由 translations/en/translation.json 中的fes.*键驱动,并支持通过 src/friendly_errors/fes.js 的语言检测与本地缓存机制加载对应语言(如fes-zh.json)。

文章后半部分收录了来自前贡献者的开发笔记,涵盖假阳性/假阴性取舍、fes.globalErrorslet检测限制、性能开销与p5.disableFriendlyErrors = true;的禁用方式,以及解耦 FES、扩充测试覆盖与消息可翻译性等未来方向。此外,社区曾于 2021–2022 年开展 FES 专项调查,调查结果以漫画与完整报告两种格式公开,可作为了解用户真实痛点的一手资料。如果你正在考虑为 FES 做贡献,建议从开发笔记中的"未来工作的想法"入手,并借助单元测试验证新增的友好错误行为。

【免费下载链接】p5.jsp5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. Looking for p5.js 2.0? http://beta.p5js.org项目地址: https://gitcode.com/GitHub_Trending/p5/p5.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询