首页 » 网站推广 » php分包技巧_一个编译问题带你理解 Flutter Web 的打包构建和分包实现

php分包技巧_一个编译问题带你理解 Flutter Web 的打包构建和分包实现

duote123 2024-11-20 0

扫一扫用手机浏览

文章目录 [+]

对付 deferred-components 官方起初紧张是用于支持 Android App Bundle 上的动态发布,而经由适配后这项能力被很好地拓展到了 Web 上,通过 deferred-components 可以方便地根据需求来拆分 main.dart.js 文件的大小。

当然这里并不是先容如何利用 deferred-components ,而是在利用 deferred-components 时,碰着了一个关于 Flutter Web 在打包构建上的神奇问题。

php分包技巧_一个编译问题带你理解 Flutter Web 的打包构建和分包实现

首先,代码如下图所示,可以看到,这里紧张是通过 deferred as 关键字将一个普通页面变成 deferred-components ,然后在路由打开时通过 libraryFuture 加载后渲染页面。

php分包技巧_一个编译问题带你理解 Flutter Web 的打包构建和分包实现
(图片来自网络侵删)

这里省略了无关的 yaml 文件代码,那么上述简单的代码,大家以为有没有什么问题

一开始我也以为没什么问题, 通过 flutter run -d chrome --web-renderer html 运行到浏览器调试也没问题,页面都可以正常加载打开,但是当我通过 flutter build web --release --web-renderer html 打包支配到做事器后,打开时却碰着了这个问题:

Deferred library scroll_listener_demo_page was not loaded.main.dart.js:16911 at Object.d (http://localhost:64553/main.dart.js:3532:3)main.dart.js:16911 at Object.aL (http://localhost:64553/main.dart.js:3690:34)main.dart.js:16911 at asV.$1 (http://localhost:64553/main.dart.js:54352:3)main.dart.js:16911 at pB.BE (http://localhost:64553/main.dart.js:36580:23)main.dart.js:16911 at akx.$1 (http://localhost:64553/main.dart.js:51891:10)main.dart.js:16911 at eT.t (http://localhost:64553/main.dart.js:47281:22)main.dart.js:16911 at Cw.bp (http://localhost:64553/main.dart.js:48714:51)main.dart.js:16911 at Cw.ih (http://localhost:64553/main.dart.js:48691:9)main.dart.js:16911 at Cw.rz (http://localhost:64553/main.dart.js:48659:6)main.dart.js:16911 at Cw.zk (http://localhost:64553/main.dart.js:48689:11)复制代码

这就很奇怪了,明明 debug 运行时没有问题,为什么 release 发布就会 not loaded 了?

经由大略调试和打印创造,在出错时期码时根本进入不到 ContainerAsyncRouterPage 这个容器里,也便是在外部就涌现了 not loaded非常,但是明明 widget 是在 ContainerAsyncRouterPage 容器内才调用,为什么会在外部就抛出 not loaded 的非常?

通过非常信息比对源码创造,编译时在对付 deferred as 进行处理时,会插入一段 checkDeferredIsLoaded 的检讨逻辑,以是抛出非常的代码是在编译期时处理 import deferred as 时添加。

通过查看打包后的文件,可以看到如果在 checkDeferredIsLoaded 之前没有完成加载,也便是对应 importPrefix 没有被添加到 set 里,就会抛出非常。

以是初步推断,问题该当是涌如今 debug 和 release 时,对付 import deferred as 的编译处理有不同之处。

二、构建差异

通过资料可以创造,Flutter Web 在不同编译期间会利用 dartdevc 和 dart2js 两个不同的编译器,而如下图所示,默认 debug 运行到 chrome 时采取的是 dartdevc ,由于 dartdevc 支持增量编译,以是可以很方便用 hot reload 来调试,通过这种办法运行的 Flutter Web 并不会在 build 目录下天生 web 目录,而是会在 build 目录下天生一个临时的 .cache.dill.track.dill 用于加载和更新。

.dill 属于 Flutter 编译过程的中间文件,该文件一样平常是二进制的编码,如果想要查看它的内容,可以在完全版 dart-sdk 的/Users/xxxxx/workspace/dart-sdk/pkg/vm/bin 目录下)实行 dart dump_kernel.dart xxx.dill output.dill.txt 查看,把稳是完全版 dart-sdk 。

而 Flutter Web 在 release 编译时,如下图所示,会经由 flutter_tools 的 web.dart 内的对应配置逻辑进行打包,利用的是 dart2js 的命令,打包后会在 build 下天生包含 main.dart.js 等产物的 web目录,而打包过程中的产物,例如 app.dill 则是存在 .dart_tool/flutter_build/一串特殊编码/ 目录下。

.dart_tool/flutter_build/ 目录下根据编译平台会输出不同的编译过程目录,点开可以看到是带 armeabi-v7a 之类的一样平常是 Android 、带有 .framework 的一样平常是 iOS ,带有 main.dart.js 的一样平常是 Web 。

而打开 web.dart 文件可以看到很多可配置参数,个中关键的比如:

--no-source-maps : 是否须要天生 source-maps ;-O4 :代表着优化等级,默认便是 -O4,dart2js 支持 O0-O4,个中 0 表示不做任何优化,4 表示优化开到最大;--no-minify : 表示是否稠浊压缩 js 代码,默认 build web --profile 就可以关闭稠浊;

以是到这里,我初步疑惑是不是优化等级 -O4 带来的问题,但是正常情形下,Flutter 打包时的 flutter_tools 并不是利用源码路径,而是利用以下两个文件:

/Users/xxxx/workspace/flutter/bin/cache/flutter_tools.stamp

/Users/xxxx/workspace/flutter/bin/cache/flutter_tools.snapshot

难道就为了改个参数就去编译全体 engine ?这样肯定是不值得的,所幸的是官方供应了利用源码 flutter_tools 编译的办法,同样是在项目目录下,通过一下办法就可以用 flutter_tools 源码的形式进行编译:

dart ~/workspace/flutter/packages/flutter_tools/bin/flutter_tools.dart build web --release --web-renderer html

而在源码里直接将 -O4 调度了 -O0 之后,我创造编译后的 web 居然无法正常运行,但是基于编译后的产物,我可以直接比对它们的差异,如下图所示,左边是 O0,右边是O4:

-O0 之后为什么会无法运行有谁知道吗?

首先可以看到, O4 确实做了不少优化从而精简了它们的体积,但是在关键的 loadDeferredLibrary 部分基本一样,以是问题并不是涌如今这里。

但是到这里可以创造其余一个问题,由于 loadDeferredLibrary 方法是异步的,而从编译后的 js 代码上看,在实行完 loadDeferredLibrary 之后立时就进入到了 checkDeferredIsLoaded ,这显然存在问题。

那为什么 debug 可以正常实行呢? 通过查看 debug 运行时的 js 代码,我创造同样的实行逻辑,在 dartdevc 构建出来后居然完备不一样。

可以看到 checkDeferredIsLoaded 函数和对应的 Widget 是被一起放在逗号表达式里,以是从实行时序上会是和 Widget 在调用时被一起被实行,也便是在 loadDeferredLibrary 之后,以是代码可以正常运行。

通过断点调试也验证了这个时序问题,在 debug 下会先走完 loadDeferredLibrary 的全部逻辑,之后再进入 checkDeferredIsLoaded 。

而在 release 模式下,代码虽然也会前辈入 loadDeferredLibrary , 但是会在 checkDeferredIsLoaded 实行之后才进入到 add(0.this.loadId) ,从而导致前面的非常被抛出。

那到这里问题基本就很清楚了,前面的代码写法在当前(2.10.3)的 Flutter Web 上,经由 dart2js 的 release 编译后会涌现某些时序不一致的问题,知道了问题也很好办理,如下代码所示,只须要把原来代码里的 Widget 变成 WidgetBuilder 就可以了。

我们再去看 release 编译后的 js 文件,可以看到此时的由于多了 WidgetBuilder ,传入的内容变成了 closure69 ,这样就可以担保在调用到 call 之后才触发 checkDeferredIsLoaded 。

三、末了

虽然这个问题不难办理,但是通过这个问题去理解 dart2js 的编译和构建过程,可以看到很多平时不会打仗的内容

末了

如果你以为此文对你有一丁点帮助,点个赞。
或者可以加入我的开拓互换群:1025263163相互学习,我们会有专业的技能答疑解惑

如果你以为这篇文章对你有点用的话,麻烦请给我们的开源项目点点star:http://github.crmeb.net/u/defu不胜感激 !

完全源码下载地址:https://market.cloud.tencent.com/products/33276

PHP学习手册:https://doc.crmeb.com技能互换论坛:https://q.crmeb.com

标签:

相关文章

上海新界华欣php技巧_内蒙贷融资破4亿

本月,“内蒙贷”正在积极为“包头市青少年足球演习中央及学校运动场馆”项目筹集借款期6个月、年化收益率8%的7000万元短期流动资金...

网站推广 2024-12-12 阅读0 评论0