Flutter Web在美团外卖的实践 - 掘金

让项目支持web :flutter create .

运行:flutter run -d chrome

运行指定渲染方式:flutter run --web-renderer html -d chrome

打包:flutter build web --release --web-renderer html

官方链接:Flutter on the Web

参考视频:构建 Flutter web 应用 (From Mobile App to web App)_哔哩哔哩_bilibili

部署参考链接:「Flutter for Web 」创建与部署 - 简书

发布到GitHub上:使用Flutter Web和GitHub Actions构建发布自己的Github Pages网站 - 简书

前端项目自动部署GitHub Pages_哔哩哔哩_bilibili

docker官网:Docker 教程 | 菜鸟教程


去除链接URL中的"#"

1.在pubspec.yaml文件中添加如下代码:
 
 # 去除网页URL中的“#”(hash) https://flutter.cn/docs/development/ui/navigation/url-strategies
  
url_strategy: 0.2.0
 
 
2.在main.dart文件中添加如下代码:
import 'package:url_strategy/url_strategy.dart';
 
 
  /// 去除URL中的“#”(hash),仅针对Web。默认为setHashUrlStrategy
  /// 注意本地部署和远程部署时`web/index.html`中的base标签,https://github.com/flutter/flutter/issues/69760
 
  setPathUrlStrategy();

dio网络请求配置:

if (!Utils.isWeb) {
      // https://developer.github.com/v3/#user-agent-required
      options.headers['User-Agent'] = 'Mozilla/5.0';
    }

Flutter Web使用手机浏览器调试

在android studio中只有电脑端的chrome,如果想用手机上的浏览器调试项目,需要本地部署一下:

flutter run --web-renderer html -d web-server --web-hostname 192.168.1.15 --web-port 8080 --release

ip地址换成你电脑的ip地址即可,然后手机和电脑在同一网络环境下,就可以输入上面的ip+端口号访问web项目了,比如:在手机浏览器输入:192.168.3.156:8080

flutter web渲染器相关

html : 通过平台的 canvas 和 Element 完成布局绘制;
canvaskit : 通过 Webassembly + Skia 绘制控件;

虽然都知道 canvavskit 更接近 Flutter 的设计理念,但是由于它构建的 wasm 文件大小和字体加载等问题带来的成本考虑,业界一般会选用更轻量化的 html 引擎

官方默认使用渲染器:
1.手机浏览器:HTML。CSS,Canvas元素和SVG元素。 该渲染器的下载大小较小。
2.桌面浏览器:Canvaskit。使用Skia编译为WebAssembly并使用WebGL渲染。 该渲染器与Flutter移动设备和台式机完全一致,具有更快的性能,并且不太可能在浏览器之间出现差异,但下载大小增加了大约2MB。
指定浏览器:
flutter run -d chrome --web-renderer html
 
flutter run -d chrome --web-renderer canvaskit

参考:超详细,Flutter2.0构建Web应用的实际体验|技术点评 - 掘金

首次加载过慢问题优化(倒序)主要文件:CupertinoIcons.ttf  main.dart.js 和 MaterialIcons-Regular.otf

总结:

  • 去除无用的 icon 引用;(flutter3.10版本看介绍说减小了图标字体的文件大小,它会从Material和Cupertino中删除了未使用的字形)

  • 使用 tree-shake-icons 优化引用矢量图库;

  • 通过 deferred-components 实现懒加载分包;

  • 开启 gzip 等压缩算法压缩  main.dart.js ;

7.通过外部手段(没试过):开启 gzip 等压缩算法压缩  main.dart.js 

例如通过在部署时开启 gzip 或者 brotli 压缩,开始 gzip 后大概可以让 main.dart.js 下降到 400k 左右 

 6.MaterialIcons-Regular.otf优化

虽然在项目中我们会使用到 MaterialIcons 的一些矢量图标,但是每次加载都要全量加载一个 1.5 MB 的字体库文件显然并不符合逻辑,所以我们可以先运行 flutter build apk ,然后通过如下命令,将 Android 上已经 shake-icons 的 MaterialIcons-Regular.otf 资源复制到已经编译好的 web/ 目录下。

cp -r ./build/app/intermediates/flutter/release/flutter_assets/ ./build/web/assets

优化大小:1.5mb   优化后大小:3kb

 5.main.dart.js拆包&优化:通过 deferred-components 实现懒加载分包;

等待过程优化可在一定程度上提升等待体验,单治标不治本,要想加载快还得让加载的资源小,对于多页面应用,可以将整个 main.dart.js 拆分成多个小的包,在使用的过程中逐步加载,目前了解到美团有相应的技术,但实现细节未知,有待研究。

//引入
import '全路径/splash_page.dart'deferred as splash_page;


//路由
RouterConstants.pageRoot: (context) => ContainerAsyncRouterPage(splash_page.loadLibrary(), (context) {
    return splash_page.SplashPage();
  })

///延时加载
class ContainerAsyncRouterPage extends StatelessWidget {
  final Future libraryFuture;

  ///不能直接传widget,因为 release 打包时 dart2js 优化会导致时许不对
  final WidgetBuilder child;

  ContainerAsyncRouterPage(this.libraryFuture, this.child);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: libraryFuture,
        builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            if (snapshot.hasError) {
              return Scaffold(
                appBar: AppBar(),
                body: Container(
                  alignment: Alignment.center,
                  child: Text(
                    'Error: ${snapshot.error}',
                    style: const TextStyle(color: Colors.red),
                  ),
                ),
              );
            }
            return child.call(context);
          }
          return Scaffold(
            appBar: AppBar(),
            body: Container(
              alignment: Alignment.center,
              child: const CircularProgressIndicator(),
            ),
          );
        });
  }
}

//pubspec.ymal文件中添加
#  延时加载
deferred-components:
  - name: crane
    libraries:
      - package:全路径/splash_page.dart

可参考 github.com/flutter/flu…

http://t.csdn.cn/hgaUO

大前端时代的乱流:带你了解最全面的 Flutter Web

Flutter 3.10 Web的改动

Flutter Web : 一个编译问题带你了解 Flutter Web 的打包构建和分包实现

Deferred components

Flutter Web 在《一起漫部》的性能优化探索与实践

一个命令行工具,针对flutter web加载慢和缓存问题提供了一套解决方案。

webGPU

4.等待过程优化

页面在 js 加载完成之前都是白屏,给人一种页面卡死的感觉,为此可以在 js 加载完成前增加加载动画不至于让页面一直白屏。参考App上管用的做法,可在数据加载出来之前插入骨骼屏,实现如下:

<iframe src="https://g.alicdn.com/algernon/alisupplier_content_web/0.9.1/skeleton/index.html"
        id="iFrame" frameborder="0" width="100%" height="100%" scrolling="no" onload="setIframeSize()"></iframe>
<script>
    function setIframeSize() {
      <!-- 骨骼屏尺寸设置,占满全屏 -->
      var iframe = document.getElementById("iFrame");
      iframe.height =  document.documentElement.clientHeight;
    }
    function removeIFrame() {
      var iframe = document.getElementById("iFrame");
      iframe.parentNode.removeChild(iframe);
    }

<!--   onresize属性可以用来获取或设置当前窗口的resize事件的事件处理函数-->
<!--  onresize事件会在窗口或框架被调整大小时发生-->
    window.onresize = function() {
           setIframeSize();
    }





</script>

<!-- load 完成之后移除骨骼屏 -->
<script type="text/javascript" onload="removeIFrame()"></script>

或者:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>gsy_flutter_demo</title>
  <style>
    .loading {
      display: flex;
      justify-content: center;
      align-items: center;
      margin: 0;
      position: absolute;
      top: 50%;
      left: 50%;
      -ms-transform: translate(-50%, -50%);
      transform: translate(-50%, -50%);
    }

    .loader {
      border: 16px solid #f3f3f3;
      border-radius: 50%;
      border: 15px solid ;
      border-top: 16px solid blue;
      border-right: 16px solid white;
      border-bottom: 16px solid blue;
      border-left: 16px solid white;
      width: 120px;
      height: 120px;
      -webkit-animation: spin 2s linear infinite;
      animation: spin 2s linear infinite;
    }

    @-webkit-keyframes spin {
      0% {
        -webkit-transform: rotate(0deg);
      }
      100% {
        -webkit-transform: rotate(360deg);
      }
    }

    @keyframes spin {
      0% {
        transform: rotate(0deg);
      }
      100% {
        transform: rotate(360deg);
      }
    }
  </style>
</head>
<body>
  <div class="loading">
    <div class="loader"></div>
  </div>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

参考链接:https://juejin.cn/post/7068533637364334622

1.渲染引擎 Canvaskit 下载太慢,这个Canvaskit是从https://unpkg.com去加载的,需要翻墙

解决方案:1.使用镜像 2.下载canvaskit.js 和 canvaskit.wasm 这两个文件放到自己的服务器,或其它高速服务器  3.指定使用html渲染器

下载四个文件(参考:10.1.1.flutter-web内网 - 简书)

(1)canvaskit.js和canvaskit.wasm
下载地址: https://unpkg.com/browse/canvaskit-wasm@0.24.0/bin/
(2)KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf
下载地址: https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf
(3)css2?family=Noto_Sans+SC
下载地址: https://fonts.googleapis.com/css2?family=Noto+Sans+SC
(4)将以上四个文件放到web/assets/canvaskit/文件夹中 

通过命令行直接运行:(release/debug)

flutter run --web-renderer canvaskit --dart-define=FLUTTER_WEB_CANVASKIT_URL=web/assets/canvaskit/ --release -d chrome

打包命令

flutter build web --web-renderer canvaskit --dart-define=FLUTTER_WEB_CANVASKIT_URL=web/assets/canvaskit/ --release 

2.字体问题Flutter web默认使用Noto字体,一般电脑上都不会带有这个字体,所以会在线加载这个字体,导致加载缓慢还会产生乱码

解决方案:使用自定义字体,把字体资源放到项目中,然后使用字体:

flutter:
  fonts:
    - family: Raleway
      fonts:
        - asset: assets/fonts/Raleway-Regular.ttf
        - asset: assets/fonts/Raleway-Medium.ttf
        - asset: assets/fonts/Raleway-SemiBold.ttf

3、去除无用的 icon 引用

若只是个人网页之类的简单web项目,可以删除一些系统的资源包,pubspec.yaml中cupertino_icons: ^1.0.2,uses-material-design: true注释掉,这样可以少加载这些资源文件。

参考:

10.1.1.flutter-web内网 - 简书

解决 Flutter Web 加载canvaskit.wasm过慢的方法 - 简书

 https://sb.sb/blog/css-cdn/
解决 Flutter Web 加载慢的问题_Amoour的博客-CSDN博客_flutter web 加载慢
Flutter Web 常见问题及兼容性处理 - 掘金

flutter web 的路径模式和hash模式

参考:最小单元 flutter for webURL导航与hashtag(#)的处理 - 简书

flutter web开发遇到的问题

1.flutter web项目上线后,点击刷新按钮(F5)报错404问题(使用nginx)

原因:我项目中使用的是路径模式,在刷新页面(或单个页面链接单独访问)时将会请求当前的链接,而Nginx无法找到对应的页面

解决:使用Nginx配置,让所有路由(url)下的页面重写到 index.html即可,在Nginx配置文件nginx.conf中加入如下配置。(index.html根据实际页面配置)

location / {

  try_files $uri $uri/ /index.html;
}

参考:Vue项目上线后,点击刷新按钮(f5)报错404的问题(使用nginx)_Honins的博客-CSDN博客_vue项目f5刷新404

flutter web 访问url去除#号以及去除后直接访问指定地址404处理_flutter_安卓小学生-华为云开发者联盟

2.文字不能选择

解决:使用SelectableText

3.图片不能拖拽(暂时没解决)

4.标题栏自带的返回按钮

解决:系统的AppBar(
        title: Text('景区购票'),
        automaticallyImplyLeading: false,//去掉自带的返回按钮
      )

参考:flutter web遇到的坑 - HemJohn - 博客园

在本地网络中,可以在任意浏览器中访问,不会存在跨域资源访问问题解决参考:Flutter工具之shelf_proxy

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐