Flutter web 首页加载优化

2,850 阅读3分钟

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

flutter web 首页加载优化

目前存在的问题:

1.功能无法及时更新:浏览器对同名文件的缓存,可能导致程序代码不被及时更新或者执行错乱。

2.首屏渲染性能差main.dart.js 文件过大,单一文件解析加载时间过长,影响首屏渲染

3.无法使用CDN:Flutter 仅支持相对路径的加载方式,无法使用当前域名以外的CDN域名,导致无法享受CDN带来的优势

功能无法及时更新

功能无法及时更新时因为浏览器对同名文件的缓存,可能导致程序代码不被及时更新或者执行错乱

Flutter 编译过程

Flutter build web -> flutter engine 源码 -> AOT产物 -> 中间产物 -> 静态资源

我们在AOT产物形成之前做优化,从以下三个方面解决:

  1. 资源文件hash化

  2. 大文件分片

  3. 资源文件CDN化

资源文件hash化

除了 web/index.html 文件之外,我们要对所有的引用到文件进行 Hash 化。对 build_system/web.dart 的修改按以下步骤进行:

  1. 遍历产物目录,并建立 ResourceMap
  2. 分别计算每个文件的 Hash 值。
  3. 为新文件命名为 name-[hash].xxx
  4. 修改新文件名在对应文件中的引用关系。

查看build_system/web.dart源码,没有找到合适的添加地方

暂采用Python脚本实现资源hash化,整个过程也是一样

1.遍历目录,找到dart.js 、part.js结尾的文件

2.计算hash

3.拼接处带有hash值的文件名

4.替换main.dart.js / index.html / flutter_service_worker.js文件中的文件名,换成带有hash值的

5.将part.js文件在index.html中主动声明

6.将dart.js 、part.js结尾的文件的文件名称更改为带hash值的

7.压缩

具体脚本如下:

#!/usr/bin/python
# coding:utf-8

import hashlib
import os, sys
import zipfile

print("\n ======== Start hash resource ======== \n")

#计算md5值
def CalcMD5(filepath):
    with open(filepath,'rb') as f:
        md5obj = hashlib.md5()
        md5obj.update(f.read())
        hash = md5obj.hexdigest()
        return hash

#给文件名加hash值
def createHashName(oldFileName):
    hash = CalcMD5(oldFileName)
    if (oldFileName.endswith('dart.js')):
            return oldFileName.replace('dart.js',hash+'.dart.js')
    if (oldFileName.endswith('part.js')):
            return oldFileName.replace('part.js',hash+'.part.js')


# 查看当前工作目录
currentPath = os.getcwd()

# 修改当前工作目录
path = "./build/web"
os.chdir( path )
webPath = os.getcwd()

print("找到.js结尾的文件并拼接hash")
jsFileList = []
jsFileHashMap = {}
pathList = os.listdir(webPath)
for path in pathList:
    if (path.endswith("dart.js") | path.endswith("part.js")):
        jsFileList.append(path)
        jsFileHashMap[path] = createHashName(path)

print(jsFileHashMap)

#检查文件,替换文件中的一些文件引用
def checkFile(file):
    with open(file, "r") as f1,open("%s.bak" % file, "w") as f2:
        for line in f1:
            for old_str in jsFileList:
                if old_str in line:
                    if old_str == 'main.dart.js':
                        if 'part.js' in line:
                            continue
                    new_str = jsFileHashMap[old_str]
                    line = line.replace(old_str, new_str)
                    lastLine = line
            f2.write(line)
        os.remove(file)
        os.rename("%s.bak" % file, file)

print("整理main.dart.js")
mainDartJs = 'main.dart.js'
checkFile(mainDartJs)

print("整理flutter_service_worker.js")
flutterServiceWorkerJs = 'flutter_service_worker.js'
checkFile(flutterServiceWorkerJs)

print("整理index.html")
indexHtml = 'index.html'
checkFile(indexHtml)

print("将part.js的引用手动添加到index.html中")
partJsFile = []
for jsFile in jsFileList:
    if (jsFile.endswith('part.js')):
        partJsFile.append(jsFileHashMap[jsFile])

with open(indexHtml, "r") as f1,open("%s.bak" % indexHtml, "w") as f2:
        for line in f1:
            if "js/plugin.js" in line :
                f2.write(line)
                for partFile in partJsFile:
                    f2.write("<script src=\"%s\" type=\"text/javascript\"></script>\n" % partFile)
                continue
            f2.write(line)
        os.remove(indexHtml)
        os.rename("%s.bak" % indexHtml, indexHtml)

print("更改文件名")
for jsFile in jsFileList :
    newFile = jsFileHashMap[jsFile]
    os.rename(jsFile,newFile)

print("返回上层文件夹,更改web文件为static-meeting-manager并压缩")
managerFile = "static-meeting-manager"
os.chdir('..')
os.rename('web',managerFile)

zipFile = "static-meeting-manager.zip"
if (os.path.exists(zipFile)):
    os.remove(zipFile)

zip = zipfile.ZipFile(zipFile,"w")
for path,dirnames,filenames in os.walk(managerFile):
    zip.write(path)
    for filename in filenames:
        zip.write(os.path.join(path,filename))
zip.close()

print("\n========= 操作完成 ===========\n")


首屏渲染性能差

flutter web 编译过后会将所有代码都生成在main.dart.js文件中,这就导致main.dart.js文件过大,从而导致加载慢

解决方法:

  1. tree shaking 优化
  2. 延迟加载

tree shaking 优化

初次加载时间可以通过最小化JS包体积来实现,tree shaking 和延迟加载都可以最大程度减少JS包体积

tree shaking 是只将一定会执行的代码打包进来,从而剔除无用代码的过程。

默认打release包时就开启了 tree shaking

延迟加载

延迟加载也叫懒加载,就是允许在需要是才去加载各种库。延迟加载是一个dart2JS特性,所以只能在Flutter web上用。

使用方式

1.首先将包或者文件引入为deferred

2.配合FutureBuilder来切换加载UI

import 'my_app.dart' deferred as myApp;
void main() async {
  runApp(AssetsApp(
      child: FutureBuilder(
          future: myApp.loadLibrary(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.done) {
              return myApp.MyApp();
            } else {
              return Center(
                child: Padding(
                  padding: EdgeInsets.all(20),
                  child: CircularProgressIndicator(),
                ),
              );
            }
          })));
}

优化参考:

segmentfault.com/a/119000003…

jelly.jd.com/article/5f7…