优雅的处理挂载window上的函数可能不存在的情况
背景
在做一个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个参数:
key:带判断的方法在window上的属性名value:不存在时的取值(function 表明直接使用此方法代替,string类型表明方法来源外部加载的js资源)options:是一些可选的配置项,主要用于处理使用过外部js资源加载方法的场景afterScriptLoad:资源加载完成后的回掉beforeAppendScript:资源加载前的回掉alreadyExistCB:方法如果已经存在执行的回掉async:控制script的async属性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上的函数可能不存在的情况的更多相关文章
- 油猴Tampermonkey 全局函数 它的注入函数都在 onload里面,直接写函数 都是内部函数,外部要是调用,就要挂靠到window上
油猴Tampermonkey 全局函数 它的注入函数都在 onload里面,直接写函数 都是内部函数,外部要是调用,就要挂靠到window上 window.like111 = function (){ ...
- window上利用pip安装pandas
官网推荐的是直接使用Anoconda,它集成了pandas,可以直接使用.安装挺简单的,有windows下的安装包.如果不想安装庞大的Anoconda,那就一步一步用pip来安装pandas.下面我主 ...
- vuejs件同一个挂载点上切换组
vuejs件同一个挂载点上切换组 动态组件 http://cn.vuejs.org/guide/components.html#动态组件 多个组件可以使用同一个挂载点,然后动态地在它们之间切换.使用保 ...
- 在window上安装pandas
之前在ubuntu上安装pandas,用的easy_install.这次在window上同样方法装遇到"unable to find vcvarsall.bat",看一些网上帖子好 ...
- linux和window下mkdir函数问题(转-锦曦月)
通过WIN32宏进行判断 window下mkdir函数 #include<direct.h> int _mkdir( const char *dirname ); linux下 ...
- iOS UIButton加在window上点击无效果问题
UIButton加在window上,点击没有效果,找了很久,原来是没有加上这名:[self.window makeKeyAndVisible]; self.window = [[UIWindow al ...
- linux和window下mkdir函数
通过WIN32宏进行判断 window下mkdir函数 #include<direct.h> int _mkdir( const char *dirname ); linux下 ...
- 【原】window上安装elasticserach
[window上安装elasticserach] 系统环境:2008R2 x64测试安装用的服务器IP:192.168.12.52elasticsearch版本:2.3.4JDK版本:jdk 1.8. ...
- 关于Windows高DPI的一些简单总结(Window上一般默认是96 dpi 作为100% 的缩放比率)
我们知道,关于高DPI的支持, Windows XP时代就开始有了, 那时关于高DPI的支持比较简单, 但是从Vista/Win7 到现在Win8 /Win8.1, Windows关于高DPI的支持已 ...
- 如果是在有master上开启了该参数,记得在slave端也要开启这个参数(salve需要stop后再重新start),否则在master上创建函数会导致replaction中断。
如果是在有master上开启了该参数,记得在slave端也要开启这个参数(salve需要stop后再重新start),否则在master上创建函数会导致replaction中断.
随机推荐
- flutter3+dart3聊天室|Flutter3跨平台仿微信App语音聊天/朋友圈
全新研发flutter3+dart3+photo_view跨多端仿微信App界面聊天Flutter3-Chat. flutter3-chat基于最新跨全平台技术flutter3+dart3+mater ...
- STM32F401的PWM输出
PWM的说明 PWM有三个关键指标: PWM频率, 占空比, 区分度 对于同一个时钟频率下工作的单片机, 区分度是和PWM工作频率相关的, 因为总频率是固定的, PWM工作频率越高, 留下给区分度的部 ...
- 基于keras的胶囊网络(CapsNet)
1 简介 胶囊网络(CapsNet)由 Hinton 于2017年10月在<Dynamic Routing Between Capsules>中提出,目的在于解决 CNN 只能提取特征,而 ...
- Vue中虚拟DOM的理解
Vue中虚拟DOM的理解 Virtual DOM是一棵以JavaScript对象作为基础的树,每一个节点称为VNode,用对象属性来描述节点,实际上它是一层对真实DOM的抽象,最终可以通过渲染操作使这 ...
- Windows SDK 之 mciSendString最后一个参数
这里在这里先附上mciSendString的函数原型: MCIERROR mciSendString( LPCTSTR lpszCommand, LPTSTR lpszReturnString, UI ...
- SpringBoot+MybatisPlus实现关联表查询
1.说明 最近写代码用到了mybatisPlus涉及到关联表查询.需求是这样的: 我有一个专业表major其中有个字段是所属院系dept_id,我需要通过这个dept_id关联院系表departmen ...
- python-鼠标宏
按下鼠标左键, 连击 按下鼠标右键, 停止 import win32api import time from pynput.mouse import Button, Controller mouse ...
- win32 - 写入安全日志(AuthzRegisterSecurityEventSource和AuthzReportSecurityEvent)
微软文档介绍说, 安全日志在其他两个重要方面与其他日志不同.首先,在默认配置中,它受到强大的访问控制列表(ACL)和特权检查的保护,这将可以读取其内容的个人的范围限制为本地系统,管理员和安全特权的持有 ...
- 并发慎用——System.currentTimeMillis()
好记忆不如烂笔头,能记下点东西,就记下点,有时间拿出来看看,也会发觉不一样的感受. System.currentTimeMillis()是极其常用的基础Java API,广泛地用来获取时间戳或测量代码 ...
- 硬件开发笔记(十六):RK3568底板电路mipi摄像头接口原理图分析、mipi摄像头详解
前言 本篇继续分析底板原理图mipi电路原理图.mipi摄像头输入硬件接口详解. RK3568芯片摄像头接口 查看RK3568的芯片手册,摄像头接口并不支持直接sensor模拟信号输入,只 ...