2023年6月21日发(作者:)

前端⽹站容灾-CDN主域重试⽅案对外⽹站前端静态资源⼀般都会部署在 CDN 上, CDN 可以减少资源请求时间,进⽽减少页⾯⾸屏时间。然⽽是否想过,有⼀天 CDN 也会被封禁⽽⽆法访问,不⽤怀疑,触不及防我们就会遇到。这个问题的严重程度导致⽹站统⼀⼀⽚⽩花花,真是空空如也啊~ , 对待⽹络问题,可以⽤ PWA 做离线加载,但是由于 PWA 的兼容性问题,并不能兼顾到所有设备,考虑是否有其它⽅式保证⽹站的正常访问呢?既然 CDN ⽆法访问,我们还有主域,当 CDN 域名请求失败时,尝试将资源向主域进⾏请求,则可保证⼤概率的资源请求成功,⽹站正常访问。⽅案⼀个⽹站的前端资源最主要的是 :1. HTML2. JS3. CSS4. IMG5. ...保证⽹站的整体访问, 可从这⼏种资源进⾏容灾,HTML 通常都是放在主域上, 做服务端渲染或者异步渲染,通过主域名访问获得 HTML内容,所以不对 HTML 进⾏考虑。⾄于 IMG, 由于现在⽤模板、jsx 形式,如 react 通过 img 组件的形式,对 img 的容灾考虑通过⽤组件的维度来进⾏,⽽将 CDN 域请求失败的资源重新向主域请求,想到的就是利⽤资源标签, <> 捕获 事件,然后统⼀利⽤⼀个捕获函数执⾏对应资源的主域请求。但这并不适合 JS,由于资源加载时间不定,⽽ JS 有执⾏顺序要求,前⾯的 JS 应当⽐后⾯的 JS 先执⾏,在使⽤ 捕获错误并将资源重新请求时,此时⽆法保证 JS 的执⾏顺序。既然要保证 JS 的执⾏顺序,需要做两件事:1. 判断资源是否加载失败,通过代码执⾏顺序来定2. 当代码执⾏判定资源请求失败,就在资源标签的位置后⽅插⼊对应的主域请求,达到保证代码按顺序执⾏以上,形成了对 JS 主域重试⽅案如下:重试代码如何插⼊ HTML 跟 资源中 ?当下前端的资源都离不开构建,我们的项⽬也是经过部门统⼀的⼯程化⼯具 IMFLOW 进⾏构建,整站静态资源也是经由构建⽣成,那便可借助构建动态插⼊重试代码,综合需要做三件事:1. ⽣成的 html 模板插⼊主域重试逻辑函数, ⽤于资源 的执⾏2. 在将构建⽣成的 JS 插⼊ html 模板时,同时在资源标签后⾯植⼊判定资源是否加载失败并请求主域的逻辑3. 构建⽣成的 JS 内容插⼊判定资源已加载的代码块IMFLOW 是基于 WEBPACK 来实现构建部分,模板的⽣成则是借助了 WEBPACK 的插件 html-webpack-plugin 来⾃动⽣成,借助对应的 HOOK 机制,在对应资源⽣成阶段,将主域重试逻辑插⼊,以实现上⾯三件事情,进⽽达到了⽬标。动态 JS 与模板内置?以上在⼯程化构建过程中实现基础的对静态 JS 、 CSS 进⾏ CDN 资源主域重试,然⽽还有什么问题呢?1. 前端⽹站为了考虑性能等,会对 JS 进⾏⼀个拆包,对部分 JS 逻辑做⼀个动态的懒加载,这部分动态的 JS 依赖于 JS 执⾏过程中动态插⼊,⽽不是直接在静态 HTML 中,如何对其进⾏容灾2. 业务中会有对部分 JS、CSS 进⾏⼀个 inline 化的需求,也有需要处理的单独插件 JS, 这些都是通过在模板 HTML 中进⾏⾃定义引⽤,然后依赖构建在扫描模板时,扫描资源,并对其进⾏相应的 parse、compile、inline 处理动态 JS 主域重试⾸先来说动态 JS 的产⽣背景,开发者们为了提升⽹站性能,可能会考虑将特定交互下才会执⾏的相关 JS 逻辑延迟动态加载,这种⽅式可以减少主逻辑 JS ⽂件的⼤⼩,进⽽请求更快,执⾏更快,这多见于单页⾯应⽤,或者某个页⾯引⼊了⼀个较复杂且较⼤的 JS 逻辑。在上篇提到的静态 JS 主域重试,主要通过两个步骤:1. 在将构建过程中,⽣成的 JS 链接插⼊模板时,将主域重试的 JS 逻辑⼀并插⼊,并保证顺序2. ⽣成的 JS ⽂件内容插⼊主域重试逻辑很明显,这种⽅案依赖于 HTML 模板, 但是动态 JS 并不是在构建的过程中插⼊ HTML 模板,反⽽它是通过主 JS 在执⾏的过程中动态插⼊的,这可怎么办?当我们使⽤ webpack 做构建时,做动态 JS 加载,就⽤到了 或者 ,webpack 在编译时,将 import 或者require 转为⾃⼰的实现⽅式,于是查看 webpack 将以上语法的转换⽅式:可以看到对应语法转换⽅式: => webpack_require.e再查看 webpack 的这个内部⽅法 webpack_require_.e⽽ webpack 在⽣成 JS 的时候,是如何将 webpack_require_.e 的定义植⼊⽬标 JS 中呢?在⽣成资源的时候,调⽤ createChunkAssets ⽣成⽂件内容,⽣成⽂件内容调⽤渲染模板,模板分为⼏类:1. 2. 3. 4. Template 包含 webpack runtime bootstrap, webpack_require_.e 正是 runtime bootstrap, 通过 mainTeplate 的 hooksrequireEnsure、jsonp 便可以植⼊主域重试代码⾄此,整体重试逻辑如下:可以查看 @tencent/webpack-cdn-assets-retry-plugin 插件包含了上述的功能。html 模板内置资源主域重试以上⽅式基本覆盖了在整个构建过程中⽣成的资源的主域重试处理,但业务⾥可能有些资源不是通过构建⽣成的,⽐如:引⼊了⼀个第三⽅的库,担⼼其修改不稳定,⼜不想单独部署,于是放在业务⼯程下⾯,在 html 模板中引⼊;⼜或者写了⼀些公共 JS 逻辑,在 html 模板中引⼊, 等等。对这种,在构建的过程中,在使⽤ loader 去 处理 html 模板时,通过解析模板语法,对模板引⼊的 JS 需要做两件事:1. 语法编译转换2. ⽣成⽣产环境访问链接,链接包含 hash可以理解为,在模板中直接⼿动引⼊的 JS 不是通过构建主流程⽣成的,它是额外的,在解析 html 的过程中⽣成,那这部分模板的 JS ⾃然就不能获得同静态 JS ⼀样的主域重试处理,即额外处理。上⾯已经解释过, 对模板⾥⾯的 JS , 通过实现⼀个 webpack loader 的⽅式去解析模板,进⽽编译模板的 JS 并产出到对应⽬录,借此即可在对应的 loader 中实现对资源的主域重试, 可是如何保持⼀致呢,总不能⼀个⽅案在两个地⽅都写⼀遍吧。哈肯定有同学想到了,公共逻辑抽取出来。在上述中,主域重试⽅案被沉淀在了 @tencent/webpack-cdn-assets-retry-plugin 插件中,对插件中的核⼼ util ⽅法进⾏暴露, 在html-loader 中引⼊对应⽅法, 在整体构建中,传⼊参数,是否 retry, 传⼊ loader 以及是否配置主域重试插件,便可实现整体的整站的主域重试以上,具体代码实现可查看:@tencent/webpack-cdn-assets-retry-plugin@tencent/imflow-html-loader数据对⽐查看 CDN 资源主域重试成功率想要学习web前端的同学,可以参考班提供的学习⼤纲;

2023年6月21日发(作者:)

前端⽹站容灾-CDN主域重试⽅案对外⽹站前端静态资源⼀般都会部署在 CDN 上, CDN 可以减少资源请求时间,进⽽减少页⾯⾸屏时间。然⽽是否想过,有⼀天 CDN 也会被封禁⽽⽆法访问,不⽤怀疑,触不及防我们就会遇到。这个问题的严重程度导致⽹站统⼀⼀⽚⽩花花,真是空空如也啊~ , 对待⽹络问题,可以⽤ PWA 做离线加载,但是由于 PWA 的兼容性问题,并不能兼顾到所有设备,考虑是否有其它⽅式保证⽹站的正常访问呢?既然 CDN ⽆法访问,我们还有主域,当 CDN 域名请求失败时,尝试将资源向主域进⾏请求,则可保证⼤概率的资源请求成功,⽹站正常访问。⽅案⼀个⽹站的前端资源最主要的是 :1. HTML2. JS3. CSS4. IMG5. ...保证⽹站的整体访问, 可从这⼏种资源进⾏容灾,HTML 通常都是放在主域上, 做服务端渲染或者异步渲染,通过主域名访问获得 HTML内容,所以不对 HTML 进⾏考虑。⾄于 IMG, 由于现在⽤模板、jsx 形式,如 react 通过 img 组件的形式,对 img 的容灾考虑通过⽤组件的维度来进⾏,⽽将 CDN 域请求失败的资源重新向主域请求,想到的就是利⽤资源标签, <> 捕获 事件,然后统⼀利⽤⼀个捕获函数执⾏对应资源的主域请求。但这并不适合 JS,由于资源加载时间不定,⽽ JS 有执⾏顺序要求,前⾯的 JS 应当⽐后⾯的 JS 先执⾏,在使⽤ 捕获错误并将资源重新请求时,此时⽆法保证 JS 的执⾏顺序。既然要保证 JS 的执⾏顺序,需要做两件事:1. 判断资源是否加载失败,通过代码执⾏顺序来定2. 当代码执⾏判定资源请求失败,就在资源标签的位置后⽅插⼊对应的主域请求,达到保证代码按顺序执⾏以上,形成了对 JS 主域重试⽅案如下:重试代码如何插⼊ HTML 跟 资源中 ?当下前端的资源都离不开构建,我们的项⽬也是经过部门统⼀的⼯程化⼯具 IMFLOW 进⾏构建,整站静态资源也是经由构建⽣成,那便可借助构建动态插⼊重试代码,综合需要做三件事:1. ⽣成的 html 模板插⼊主域重试逻辑函数, ⽤于资源 的执⾏2. 在将构建⽣成的 JS 插⼊ html 模板时,同时在资源标签后⾯植⼊判定资源是否加载失败并请求主域的逻辑3. 构建⽣成的 JS 内容插⼊判定资源已加载的代码块IMFLOW 是基于 WEBPACK 来实现构建部分,模板的⽣成则是借助了 WEBPACK 的插件 html-webpack-plugin 来⾃动⽣成,借助对应的 HOOK 机制,在对应资源⽣成阶段,将主域重试逻辑插⼊,以实现上⾯三件事情,进⽽达到了⽬标。动态 JS 与模板内置?以上在⼯程化构建过程中实现基础的对静态 JS 、 CSS 进⾏ CDN 资源主域重试,然⽽还有什么问题呢?1. 前端⽹站为了考虑性能等,会对 JS 进⾏⼀个拆包,对部分 JS 逻辑做⼀个动态的懒加载,这部分动态的 JS 依赖于 JS 执⾏过程中动态插⼊,⽽不是直接在静态 HTML 中,如何对其进⾏容灾2. 业务中会有对部分 JS、CSS 进⾏⼀个 inline 化的需求,也有需要处理的单独插件 JS, 这些都是通过在模板 HTML 中进⾏⾃定义引⽤,然后依赖构建在扫描模板时,扫描资源,并对其进⾏相应的 parse、compile、inline 处理动态 JS 主域重试⾸先来说动态 JS 的产⽣背景,开发者们为了提升⽹站性能,可能会考虑将特定交互下才会执⾏的相关 JS 逻辑延迟动态加载,这种⽅式可以减少主逻辑 JS ⽂件的⼤⼩,进⽽请求更快,执⾏更快,这多见于单页⾯应⽤,或者某个页⾯引⼊了⼀个较复杂且较⼤的 JS 逻辑。在上篇提到的静态 JS 主域重试,主要通过两个步骤:1. 在将构建过程中,⽣成的 JS 链接插⼊模板时,将主域重试的 JS 逻辑⼀并插⼊,并保证顺序2. ⽣成的 JS ⽂件内容插⼊主域重试逻辑很明显,这种⽅案依赖于 HTML 模板, 但是动态 JS 并不是在构建的过程中插⼊ HTML 模板,反⽽它是通过主 JS 在执⾏的过程中动态插⼊的,这可怎么办?当我们使⽤ webpack 做构建时,做动态 JS 加载,就⽤到了 或者 ,webpack 在编译时,将 import 或者require 转为⾃⼰的实现⽅式,于是查看 webpack 将以上语法的转换⽅式:可以看到对应语法转换⽅式: => webpack_require.e再查看 webpack 的这个内部⽅法 webpack_require_.e⽽ webpack 在⽣成 JS 的时候,是如何将 webpack_require_.e 的定义植⼊⽬标 JS 中呢?在⽣成资源的时候,调⽤ createChunkAssets ⽣成⽂件内容,⽣成⽂件内容调⽤渲染模板,模板分为⼏类:1. 2. 3. 4. Template 包含 webpack runtime bootstrap, webpack_require_.e 正是 runtime bootstrap, 通过 mainTeplate 的 hooksrequireEnsure、jsonp 便可以植⼊主域重试代码⾄此,整体重试逻辑如下:可以查看 @tencent/webpack-cdn-assets-retry-plugin 插件包含了上述的功能。html 模板内置资源主域重试以上⽅式基本覆盖了在整个构建过程中⽣成的资源的主域重试处理,但业务⾥可能有些资源不是通过构建⽣成的,⽐如:引⼊了⼀个第三⽅的库,担⼼其修改不稳定,⼜不想单独部署,于是放在业务⼯程下⾯,在 html 模板中引⼊;⼜或者写了⼀些公共 JS 逻辑,在 html 模板中引⼊, 等等。对这种,在构建的过程中,在使⽤ loader 去 处理 html 模板时,通过解析模板语法,对模板引⼊的 JS 需要做两件事:1. 语法编译转换2. ⽣成⽣产环境访问链接,链接包含 hash可以理解为,在模板中直接⼿动引⼊的 JS 不是通过构建主流程⽣成的,它是额外的,在解析 html 的过程中⽣成,那这部分模板的 JS ⾃然就不能获得同静态 JS ⼀样的主域重试处理,即额外处理。上⾯已经解释过, 对模板⾥⾯的 JS , 通过实现⼀个 webpack loader 的⽅式去解析模板,进⽽编译模板的 JS 并产出到对应⽬录,借此即可在对应的 loader 中实现对资源的主域重试, 可是如何保持⼀致呢,总不能⼀个⽅案在两个地⽅都写⼀遍吧。哈肯定有同学想到了,公共逻辑抽取出来。在上述中,主域重试⽅案被沉淀在了 @tencent/webpack-cdn-assets-retry-plugin 插件中,对插件中的核⼼ util ⽅法进⾏暴露, 在html-loader 中引⼊对应⽅法, 在整体构建中,传⼊参数,是否 retry, 传⼊ loader 以及是否配置主域重试插件,便可实现整体的整站的主域重试以上,具体代码实现可查看:@tencent/webpack-cdn-assets-retry-plugin@tencent/imflow-html-loader数据对⽐查看 CDN 资源主域重试成功率想要学习web前端的同学,可以参考班提供的学习⼤纲;