背景

在做一个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的文档进行使用
window.sdkB(options)

解决方案

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

方法定义如下

function patchWindowFun(
key: string,
value: string | Function,
options?: {
afterScriptLoad?: Function
beforeAppendScript?: Function
alreadyExistCB?: Function
async?: boolean
defer?: boolean
},
)

总共支持传入3个参数:

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

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

方法实现

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

  if (window[key]) {
alreadyExistCB && alreadyExistCB()
console.log(key, 'already exist')
return
}

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

  // 函数直接赋值
if (typeof value === 'function') {
window[key] = value
return
}

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

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

let params = []
window[key] = function () {
params.push(arguments)
}

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

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

const script = document.createElement('script')
script.src = value
script.async = !!defer
script.defer = !!async
script.onload = function () {
afterScriptLoad && afterScriptLoad()
// 处理原来没处理的
params.forEach(param => {
window[key].apply(this, param)
})
}
beforeAppendScript && beforeAppendScript()
document.body.append(script)

完整源码如下

function patchWindowFun(
key: string,
value: string | Function,
options?: {
afterScriptLoad?: Function
beforeAppendScript?: Function
alreadyExistCB?: Function
async?: boolean
defer?: boolean
},
) {
// 存在不处理
const { alreadyExistCB, afterScriptLoad, beforeAppendScript, defer, async } = options || {} if (window[key]) {
alreadyExistCB && alreadyExistCB()
console.log(key, 'already exist')
return
} // 函数直接赋值
if (typeof value === 'function') {
window[key] = value
return
} // script url
if (typeof value === 'string') {
let params = []
window[key] = function () {
params.push(arguments)
} const script = document.createElement('script')
script.src = value
script.async = !!defer
script.defer = !!async
script.onload = function () {
afterScriptLoad && afterScriptLoad()
// 处理原来没处理的
params.forEach(param => {
window[key].apply(this, param)
})
}
beforeAppendScript && beforeAppendScript()
document.body.append(script)
}
}

小结

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

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

优雅的处理挂载window上的函数可能不存在的情况的更多相关文章

  1. 油猴Tampermonkey 全局函数 它的注入函数都在 onload里面,直接写函数 都是内部函数,外部要是调用,就要挂靠到window上

    油猴Tampermonkey 全局函数 它的注入函数都在 onload里面,直接写函数 都是内部函数,外部要是调用,就要挂靠到window上 window.like111 = function (){ ...

  2. window上利用pip安装pandas

    官网推荐的是直接使用Anoconda,它集成了pandas,可以直接使用.安装挺简单的,有windows下的安装包.如果不想安装庞大的Anoconda,那就一步一步用pip来安装pandas.下面我主 ...

  3. vuejs件同一个挂载点上切换组

    vuejs件同一个挂载点上切换组 动态组件 http://cn.vuejs.org/guide/components.html#动态组件 多个组件可以使用同一个挂载点,然后动态地在它们之间切换.使用保 ...

  4. 在window上安装pandas

    之前在ubuntu上安装pandas,用的easy_install.这次在window上同样方法装遇到"unable to find vcvarsall.bat",看一些网上帖子好 ...

  5. linux和window下mkdir函数问题(转-锦曦月)

    通过WIN32宏进行判断   window下mkdir函数   #include<direct.h> int _mkdir( const char *dirname );   linux下 ...

  6. iOS UIButton加在window上点击无效果问题

    UIButton加在window上,点击没有效果,找了很久,原来是没有加上这名:[self.window makeKeyAndVisible]; self.window = [[UIWindow al ...

  7. linux和window下mkdir函数

    通过WIN32宏进行判断   window下mkdir函数   #include<direct.h> int _mkdir( const char *dirname );   linux下 ...

  8. 【原】window上安装elasticserach

    [window上安装elasticserach] 系统环境:2008R2 x64测试安装用的服务器IP:192.168.12.52elasticsearch版本:2.3.4JDK版本:jdk 1.8. ...

  9. 关于Windows高DPI的一些简单总结(Window上一般默认是96 dpi 作为100% 的缩放比率)

    我们知道,关于高DPI的支持, Windows XP时代就开始有了, 那时关于高DPI的支持比较简单, 但是从Vista/Win7 到现在Win8 /Win8.1, Windows关于高DPI的支持已 ...

  10. 如果是在有master上开启了该参数,记得在slave端也要开启这个参数(salve需要stop后再重新start),否则在master上创建函数会导致replaction中断。

    如果是在有master上开启了该参数,记得在slave端也要开启这个参数(salve需要stop后再重新start),否则在master上创建函数会导致replaction中断.

随机推荐

  1. 零基础入门学习Java课堂笔记 ——day04

    Java数组 1.数组概述 定义:数组是相同类型的有序集合,同一组同一类型的数据可以按照先后次序排列组合在一起,其中一个数据可以称为元素,每个数组元素可以通过一个下表访问它们 2.数组声明创建 dat ...

  2. Java Calendar 多用,日期 加减

    服务需要订购一个月,订购一个月 不等于增加 30天:若是1,3,5的话应该 31天,要善用 Calendar public static void main(String[] args) throws ...

  3. Docker生命周期,一张图秒懂docker

  4. IIS的详细配置

    一:配置默认文档 输入ip打开哪个页面是由默认文档设定的 1.打开IIS配置页面,点击网站.我们的默认站点已经启动,可以看到绑定的ip和网页的路径 2.选中Default Web Site,可以看到有 ...

  5. Linux zip常用命令

    1.将当前目录下的所有文件和文件夹全部压缩成myfile.zip内联代码块文件zip -r myfile.zip ./*-r表示递归压缩子目录下所有文件. 2.unzip把myfile.zip文件解压 ...

  6. Guava EventBus的具体使用以及源码解析

    使用Guava EventBus对系统进行异步解耦改造 一.背景 最近在写的项目里,在使用定时器进行自动任务下派时,发现之前写的程序中将包括启动流程.地图更新.发送短信.效能计算等操作全部集成在同一个 ...

  7. JS Leetcode 33. 搜索旋转排序数组题解,图解旋转数组中的二分法

    壹 ❀ 引 本来今天(2021.4.7)的每日一题是81. 搜索旋转排序数组 II,但今天工作很忙,下班人基本累个半死,题目别说按照二分法的思路做不出来,连题解看了会都没法沉下心去看,不过得到的信息是 ...

  8. Uniapp+Nodejs实现外卖App项目1-项目介绍

    项目介绍 本项目采用uniapp和nodejs(数据接口).mongodb等技术实现了一个类似美团外卖的简易APP.项目主要目的是为了快速上手,如何快速使用uniapp开发一个app项目,同时掌握一些 ...

  9. springboot和jquery.form.js实现监听文件上传进度

    说明 文件上传作为程序开发最常用的功能之一,上传进度展示也是必须的.但是有时候进度并不准,进度100%了实际上后台尚未接收完毕,本篇就介绍如何利用jquery的form插件来实时反馈文件上传进度. 实 ...

  10. 面试官:什么是Java内存模型?

    当问到 Java 内存模型的时候,一定要注意,Java 内存模型(Java Memory Model,JMM)它和 JVM 内存布局(JVM 运行时数据区域)是不一样的,它们是两个完全不同的概念. 1 ...