Skip to content

调试 ​

先体检 ​

bash
npx fjs doctor

检查 Node 版本、是不是 fjs 项目、@ufjs/cli 与 @ufjs/runtime 版本是否匹配、字节码编译器 fjsc 从哪来、Flutter / adb / xcodebuild、可用设备、宿主的 flutter_fjs 来源。挡住构建的问题会让退出码为 1。

热更新 ​

fjs dev --pages(fjs run 会自动启动它)按改动的范围选择最小的更新方式,只有两档:

改了什么设备上发生什么
某个页面自己的代码(页面 .vue 和只被这一页 import 的模块)只重新加载这一页,其它页面和页面栈不动
其它一切:Shell、入口、路由表、app.config.ts、被多页或入口引用的组件与 ts、插件、配置整个虚拟机重建,回到首页

共享模块打进 shared.js,页面通过同一份实例取用。Web 端(dev:web)是 Vite 原生的组件级 HMR。

dev server 终端快捷键 ​

fjs dev 跑着的时候,终端本身就是控制台:

键作用
r完整重新构建并 reload(热更新判断出错时按它)
l开关应用日志
d当前连着几个应用
c再打印一次地址和二维码
p在 App 上开关性能面板
o--web 模式下用浏览器打开
?再打印一次快捷键列表
q退出

看日志 ​

bash
npx fjs log

实时查看 App 里的 console.log / warn / error。它连的是 dev server,所以模拟器、局域网真机、浏览器用法都一样,不需要 USB。

在 dev server 终端按 l 也能看到同一条日志流。flutter run 的输出里也有,前缀是 [js:info] 这样的格式。

在设备上求值 ​

bash
npx fjs eval '1 + 1'
npx fjs eval 'Object.keys(globalThis).length'

在正在运行的 JS 虚拟机里执行表达式,打印结果。排查「页面上到底是什么状态」时很好用。

断点调试 ​

用 Chrome DevTools 直连设备上正在跑的 JS:断点(含条件断点)、单步、调用栈、局部变量、在暂停的帧上求值,全都可用。

fjs dev 或 fjs run 跑着的时候,另开一个终端:

bash
npx fjs debug

它会打印一条打开 DevTools 的命令,复制执行即可:

bash
open -a "Google Chrome" \
  "devtools://devtools/bundled/inspector.html?ws=127.0.0.1:38902/cdp"

然后在 Sources 面板里选脚本、点行号下断点,在设备上触发那段代码就会停下来。

别走 chrome://inspect

那条路要靠 Chrome 自己发现目标,实测经常一片空白而且不给任何报错。另外 devtools:// 地址粘进地址栏会被 Chrome 拦掉,只能像上面这样从命令行 open。

Elements 和 Network 两个面板也在工作:

面板能看到什么注意
Elements当前页的元素树(标签 / class / style / 文本),展开箭头按需拉取子树,任意深度都能展开;选中节点后 Styles 侧栏依次是 inline、命中规则(选择器原文 + 声明,按特异性排序)、Computed(最终生效的样式)结构变化(增删元素、路由切页)约 1–2 秒内自动刷新;文本和属性变化就地更新;纯样式变化点节点时自愈
Network应用里 fetch() 的请求行和响应体预览行有最多 500ms 延迟;响应体在应用自己读取它(text() / json() 等)之后才看得到,上限 512 KB

几件需要知道的事:

  • 断点停住的时候整个界面是冻结的。JS 跑在 UI 线程上,暂停 JS 就是暂停界面,resume 后恢复;期间到达的热更新推送会等 resume 后再处理。
  • Sources 里能打开 .vue 原文。dev 构建带 source map:断点可以下在 <script setup> 的行上,Call Stack 显示原文行号;Vue 里 import 的项目 .ts 同样可下断点。shared.js 里来自 node_modules 的依赖(pinia、vue……)没有 map,还是编译后的样子。
  • Console 收全两侧日志。JS 的 console.* 带调用栈进 Console;终端里 [dev] / [nav] 这些 Dart 侧日志也由 fjs debug 合成进来,终端看到的流 DevTools 里都有。调试器连上期间 App 侧的 fjs log 可能收不到 JS 日志。
  • 启动顺序无所谓。fjs debug 先起、后起、dev server 中途重启都能接上;App 连上调试通道时会整包重载一次,让所有脚本进入调试器的脚本表。
  • 只能调开发构建的 App 端,且默认引擎才有调试器。调试器物理编在独立模块里:release 包不含它;quickjs flavor(--js-engine quickjs)没有它。非 dev 构建想看 Elements / Network 面板,构建时加 fjs build --devtools 保留数据平面。Web 端直接用浏览器自带的 DevTools。

性能面板 ​

dev server 终端按 p,App 右上角浮出一个可拖动的面板:

text
fps                3
ui        4.3/60.9 ms
gpu        1.3/2.7 ms
heap   4.6MB·14806
nodes            100
行含义
fps最近一秒真正画了多少帧(空闲时低是正常的)
uiUI 线程耗时:均值 / 最坏值。JS 也跑在这里
gpu光栅线程耗时
heapJS 堆大小 · 活对象数
nodes节点数

超过 16.7 ms 标红。最坏值不会随时间滚掉,收起再打开才重置 —— 刚点的那一下造成的卡顿会一直留在面板上等你看。

ui 高而 gpu 低:JS 或布局太重,考虑 list-view、组件拆分、onPageSettled。gpu 高:画的东西太复杂(阴影、裁剪、大图)。

体积分析 ​

bash
npx fjs build --pages --bytecode --analyze

按产物列出 JS / gzip / 字节码三个尺寸,以及每个产物里占比最大的包:

text
shared.js  402.3 KB  gz 90.9 KB  bytecode 1.1 MB
  @vue/runtime-core                       254.5 KB   63.3%
  @vue/reactivity                          56.1 KB   14.0%

字节码那一列最值得盯:它决定冷启动要读多少。

首帧节点预警 ​

构建时会静态估算每个页面首帧要创建多少节点,超过 500 个就输出 [fjs perf] 告警。可以调整预算:

json
{ "fjs": { "performance": { "nodeBudget": 800 } } }

遇到告警,优先考虑 list-view、减少默认行数,或把非首屏内容延后渲染。

Flutter 侧 ​

生成的宿主是标准 Flutter 工程,Flutter DevTools、flutter logs 都能用:

bash
cd .fjs/flutter
flutter analyze

用 fjs go 调试时,需要 Flutter DevTools 就装 debug 版 APK。

常见症状 ​

症状可能原因
真机连不上 dev server,No route to host (errno = 65)iOS 本地网络 / 无线数据权限没给;手机和电脑不在同一网段
页面白屏,日志里有 Maximum call stack size exceeded页面文件名和内置标签同名,被当成组件自引用。加 defineOptions({ name: 'XxxPage' })
某条 CSS 不生效先 npx fjs lint 静态查一遍;运行期也看日志里的告警:不支持的写法会告警一次,不会静默
两个页面的 pinia 状态不通store 实例建在插件函数里,或者没有登记 fjs.shared
invokeHost: no native host在 Web 端调用了宿主函数,加 hasNativeHost 判断
转场动画卡一下页面 setup 里有重计算,挪进 onPageSettled

更多见常见问题。

基于 MIT 协议发布