通过Tree shaking和deferred loading来优化Flutter Web应用程序的性能

为了获得最佳的用户体验,应用程序的加载速度非常重要。Flutter Web应用程序的初始加载时间可以通过最压缩其JavaScript包的大小缩短。Dart编译器包含诸如Tree shaking和deferred loading(延迟加载)之类的功能,它们都可以压缩JavaScript。本文介绍了它们的工作方式以及如何在应用程序中使用它们。

默认情况下的Tree shaking

编译Flutter Web应用程序时,JavaScript由dart2js编译器生成。发布版本具有最高级别的优化,其中包括通过tree shaking删除无用代码。

Thee shaking是消除无效代码的过程,只包含会被执行的代码。这意味着您无需担心应用程序包含的库的大小,因为未使用的类或函数将从已编译的JavaScript包中排除。

我们来看一个tree shaking示例:

  1. 创建一个dart文件greeter.dart:

        abstract class Greeter {
          String greet(String name);
        }
        class EnglishGreeter implements Greeter {
          String greet(String name) => 'Hello $name!';
        }
        class SwedishGreeter implements Greeter {
          String greet(String name) => 'Hej $name!';
        }
        void main() {
          print(EnglishGreeter().greet('World'));
        }
    
  2. 在您的终端中运行dart2js -O4 greeter.dart,然后查看生成的输出out.js。
    在生成的JavaScript代码中,没有任何对SwedishGreeter类的引用,也没有任何字符串Hej $ name的引用,因为在编译器tree shaking时已将其删除。

    编译器只能通过静态分析找出哪些代码可以访问,哪些是不会被访问的代码。

    以以下示例为例,其中根据系统区域设置定义了问候语:

        Locale locale = Localizations.localeOf(context);
        if (locale.languageCode == 'sv') {
           greeter = SwedishGreeter();
        } else {
           greeter = EnglishGreeter();
        }
    

    编译器不知道用户的系统区域设置,因此EnglishGreeter和SwedishGreeter都包含在JavaScript包中。对于此类用例,deferred loading可以帮助减小初始js包大小。

仅在需要时才使用延迟加载来加载代码

Deferred loading,也称为lazy loading(惰性加载),允许您在需要时加载库。如果应用程序中有很多不是很常用的功能,适合使用延迟加载。请注意,延迟加载是dart2js功能,因此不适用于Flutter移动应用程序。下面是个简单的例子,将导入的程序包或文件标记为deferred,然后等待其加载后再使用:

import 'greeter.dart' deferred as greeter;
void main() async {
  await greeter.loadLibrary();
  runApp(App(title: greeter.EnglishGreeter().greet('World')));
}

编译此代码将生成两个JavaScript文件。在延迟导入上调用loadLibrary时,它将加载greeter库。

在Flutter(一切都widget)中,您可能要使用FutureBuilder。widget的构建方法是同步的,因此您不能在build方法内部的loadLibrary上调用await。但是,您可以在build方法中返回FutureBuilder,也可以在加载库时使用它来显示不同的UI:

import 'greeter.dart' deferred as greeter;
FutureBuilder(
  future: greeter.loadLibrary(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.done) {
      return Text(greeter.greet('World'));
    } else {
      return Text('Loading...');
    }
  },
)

要自己尝试(请参阅GitHub上的完整示例),请打开Chrome DevTools并单击“网络”标签以查看网络请求。重新加载页面以查看何时加载和导入库。在以下屏幕快照中,延迟了main.dart.js_1.part.js文件的加载:

Flutter Gallery支持70多种语言,但大多数用户只使用一种。延迟加载本地化字符串是此功能的一个很好的用途。例如,在Flutter Gallery中实施了本地化字符串的延迟加载后,该应用程序的初始JavaScript包大小减少了一半。如果Flutter Web应用程序中有很多本地化字符串,请考虑延迟加载这些文件。gen_l10n.dart脚本为此包含标志--use-deferred-loading(当前仅在1.19 SDK master channel可用)。

这篇文章是关于我们在提高Flutter Gallery性能时所学知识的系列文章的一部分。我希望您发现它有用,并且您学到了一些可以应用于Flutter Web应用程序的知识!

译自:http://dd.ma/3TQEY627