优雅的处理Window.Fun可能不存在的情况

系统 Windows
在做一个Web JS SDK(A)时,内部会用到另一个Web JS SDK(B)的方法。(文中后续用A/B代替两者)

[[425592]]

本文转载自微信公众号「粥里有勺糖」,作者粥里有勺糖。转载本文请联系粥里有勺糖公众号。

背景

在做一个Web JS SDK(A)时,内部会用到另一个Web JS SDK(B)的方法。(文中后续用A/B代替两者)

B通常会提供Script和NPM包两种使用方式

使用npm pkg的缺点

  • 增加包体积
  • 如果这个SDK被Web应用已经引入过页面,那么理论上可直接使用,不必要再整一个

如果SDK B包含script引入的方式,目标页面也存在可能会引入B的情况,那么优先考虑使用Script引入依赖的SDK的情况:例如

  • 目标页面已经引入过JQuery(符合SDK A的使用需求),那么SDK A就可以直接使用已经存在的$进行操作即可,不必再创建jQuery的script
  • 通常页面都会接入埋点监控等基建服务SDK B,SDK A也需要通过B进行数据的上报

衍生需求

  • 挂载在window上的函数不存在时,自动通过script或者polyfill(垫片方法)补全这个方法
  • 调用方依旧按照SDK B的文档进行使用
  1. window.sdkB(options) 

解决方案

编写一个通用的工具函数,处理上述的衍生需求

方法定义如下

  1. function patchWindowFun( 
  2.   key: string, 
  3.   value: string | Function
  4.   options?: { 
  5.     afterScriptLoad?: Function 
  6.     beforeAppendScript?: Function 
  7.     alreadyExistCB?: Function 
  8.     async?: boolean 
  9.     defer?: boolean 
  10.   }, 

总共支持传入3个参数:

  1. key:带判断的方法在window上的属性名
  2. value:不存在时的取值(function 表明直接使用此方法代替,string类型表明方法来源外部加载的js资源)
  3. options:是一些可选的配置项,主要用于处理使用过外部js资源加载方法的场景
  • afterScriptLoad:资源加载完成后的回掉
  • beforeAppendScript:资源加载前的回掉
  • alreadyExistCB:方法如果已经存在执行的回掉
  • async:控制script的async属性
  • defer:控制script的defer属性

由于大多数web sdk都会存在需要调用特定函数或者方法进行初始化的情况,固提供了afterScriptLoad,beforeAppendScript,alreadyExistCB三个钩子函数处理不同时机初始化的情况

方法实现

如果目标属性存在则直接执行相应的回调,不做进一步处理

  1. if (window[key]) { 
  2.   alreadyExistCB && alreadyExistCB() 
  3.   console.log(key'already exist'
  4.   return 

目标属性不存在,传入的方法存在时直接进行赋值

  1. // 函数直接赋值 
  2. if (typeof value === 'function') { 
  3.   window[key] = value 
  4.   return 

剩余逻辑则是处理方法从外部js资源加载的情况

由于加载script大部分情况是异步的,业务代码中可能已经调用了相关方法,为此临时创建一个方法收集传入的参数

  1. let params = [] 
  2. window[key] = function () { 
  3.   params.push(arguments) 

下面的逻辑就是处理script加载的逻辑

在js资源加载完成后通过apply配合forEach将提前调用方法产生的参数重新正确的执行一次

  1. const script = document.createElement('script'
  2. script.src = value 
  3. script.async = !!defer 
  4. script.defer = !!async 
  5. script.onload = function () { 
  6.   afterScriptLoad && afterScriptLoad() 
  7.   // 处理原来没处理的 
  8.   params.forEach(param => { 
  9.     window[key].apply(this, param) 
  10.   }) 
  11. beforeAppendScript && beforeAppendScript() 
  12. document.body.append(script) 

完整源码如下

  1. function patchWindowFun( 
  2.   key: string, 
  3.   value: string | Function
  4.   options?: { 
  5.     afterScriptLoad?: Function 
  6.     beforeAppendScript?: Function 
  7.     alreadyExistCB?: Function 
  8.     async?: boolean 
  9.     defer?: boolean 
  10.   }, 
  11. ) { 
  12.   // 存在不处理 
  13.   const { alreadyExistCB, afterScriptLoad, beforeAppendScript, defer, async } = options || {} 
  14.  
  15.   if (window[key]) { 
  16.     alreadyExistCB && alreadyExistCB() 
  17.     console.log(key'already exist'
  18.     return 
  19.   } 
  20.  
  21.   // 函数直接赋值 
  22.   if (typeof value === 'function') { 
  23.     window[key] = value 
  24.     return 
  25.   } 
  26.  
  27.   // script url 
  28.   if (typeof value === 'string') { 
  29.     let params = [] 
  30.     window[key] = function () { 
  31.       params.push(arguments) 
  32.     } 
  33.  
  34.     const script = document.createElement('script'
  35.     script.src = value 
  36.     script.async = !!defer 
  37.     script.defer = !!async 
  38.     script.onload = function () { 
  39.       afterScriptLoad && afterScriptLoad() 
  40.       // 处理原来没处理的 
  41.       params.forEach(param => { 
  42.         window[key].apply(this, param) 
  43.       }) 
  44.     } 
  45.     beforeAppendScript && beforeAppendScript() 
  46.     document.body.append(script) 
  47.   } 

小结

目前的方法实现仅适用于,调用的方法相对独立不影响正常的交互

 

如果业务代码依赖方法的返回值,那么异步通过script加载的方法方式将不太适用

 

责任编辑:武晓燕 来源: 粥里有勺糖
相关推荐

2019-12-31 09:11:01

后台Android系统

2009-09-12 09:34:18

Windows 7中国售价

2018-07-19 06:14:09

2023-09-12 08:02:13

viewport断点

2021-01-25 07:21:24

GitHub 开源代码下载

2018-07-03 14:20:10

数据库恢复备份

2018-06-25 15:08:34

打印机处理打印

2020-11-03 10:23:22

云计算容器技术

2017-12-07 14:57:13

404互联网错误代码

2017-12-26 08:25:57

硬盘数据丢失

2012-05-16 11:35:16

SQL Server拒绝访问

2021-03-08 15:28:18

滴滴网约车大数据

2015-10-20 10:30:59

创业时机

2011-07-13 10:01:16

域控制器

2023-10-13 09:46:48

2018-07-03 10:09:18

闪存

2010-01-05 13:52:02

2018-01-18 09:43:46

调研 计算机 薪资

2018-01-30 15:50:52

Android常亮方法

2018-08-26 06:24:58

MySQLInnoDB行锁
点赞
收藏

51CTO技术栈公众号