评论

小程序字体重复加载的优化方案

利用本地文件缓存字体,避免页面跳转重新下载字体。

最近有一个需求,需求方一定要求加载自有的字体,一共5个包,加起来要60M,而且因为有后台上传的文章,还不能用字体精简工具压缩字体。

在调用官方的wx.loadfontface的时候,开始看着一切正常,但真机调试的时候,发现每个页面都在重新下载字体。

先不说每个页面抖动的问题,就是流量也扛不住他这么造啊。

社区里面看到有很多人在反馈这个bug,从20年到23年都有

https://developers.weixin.qq.com/community/develop/doc/0006e6ad5b49b02e3bc98398d51000?highLine=loadfontface%2520%25E7%25BC%2593%25E5%25AD%2598

https://developers.weixin.qq.com/community/develop/doc/00084c852f40280d6b2f6461550000?highLine=loadfontface%2520%25E9%2587%258D%25E5%25A4%258D

本来给客户反馈说微信bug,他们也勉强接受,但是上线10天,跑了1000多rmb的流量钱,实在是顶不住,压力又给到开发这边。。。

后面在掘金看到一篇文章(https://juejin.cn/post/7103928278225780743?searchId=2023081018280858E8AD21B4FDD711555A

本地路径不行,那把本地文件转成base64曲线救国确实是一条思路。

原文是behavior做的,而且是用云开发,但我们的业务场景是需要全局生效,每个页面都写一个behavior也太累了。

于是改造成以下模式。

先创建一个loadFont.js,代码如下

注释写的比较详细了,简述思路:

先用wx.getFileSystemManager().access()看本地有没有,有就直接load,没有就先下载

wx.downloadFile把字体下到本地,再从本地wx.getFileSystemManager().readFile()取出文件并转Base64

最后source: `url("data:font/woff2;charset=utf-8;base64,${res.data}")`,搞定!

// 建议循环调用方法,而不是这个方法内循环下载
// 下载字体文件,注意要把字体域名加到后台downloadFile白名单中
function _downloadFont(fontUrl, filePath, fontFamily{
  wx.downloadFile({
    url: fontUrl,
    successres => {
      wx.getFileSystemManager().saveFile({ // 下载成功后保存到本地
        tempFilePath: res.tempFilePath,
        filePath,
        successres => {
          // 加载字体
          _loadFontFace(fontFamily, res.savedFilePath)
        }
      })
    }
  })
}
// 加载文件字体转 base64,load
function _loadFontFace(fontFamily, filePath{
  // 读文件
  wx.getFileSystemManager().readFile({
    filePath, // 本地文件地址
    encoding'base64',
    successres => {
      wx.loadFontFace({
        globaltrue// 是否全局生效
        scopes: ['webview''native'], //native可能有点问题,超哥生个海报试一下
        family: fontFamily, // 字体名称
        source`url("data:font/woff2;charset=utf-8;base64,${res.data}")`,
        success(res) {
          console.log(fontFamily + '加载成功:' + res.status)
        },
        failfunction (res{
          console.log(fontFamily + '加载失败' + res)
        },
      })
    }
  })


}
// fontUrl: 字体地址
// filename: 存储文件路径
// fontFamily: css 中字体的 family
function loadCloudFontFace(fontUrl, filename, fontFamily{
  const filePath = `${wx.env.USER_DATA_PATH}/${filename}`
  wx.getFileSystemManager().access({
    path: filePath,
    success() => {
      _loadFontFace(fontFamily, filePath)
      console.log('从本地加载了字体');
    },
    fail() => {
      _downloadFont(fontUrl, filePath, fontFamily)
      console.log('从外部加载了字体', fontUrl);
    }
  })
}

module.exports = {
  loadCloudFontFace
}


由于业务本身的需求,是全局所有字体都用,所以,直接在app.js中(实际上我们是抽了一个文件单独维护的,这里就简化吧)

const loadFont = require('./loadFont.js');

loadFont.loadCloudFontFace('https://xxx.com/wxapp/fonts/STHeiti.woff2''STHeiti.woff2''STHeiti')


并且,在app.wxss,让font-family全局给一个默认字体(事实上还有英文字体,这里简化了)

page {
  font-family'STHeiti';
}


当然也支持每个页面独立配置不同字体,方法大同小异。

真机测试,终于只加载一次网络字体了。

其他注意的事项:

  1. 由于第一次仍然要加载字体,会产生抖动,我们的做法是,尽量不在首页用文字,大量用图片替代,同时在后台加载字体,供其他页面使用。
  2. 对于个别页面需要加载字体时,可以出个loading框(虽然体验依然不好,但我尽力了)
  3. 直接load网络字体,会存在跨域的问题,有的时候,这个问题确实很难解决(字体一般都用cdn、对象存储一类的,如果有权限操作配置很简单,但很多情况下开发人员确实不好增加跨域配置)。采用这个方案,由于是本地,就不存在跨域问题了,一举多得。


最后还是要吐槽下,这个需求,并不小众。最早从20年就有bug反馈,官方也有回复,但到23年了这个bug还存在,也着实无奈。

最后一次编辑于  2023-08-10  
点赞 6
收藏
评论

5 个评论

  • 午后红茶
    午后红茶
    07-24

    试验了之后发现,在目前的基础库(3.5.0),wx.loadFontFace 的 scopes 如果含有 native,这种加载方式就会报错,而且模拟器和真机都无法生效。而新版的 Canvas 2D 是需要 scopes 含有 native 才能显示加载的字体。

    但是旧版使用 wx.createCanvasContext 创建的 Canvas 可以支持显示 scopes 是 webview 加载的字体。

    所有如果要用这种方式载入字体又要能在 canvas 中生效,只能用旧版的 wx.createCanvasContext。

    07-24
    赞同
    回复 1
    • Fn.prototype
      Fn.prototype
      10-11
      老铁怎么解决的。。
      10-11
      回复
  • Lxn
    Lxn
    07-18

    使用该方案已解决目前遇到的问题

    07-18
    赞同
    回复
  • 可靠的罗响亮
    可靠的罗响亮
    01-30

    用了楼上的办法 下载了字体 ttf woff 都有,加载的时候都报错 加载失败 ???

    01-30
    赞同
    回复
  • 文举
    文举
    2023-11-17

    现在我看loadFontFace是已经有缓存的了,不用这么麻烦

    2023-11-17
    赞同
    回复 2
    • LP
      LP
      2023-12-13
      实际真机并没有、无论你如何配置
      2023-12-13
      回复
    • 超哥[天啊][天啊][天啊]
      超哥[天啊][天啊][天啊]
      05-27
      缓存是有了,但又重新下载一次
      05-27
      回复
  • 缪木皆
    缪木皆
    2023-11-15

    感谢,用了您的方法只加载一次了,但每次进页面读取文件还是会有轻微的字体抖动

    2023-11-15
    赞同
    回复
登录 后发表内容