Skip to content

mp-flutter 0.2.0

Choose a tag to compare

@jackwangfeng jackwangfeng released this 28 Sep 10:35
· 11 commits to main since this release

在 0.1.0 的基础上补齐三类运行时缺口(随机数、路由切换崩溃、图片离屏解码)、修复两处
真机启动/渲染问题(iOS 白屏、部分安卓启动失败),并完成一轮包体积与冷启动性能收尾
(分包重组、常用汉字合一字体、原生图片解码、字体缓存)。

运行时缺口补齐

  • Random.secure() / crypto.getRandomValues / crypto.randomUUID:小程序逻辑层没有
    window.crypto,之前直接抛 Unsupported。现在启动时用 wx.getRandomValues 取一次真随机
    种子,喂给 ChaCha20(RFC 8439)当确定性随机数发生器,之后本地同步产出;重播种失败自动换钥、
    取熵 10 秒超时、计数器回绕前换钥;播种提前到 loadSubpackages 阶段与 CanvasKit 加载并行发
    起,不阻塞启动关键路径,播种失败/超时按浏览器无该 API 的标准语义处理,不伪造安全性
  • 路由切换崩溃修复:滚动帧密集时 push/pushReplacement 偶发触发
    Null check operator used on a null value / Cannot read properties of null (reading 'cullRect')(CanvasKit 渲染管线内部)。根因是垫片缺 self.scheduleImmediate,导致 Dart
    微任务退化为 setTimeout 调度,下一帧得以插进 draw() 的 await 与 preroll 之间,读到一张
    在途 dispose 的 SkPicture。已修复;复测未再复现该崩溃
  • Image.cacheWidth/cacheHeight 与 toByteData():小程序画布拿不到通用离屏 2D 上下文,
    改由 runtime/image.js 的 SoftCanvas 用 CanvasKit CPU 光栅 surface 实现(drawImage/
    getImageData/toDataURL/transferToImageBitmap);toByteData(rawRgba) 返回非预乘
    RGBA 像素(与浏览器一致,与 Flutter 原生平台预乘不同);大图 toByteData 峰值内存约为
    宽×高×4 的 3 倍,压在 wasm 堆上

微信隐私接口

  • --private-info=<接口名>(可重复)/ mp_flutter.yaml 的 private_infos:按需声明任意
    隐私接口(getLocation/chooseLocation/choosePoi 等),写入 app.json 的
    requiredPrivateInfos 与对应权限说明;--require-location 保留为等价于
    --private-info=getLocation 的历史开关

体积与冷启动

  • 首帧前只加载必需分包:以前 boot 在首帧前并行拉取全部分包(某真实电商小程序约 10MB);现在
    只拉 pkg-dart-*、pkg-wasm 与新的启动资源包 pkg-assets-boot(FontManifest/
    AssetManifest、清单里声明的字体、回退 Roboto),其余资源按需下载。该真实电商小程序首帧前阻塞
    下载 10.04MB → 6.33MB(不在预下载里的 8.26MB → 4.45MB;当前默认 cjk_font: full
    下的数字,含独立分包 pkg-cjk 的约 1.1MB——见下方"常用汉字合一字体")
  • 资源分包重组:NOTICES 单独放 pkg-notices;简体中文回退字体分片按"常用字优先 +
    码位邻近"分进约 512KB 的 pkg-fonts-*;其余资源按路径分进约 512KB 的 pkg-assets-*
  • --no-licenses / licenses: false:NOTICES 换成空占位(许可证页仍可打开,只是不列出
    第三方包)
  • font_base_url / --font-base-url:实际移到远端的是 notosanssc(简体中文回退,全部
    101 个分片)——roboto 始终留在包内;emoji/日文/韩文/繁体等其余家族本来就不打包(不受
    这个开关影响,一直是 404)。运行时从 CDN 拉取并缓存到本地文件(10MB 上限,LRU);产物下
    输出待上传目录 mp-fonts-remote/
  • 原生启动界面:首帧前显示应用名(splash_title)、背景色(splash_color)与进度条,
    启动失败时显示错误文案
  • 构建成功提示里提醒:开发者工具界面打开产物时需取消「将 JS 编译成 ES5」「增强编译」

字体到达引发的整体重排

  • 常用汉字合一字体(cjk_font: level1 | full | false,--cjk-font=level1|full / --no-cjk-font,
    默认 full):Noto Sans SC v37 子集(level1 = GB2312 一级 + 标点/全角/Latin-1/常用符号,TTF 1.2MB;
    full = 一二级,2.2MB;入库,tools/fonts/gen_cjk_common.py 可复现),brotli 后放独立分包 pkg-cjk,
    boot 一开始就用 readCompressedFile 读出、CanvasKit 就绪即预解析,引擎初始化时直接应答;
    main.dart.js 构建期补丁让引擎把它当作 Roboto 之后的第一个回退字体、缺字检测时算上它并只把
    真正缺的码点交给选字体算法。该真实电商小程序(开发者工具,full)首屏回退分片 fetch 20→0、fontsChange 5→0、

    50ms 长帧 3–4→0。真机数据显示 full 不在首帧关键路径上(字体读取 28–30ms,首帧 354/533ms),
    而 level1 会因服务端下发文案命中二级字/常用符号触发额外回退分片下载与 fontsChange,因此把
    默认从 level1 改为 full。注意:cjk_font 默认开启会给总包增加约 1.1MB(pkg-cjk,brotli
    后约 1.15MB,该真实电商小程序实测总包 10.05MB → 11.21MB);服务商代开发小程序总包上限 20MB,总包已经
    接近上限的应用升级前请确认还有余量,或用 --cjk-font=level1 / --no-cjk-font 收窄/关闭

  • 常用符号子集扩充:iPhone 真机首屏仍触发一次 NotoSansSC v37 分片 119(NexQ2w.119.woff2)
    下载并引发一次 fontsChange、215ms 长帧;字表补充通用标点 U+2000–206F、箭头 U+2190–21FF、
    €℃№™、常用数学运算符(∑√∞∫≈≠≤≥)、带圈数字 ①–⑳、制表符小集合(┌┐└┘├┤┬┴┼─│)与
    几何图形/五角星(●○■□▲△▼▽◆◇★☆);level1 TTF 1,152,356→1,166,672 字节(br 662,319→664,089),
    full TTF 2,149,868→2,170,200 字节(br 1,171,426→1,173,234),仍远低于单分包 2MB 上限
  • 同一份字体字节只解析一次(引擎每注册一批回退字体都重注册全部字体,不再重复解析)
  • 回退字体响应按 100ms 窗口合并,窗口内的多批缺字只触发一次 fontsChange
  • 同一个未下载的按需分包只发一次 require.async(安卓真机字体 fetch 恰好 1001–1003ms 的疑因)
  • --perf-hud:cjk-font pkg/read/parse/引擎取用等待 首帧前诊断行、font-change #n 与每秒行的
    fontChange=、字体解析复用次数 (reuse n)

修复

  • iOS 真机白屏(UnimplementedError: v8BreakIterator is not supported.)
    与部分安卓真机启动失败(ReferenceError: Intl is not defined):改为打包
    完整版 CanvasKit(wasm 自带 ICU 断行,不依赖 Intl.Segmenter/
    Intl.v8BreakIterator),iOS/安卓/模拟器统一只打这一个变体;为放进单个分包,
    构建期清零 ICU 里泰/老/高棉/缅文的断词词典(这四种文字词内不再给换行机会)。
    pkg-wasm 分包由约 1573KB 增至约 1822KB。垫片对 Intl 等可能缺失的宿主
    全局改为 typeof 探测,没有 Intl 时给引擎一个只有 Locale 的最小替身
  • --force-platform 新增 ios(遮蔽 Intl.v8BreakIterator/Intl.Segmenter)与
    android-noIntl(遮蔽整个 Intl),tools/e2e/run.sh 新增同名配置,在开发者
    工具里复现真机 JS 引擎的能力缺失

性能批次收尾

  • 原生图片解码生命周期:成功后立即释放已解码字节(按需从临时文件读回),临时文件不再
    与单份 SkImage.delete() 绑定(真机上引擎会反复对同一原生图片对象请求/丢弃惰性
    SkImage,没有真正意义上的"最后一次",绑定会在下一次请求时出错);改为总字节数
    (64MB)顶到上限时才淘汰已使用且最久未用的文件,淘汰/磁盘空间不足/总字节超限均不计入
    原生解码失败计数
  • cacheWidth 缩放遇到"整块全透明"时,先用 wasm 解一遍源图确认不是真透明才判定 2D 画布
    不可用,避免误判真正透明的图片
  • --perf-hud 相关代码(perf-hud.js、图片统计对象)只在开关打开时才写进产物/构造,不
    影响未开启时的产物体积与运行时开销
  • 合一字体(cjk_font)读取加 10 秒超时,超时按读取失败处理,不阻塞启动
  • 远端字体缓存索引(font-cache.js)命中时节流写 index.json(2 秒一次)