前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter Web - 一种取巧的 CDN 方案

Flutter Web - 一种取巧的 CDN 方案

作者头像
Swift社区
发布2022-12-12 17:31:44
1.2K1
发布2022-12-12 17:31:44
举报
文章被收录于专栏:Swift社区Swift社区

背景

用上文的方式,落地稿定 WAP 版的过程中,遇到了一个严重的卡点:

如何将 Flutter build web 的资源 CDN 化,也是笔者以前接触比较少的(笔者以前 Web 开发经验更多是管理后台以及离线包,很少需要接触到部署)。

为什么是卡点?在于 Flutter 默认仅支持相对域名的资源加载方式,无法使用当前域名以外的 CDN 域名,导致无法享受 CDN 带来的优势。

原以为 Flutter 官方有现成的方案,翻了一大圈,只能证明自己想的太美 ...

方案探寻

不过,在美团技术团队发表的 FlutterWeb 性能优化探索与实践[1] 中找到了部分解决方式:

  • 对于图片相关的资源在 index.html 上增加 meta,可以解决 assets 资源路径是相对路径的问题问题。
代码语言:javascript
复制
<meta name="assetBase" content="你的 CDN 路径" />
  • 上述 meta 对于加载的 JS 文件不适用(Flutter 官方快支持)。main.dart.js 特别是用了延迟加载 deferred-components 会生成多个 main.dart.js_XX.part.js 多个 JS 的情况下,怎么配置 CDN 域名就成了一个大难题。

美团技术团队也输出了一种方案:

通过对 js_helper.dart 的动态编译,读取 src 属性修改为读取 assetBase 来实现 xxx.part.js 文件的 CDN 加载

笔者看了下 js_helper.dart 代码

image.png

Emmm ... 3000 多行代码,而且还要准备 hook dart 的工具,或者自行编译 Flutter Engine,并不是一个短期能实现的一种方式。

那解决思路是 hook 来改变 <script src='xxx'>,那是不是直接从 JS 代码 hook 就行了,毕竟 JS 这运行时可有无限可(bug)能(bug),改造也更简单。

失败的第一版

通过观察 flutter.js 文件以及研究 main.dart.js,发现其实也都是动态添加 element 的方式添加 script 的,那我们直接 hook createElement 方法是不是就可以拿到 script 的创建对象,然后再去增加 CDN 的域名即可。

说做就做:

代码语言:javascript
复制
var _createElement = document.createElement.bind(document)
document.createElement = function (tagName: string) {
  var element = _createElement(tagName)

  if (tagName.toLowerCase() == 'script') {
    Object.defineProperty(element, 'src', {
      set: function (val) {
        this.sourceSrc = val
        return val
      },
      get: function () {
        return this.sourceSrc
      },
    })
  }
  return element
}

通过重写 script src 的 get set 方法,看起来是可行的,set 方法也是会成功拦截。但 get 方法根本不会执行,这可能是 script src 是特有属性,有固定的底层逻辑,不能被重写(没找到相关说明,有了解的同学评论一下)。

所以这并不可行,需要想其他的 hook 方式。

成功的第二版

直接 hook src 不可取的话,我们就要从它的代码上下游做手脚才行。

flutter.js

image.png

main.dart.js

image.png

通过 build 后的代码观察到,是通过 main.dart.js 的加载是通过 body.append, main.dart.js_XX.part.js 的动态加载是通过 body.appendChild

那我们直接 hook 这两个方法是不是可以呢?

代码也很简单:

代码语言:javascript
复制
/**
 * hook appendChild
 */
let appendChild = document.body.appendChild
document.body.appendChild = function (el) {
  return appendChild.call(document.body, convertCDNScript(el))
}

/**
 * hook append
 */
let append = document.body.append
document.body.append = function (el) {
  append.call(document.body, convertCDNScript(el))
}

/**
 * 转换成带有 CDN 域名的 Script
 */
function convertCDNScript(el) {
  const cdnURL = import.meta.env.VITE_GAODING_CDN
  if (cdnURL !== '/' && el.nodeName.toLowerCase() === 'script' && el.baseURI !== cdnURL) {
    el.src = el.src.replace(el.baseURI, cdnURL)
  }
  return el
}

大功完成, Enjoy ~

后续

用 hook 的方式毕竟不是什么长(cou)久(he)之(de)计(yong),还是期待 Flutter 官方提供一个 API 或者环境变量设置。

本篇就只是抛砖引玉,期待能有同学放出来更优雅的实现方式。

参考资料

[1]FlutterWeb 性能优化探索与实践: https://juejin.cn/post/7043700931480780831 - EOF -

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-11-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Swift社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 方案探寻
    • 失败的第一版
      • 成功的第二版
      • 后续
        • 参考资料
        相关产品与服务
        内容分发网络 CDN
        内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档