本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
优雅的处理Window.Fun可能不存在的情况
codesky · 316浏览 · 发布于2021-09-26 +关注

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

背景

在做一个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资源加载方法的场景

      • 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加载的方法方式将不太适用


                相关推荐

                将Fedora 29升级到Fedora 30

                吴振华 · 704浏览 · 2019-05-14 22:00:02
                使用Nginx反向代理到go-fastdfs

                iamitnan · 728浏览 · 2019-05-23 13:42:00
                利用VLC搭建组播流服务器

                追忆似水年华 · 2695浏览 · 2019-06-14 11:27:06
                用Bash脚本监控Linux上的内存使用情况

                吴振华 · 975浏览 · 2019-06-24 11:27:02
                加载中

                0评论

                评论
                分类专栏
                小鸟云服务器
                扫码进入手机网页