Skip to content

Releases: jackwangfeng/mp-flutter

mp-flutter 0.3.2

Choose a tag to compare

@jackwangfeng jackwangfeng released this 01 Oct 09:36

冲 pub.dev 满分:三包 README 英文化、补 example、修分析提示

  • 三个包的 packages/*/README.md 改为英文为主(安装/用法/配置/已知限制
    全量英文内容),原中文内容原样移到同目录 README.zh.md,两边互相加一行
    跳转链接。仓库根 README 不受影响,仍是中文(它不算在"包"范围内,pana 也
    不检查它)。
  • 三个包各自新增 example/(修复 pana "No example found" 0/10):
    • flutter_miniprogram(CLI/构建期工具,不是库)用
      example/README.md:pubspec dev_dependency 片段 + dart run flutter_miniprogram + mp_flutter.yaml 示例,而不是可运行的 Dart 程序。
    • mp_flutter_wechat/mp_flutter_native 各自新增 example/lib/main.dart
      (MpWechat/MpVideo+MpMap 最小用法,均先判断
      isAvailable/mpNativeAvailable 再调用)与 example/pubspec.yaml
      (path: ../ 依赖本包)。
  • flutter_miniprogram 静态分析 40/50 → 补齐:新增
    analysis_options.yaml(include: package:lints/core.yaml,lints 已是
    dev_dependency),修完 dart analyze 报出的全部问题,含
    lib/src/transform/canvaskit_js.dart:29 的 "Angle brackets will be
    interpreted as HTML" dartdoc 提示(把那段正则示例文字框进代码块)。随之
    dart format lib bin 全量重排(当前 SDK 的新排版风格,纯格式改动,行为
    不变)。
  • 文档覆盖率:给 mp_flutter_wechat(MpPaymentParams/MpAddress/
    MpScanResult/MpLocation 各字段与构造函数)、mp_flutter_native
    (camera.dart/video.dart/map.dart 的公开字段、构造函数、控制器方法)
    补文档注释。
  • 三包版本号统一改为 0.3.2(flutter_miniprogram 的 kPackageVersion 常量
    同步更新,有测试校验两者一致)。

mp-flutter 0.3.1

Choose a tag to compare

@jackwangfeng jackwangfeng released this 01 Oct 08:39

E2E 驱动脚本随包发布

  • drive.js(驱动微信开发者工具跑 E2E 验收的脚本,含冷启动处理与几个
    开发者工具 CLI 版本兼容绕过)移入 packages/mp_flutter/tool/e2e/drive.js,
    随 flutter_miniprogram 包一起发布到 pub.dev
    (tool/ 未被 .pubignore
    排除,照常随包分发)。同目录新增 package.json(声明
    miniprogram-automator 依赖,与仓库原 tools/e2e/package.json 同版本)与
    一份独立 README.md(如何定位该目录、如何装依赖、runE2E 最小用法、
    冷启动/已知偶发问题说明)。
  • 仓库内 tools/e2e/drive.js 改为薄转发(module.exports = require('../../packages/mp_flutter/tool/e2e/drive.js')),
    tools/e2e/accept-*.js 的 require('./drive') 用法不变。
  • drive.js 对 miniprogram-automator 的解析改为懒加载(调用 runE2E 时
    才 require),解析顺序:正常 require 解析 → 实际被 node 执行的入口脚本
    所在目录(require.main.filename,覆盖本仓库 tools/e2e/node_modules 这种
    "入口脚本与依赖同目录、但转发到别处的 drive.js" 布局)→ 调用方
    process.cwd() → NODE_PATH。都找不到时报清晰的错误信息,提示
    npm i miniprogram-automator。
  • 新增 CLI 子命令 dart run flutter_miniprogram e2e-driver:打印随包
    分发的 tool/e2e 目录绝对路径(复用 runtime/ 已在用的包根解析器
    resolvePackageRoot()),退出码 0。推荐配方:
    DIR=$(dart run flutter_miniprogram e2e-driver)
    cp -R "$DIR" ./mp-e2e && (cd mp-e2e && npm i)
    然后 require('./mp-e2e/drive.js')。已加入 --help、根 README(中/英)与
    packages/mp_flutter/README.md。

mp-flutter 0.3.0 — pub.dev: flutter_miniprogram

Choose a tag to compare

@jackwangfeng jackwangfeng released this 01 Oct 05:30

Breaking:主包改名为 flutter_miniprogram

  • packages/mp_flutter 发布到 pub.dev 的包名由 mp_flutter 改为
    flutter_miniprogram
    (mp_flutter 这个名字在 pub.dev 上已被另一个包
    占用)。仓库/项目名、仓库里的目录名(packages/mp_flutter)、配置文件名
    (mp_flutter.yaml)都不受影响,仍是旧名字——只有 pubspec.yaml 的
    name、dev_dependencies 里引入它用的 key、package: import 前缀、
    以及 dart run 后面跟的可执行文件名变了:
    • dev_dependencies: { mp_flutter: ... } → dev_dependencies: { flutter_miniprogram: ... }
    • dart run mp_flutter → dart run flutter_miniprogram
    • import 'package:mp_flutter/...' → import 'package:flutter_miniprogram/...'
    • mp_flutter_wechat、mp_flutter_native 两个包名不变
  • 三个包(flutter_miniprogram、mp_flutter_wechat、mp_flutter_native)
    同步升到 0.3.0,补齐 pub.dev 发布所需的 homepage/repository/
    issue_tracker/topics/LICENSE/CHANGELOG.md,去掉 publish_to: none。

mp-flutter 0.2.5

Choose a tag to compare

@jackwangfeng jackwangfeng released this 01 Oct 04:53

0.2.5 — 2026-10-01

安卓真机文本输入修复:插入文字不回显(构建期自动改写正则 Unicode 属性转义)、安卓原生输入框
放到屏幕外避免双光标、输入期间按字段 diff 不整份 setData、iOS 原生光标隐藏、焦点嵌套修复、
键盘重弹兜底、退格/空值处理、--input-timing 诊断。

输入跟手(安卓)

  • 安卓原生输入框默认放到屏幕外(android_input: offscreen / --android-input,默认):安卓
    原生光标设不成透明,叠在 TextField 上会出现两根光标、两套字体排版越打越偏。现在只在安卓上把原生框
    水平移出可视区(left 固定 -2000px 起,top/宽/高照常跟随,键盘顶起页面照常),iOS 仍叠放;
    textarea 同样处理。平台取 wx.getSystemInfoSync().platform,--force-platform 构建按模拟的平台。
    overlay 恢复旧行为。TextField 区域的点击因此落到画布上,Flutter 自己挪光标再推给原生框。
  • 输入期间不再 setData:文本桥状态里 value/cursor 只在引擎主动改值、程序化挪光标时出现,
    视图同步按字段 diff,几何/焦点不变时一个字段都不发(之前每次按键整份 13 个字段 setData,真机 4–7ms)。
    引擎清空推回原生框等两步写逻辑保留,光标也走同样的两步写。
  • 修复:输入框聚焦时点画布,原生框消失、键盘收起:垫片焦点切换派发 blur 时,引擎
    handleBlur 同步把焦点抢回输入元素,外层随后又用过期的 flutter-view 通知了订阅者,文本桥以为
    失焦。现在嵌套切换时只按最终焦点通知一次;同一元素重新聚焦不算新会话。
  • 安卓组字期间的空 value 不清掉已上屏内容:一次事件从 ≥2 个字符直接变空(或 1→0 且带非退格
    keyCode)视为可疑并丢弃。微信 WebView 的 input 拿不到组字文本(见 support-matrix 第 15 条)。
  • 退格(keyCode 8)与无 keyCode 的空值一律照转:修复安卓输入法上屏后退格删不掉的问题。
  • 键盘重弹兜底:原生 blur 后 5s 内键盘升起而没有聚焦中的原生框时 wx.hideKeyboard()。
  • --input-timing(input_timing,默认关,关闭时不打包不注入):每个输入相关事件一行 [mp-t]
    ——触摸、引擎焦点、状态下发、setData(同步/回调)、原生 focus/blur/input(完整 e.detail 与
    e.timeStamp)、键盘高度、ParagraphBuilder.addText 出现这次输入文字(paint)、该帧 flush、
    下一个 rAF(估算上屏),并用 Date.now() - e.timeStamp 的最小值做基准估算按键到 JS 的延迟。

修复:安卓真机输入框插入文字不回显、iOS 双光标

  • 安卓真机输入框插入文字不回显(控制台 Illegal RegExp pattern (SyntaxError: Invalid regular expression: /\p{Space_Separator}/u: Invalid property name),之后一串 Another exception was thrown):
    安卓微信的 JS 引擎不带 ICU,不支持正则的 Unicode 属性转义 \p{…}/\P{…};Flutter 框架
    text_painter.dart 的 [\p{Space_Separator}\p{Punctuation}] 与 \p{Space_Separator} 在插入文字时
    构造,抛异常后这次更新丢掉,输入框直到失焦才刷新(删除不走这条路径)。开发者工具(Chromium)与
    iOS(JavaScriptCore)都支持,复现不了。现在构建期自动改写:js/unicode-props.js 扫描
    main.dart.js 的字符串/正则字面量,把属性转义展开成等价的 \u{…} 码点区间(区间由构建机
    Node 遍历全部码点实测,按字符类内外位置展开,\P 用补集),在分片之前执行;Node 也不认识的
    属性名构建失败(退出码 5,错误码 unknown-property)。
  • --force-platform android / android-noIntl 的垫片模拟"不支持 Unicode 属性转义"(包一层
    RegExp 构造函数,u 标志下遇到 \p{ 抛与真机同样的 SyntaxError),模拟器可复现;
    tools/e2e/run.sh 的这两个配置缺省跑 accept.js + accept-interact.js。
  • iOS 双光标:原生透明输入框的光标(微信默认绿色)仍然可见、且与 Flutter 画的光标对不上。
    <input>/<textarea> 加 cursor-color="#00000000"(基础库 3.1.0+,iOS 取十六进制色值;
    textarea 文档未列该属性,一并带上),WXSS caret-color: transparent 保留并加 !important。

真机验证

安卓(小米)、iPhone 15 均已真机验证:输入法上屏、退格、组字确认均正确回显,只剩一根光标,
按键到画面渲染约 10–25ms。模拟器配置 android / android-noIntl 现在模拟"不支持 \p{}",
默认跑 accept-interact 脚本。

mp-flutter 0.2.4

Choose a tag to compare

@jackwangfeng jackwangfeng released this 30 Sep 01:35

0.2.4 — 2026-09-30

冷启动优化五项,均可单独开关,外加分包下载/注入耗时的真机测量手段;这一版对真机首次
下载启动的实测结果如实记录在下面,总时长暂未改善。

  • 测量(--perf-hud):每个分包一行 [mp-boot] pkg <name> req=+x dl=+a..+b inject=c ready=+y size=…
    ——req/ready 是 boot 发出/等到 require.async 的时刻,dl 取 wx.getPerformance() 的
    loadPackage 条目(真实下载时间窗),inject 取 evaluateScript 条目(该分包 JS 注入耗时),
    均相对 App.onLaunch。API 不可用时对应字段静默省略。
  • 粗体合一字体首帧后才请求(cjk_font_bold_timing: after_first_frame,默认;eager = 旧行为):
    以前粗体(约 1.2MB)从第一毫秒就与 wasm/dart/常规字体抢带宽。现在首帧提交后才请求,首帧里的粗体
    一律合成加粗;字节到了经已有的晚到补注册路径(ui.loadFontFromList,一次 fontsChange)补上,
    补注册安排在空闲时(没有手指按着、最近 300ms 没有出帧,最多等 10s),避开滚动。
  • wasm 一到就编译(early_wasm,默认开):boot 把 pkg-wasm 单独拆出来,一到就
    WXWebAssembly.instantiate,与 dart 分包的下载/注入并行;"CanvasKit 先于垫片、垫片先于
    main.dart.js"的顺序不变。真机实测 CanvasKit 就绪比 Dart 完成早 0.85s。
  • preloadRule 改成 dart 优先(preload: auto|dart|wasm|none,CLI --preload,默认 auto = dart
    优先):dart 分包到了还要在 JS 线程上注入,先到才能和 wasm 下载重叠;wasm 恢复旧顺序,none
    不写 preloadRule。
  • 启动资源并进主包(boot_assets: auto|main|dart|package,默认 auto):FontManifest/AssetManifest/
    清单字体/Roboto 不再单独成 pkg-assets-boot 分包,并入主包(≤1200KB 时进 mp-assets-boot/),
    否则并进最小的 dart 分包,都放不下才单独成包;少一个首帧前分包请求。
  • 初始渲染缓存 + 按需注入(initial_rendering_cache、lazy_code_loading,默认开):承载页 json 写
    "initialRenderingCache": "static",第二次起冷启动时原生启动界面直接上屏(缓存只含 view/text,
    canvas 不显示,被启动界面盖住);app.json 写 "lazyCodeLoading": "requiredComponents"(只有一个
    真页面、没有自定义组件,收益很小,无害)。
  • 真机结果如实记录:iPhone 15、预览版、首次下载启动(冷设备,无缓存):3.45s → 3.43s,
    基本没变化——首帧前约 5.4MB、当前测试网络带宽 <2MB/s,是带宽瓶颈,不是本轮优化能解决的
    瓶颈;粗体字体确认已经挪到首帧之后下载;CanvasKit 比 Dart 提前 0.85s 就绪。命中缓存后的
    二次冷启动收益(initial_rendering_cache 等)尚未在真机上测量,留待下一轮。

mp-flutter 0.2.3

Choose a tag to compare

@jackwangfeng jackwangfeng released this 29 Sep 23:51

mp-flutter 0.2.3

着色器预热与表单/输入体验优化,减少长列表、大表单等场景的卡顿,并修复真机复测中发现的密码框、多指触控、文本桥唤醒等问题。

着色器预热

  • 首帧之后趁空闲(最近 150ms 未出帧且无手指按屏)在共享的 GrDirectContext 上预热常见绘制组合(文字、圆角裁剪图片、阴影、BoxShadow 模糊、渐变、半透明层、BackdropFilter 等),让 GL program 提前编译;有动画/滚动/触摸即暂停
  • 每片时间预算 + 重组合拆分:轻项一片最多画 8ms 让出主线程;重项(阴影 opaque/transparent、BoxShadow/BackdropFilter 按核宽拆分、颜色矩阵/混合)改为要求最近 1s 内无刷新、无触摸才画,每片一个,解决真机单片 426ms 的长任务
  • 新增 shader_warmup_light(默认关,--shader-warmup-light)只画轻项、跳过全部重项

iOS 输入体验

  • iOS 光标常亮(EditableText.debugDeterministicCursor),避免聚焦期间光标闪烁动画导致的持续 60fps 出帧;应用可在 main() 里设回 false 恢复闪烁
  • 修复:常亮光标关闭了 iOS 光标 tick 后,密码框最后一位可能明文常驻直到下一次输入或失焦;现在聚焦密码框时临时恢复 tick,失焦后再关闭

文本输入桥

  • 聚焦后 500ms 内无几何/值变化时轮询从 16ms 退到 100ms,有变化立即恢复
  • 新增触摸画布、键盘高度变化时主动 wake(),缩短退避后引擎侧变化同步到原生输入框的最坏延迟
  • 修复着色器预热的多指触控计数:直接取 touches.length 而非按 touchstart/touchend 事件次数 ±1,避免两指分开按下、一起抬起时计数卡死导致预热一直暂停

--perf-hud

  • 长帧明细拆出框架各阶段(dart=build/layout/paint/comp/…)、光栅化 raster=、文本桥 tb=/setData=、resize=
  • 每个 GL program 一行摘要(编译耗时 + attribute/uniform + 源码哈希);着色器预热逐项打点,汇总行含 heavy/skipped 计数

真机复测(iPhone 15)

  • 长列表最长帧 1061→97ms,>100ms 帧 2→0,fps 43→56
  • 大表单 >100ms 帧 6→4
  • 冷启动 3.76→3.45s

详见 docs/capability-guide.md 2.1 节。

mp-flutter 0.2.2

Choose a tag to compare

@jackwangfeng jackwangfeng released this 29 Sep 16:03

mp-flutter 0.2.2

新增仓库自带的真机压测页与测量工具,并把首批真机压测数据整理成一篇能力边界文档;不涉及运行时/构建管线代码改动。

压测页与测量工具

  • 压测页(example/lib/stress):只在编译时加 --dart-define=MP_STRESS=true 才会进最终产物,不加时对产物体积/行为零影响(main.dart 按这个编译期常量二选一 runApp,dart2js 会摇树掉未选中的分支)。首帧自动依次跑完七项场景:长列表(A)、图片墙(B)、长图文一次性构建(C)与懒加载对照(D)、大表单(E)、视觉效果(F)、原生组件(G),每项打印一行 [mp-stress](首帧、fps、最长帧、jank 帧数等)
  • tools/e2e/accept-stress.js:自动构建 example(带 MP_STRESS=true 与 --perf-hud)、在微信开发者工具里跑完整套 A–G,收集 [mp-stress]/[mp-perf] 遥测行并打印汇总表,断言七项均有输出、无 console error
  • tools/e2e/test-server.js:新增 /stress/img/:id/:w(按 id/宽度确定性生成纯色 PNG,压测页图片墙用,不依赖外网图床)与 /stress/redirect/:id/:w(302 跳转到前者,验证图片跳转后仍能正常解码)

文档

  • 新增 docs/capability-guide.md「能力边界与复杂页面指南」:iPhone 15 / 安卓真机压测数据(七项场景的首帧/fps/最长帧)、一个真实电商小程序的线上基线、"能撑多大"的可操作经验值(懒加载列表行数不受限、一次性构建文字建议控制在约 1500 字/50 段以内、表单超过 10–15 个输入框建议分步、大面积 BackdropFilter/阴影会把 iOS 帧率拉到约 30fps 等)、特别贵的写法与推荐替代方案
  • README、docs/support-matrix.md 补充指向新指南的链接

安装

mp_flutter:
  git:
    url: https://github.com/jackwangfeng/mp-flutter.git
    path: packages/mp_flutter
    ref: v0.2.2

完整历史见 CHANGELOG.md。

🤖 Generated with Claude Code

mp-flutter 0.2.1

Choose a tag to compare

@jackwangfeng jackwangfeng released this 28 Sep 11:57

mp-flutter v0.2.1 发布说明(本地准备,未推送)

仓库:/Users/jeff/work/mp-flutter-public(main,remote origin 未 push)
基于:v0.2.0(031daab + 87dd015),对应内部仓库 4e78c7e → 3468c66 的差异

本地提交:d3bb1e7 "mp-flutter 0.2.1"(author jeff <jackwangfeng163@gmail.com>,
trailer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>)
本地标签:v0.2.1(annotated,未推送)

变更内容

合成加粗

  • 常用汉字合一字体新增真粗体子集 cjk_font_bold(level1 | full | false,
    --cjk-font-bold=...,默认跟随 cjk_font,必须与其同档):Noto Sans SC v37
    Bold 子集,与常规合一字体同 family、字重 700 注册,w≥600 中文不再由 CanvasKit
    合成加粗;独立分包 pkg-cjkb(full 约 1.17MB / level1 约 660KB br)
  • --perf-hud 新增 fakeBold=N(本帧合成加粗段落数)统计与 cjk-bold 系列
    首帧前诊断行
  • 实测:iOS 真机详情页首次打开的 layout 长帧从 102–243ms 降到 4–7ms(典型值),
    冷启动耗时不受影响(约 3.9s,与 0.2.0 持平)

E2E

  • tools/e2e/drive.js:微信开发者工具基础库升至 3.17.4 后 Page.callMethod
    这条自动化 RPC 本身损坏;绕开为改走 mp.evaluate()(App.callFunction),
    在 App Service 层用 getCurrentPages() 拿真实页面实例直接调用同名方法;
    另加"句柄过期自动重取重试"通用兜底

版本号

  • pubspec.yaml(mp_flutter/mp_flutter_native/mp_flutter_wechat)与
    kPackageVersion 在 v0.2.0 时即为 0.1.0(未跟随发布 tag),本次同样未改动
  • README(根 + 3 个包)安装片段的 ref 由 v0.2.0 改为 v0.2.1
  • CHANGELOG 新增独立 ## 0.2.1 段落(未并入 0.2.0 段落)

处理过程要点

  1. git -C /Users/jeff/work/mp-flutter diff 4e78c7e 3468c66 -- . ':!docs/superpowers' ':!spike' ':!docs/history'
    生成 1804 行 diff,涉及 25 个文件(23 文本 + 2 新二进制字体)
  2. git apply --3way 因公开仓库与内部仓库无共同 blob 历史而全部退化为直接应用;
    由于二进制新文件与 docs/support-matrix.md 一处 hunk 冲突(该行此前已被脱敏
    替换过 "keel"→"真实电商小程序",导致上下文不匹配),改用 git apply --reject
    应用 22 个文本文件,docs/support-matrix.md 的 1 个 hunk 手工合并(保留已脱敏
    措辞 + 新增行),2 个新字体二进制文件直接从内部仓库复制
  3. 全仓库重新 grep 脱敏关键词:wxc6c33e38dbeed25f/keel/zzss/192.168./
    /Users///private/tmp/claude-501 均为 0 命中;jeff 仅剩 NOTICE 版权行
    2 处(允许项)。新引入内容里发现 3 处新的 "keel" 残留(CHANGELOG ×2、
    README ×1),已替换为 "该真实电商小程序"/"真实电商小程序首页"
  4. CHANGELOG 的"合成加粗"段落原样 patch 后落在 ## 0.2.0 标题下(因为内部仓库
    与公开仓库的 CHANGELOG 结构不同导致 diff context 巧合匹配);按任务要求手工
    挪到新增的 ## 0.2.1 独立段落,并补充 E2E drive.js 条目与实测数据
  5. bash tools/ci/check.sh(PATH 前置 /Users/jeff/development/flutter/bin)
    首次跑需先对 4 个包各跑一次 flutter pub get / dart pub get(.dart_tool
    未随 git 跟踪,构建产物均在 .gitignore 内,未提交)。全部检查通过,
    example 构建冒烟产出 pkg-cjkb(1170KB)分包,证实真粗体子集正确构建
    且各分包均在 2048KB 限内

验证结果

  • 远程 grep 复查:0 命中(除 NOTICE 版权行外)
  • tools/ci/check.sh:EXIT=0,全部检查通过
  • 未 push、未推送 tag,仅本地提交 + 本地 tag

mp-flutter 0.2.0

Choose a tag to compare

@jackwangfeng jackwangfeng released this 28 Sep 10:35

在 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 秒一次)