被低估的前端模块化


title: 被低估的前端模块化 tags:

  • 前端模块化 categories: 前端 date: 2017-03-25 18:18:55

对于前端的缓存主要包括静态资源的缓存

为避免出现类似问题参考 变态的静态资源缓存与更新

在经历了上传前端资源文件等之后测试发现了一堆问题

发现每一个出现问题的均是弹出对话框画面,由于均是出现函数未定义,初步判断为js加载顺序问题

因此对比了生产实现和开发实现,发现现象如下

同样的文件在生产环境下表现如此

分别出现在chrome的网络面板XHR标签和JS标签下

对于两个版本的差异出现在js加载的域不一样(生产的域为当前服务器而开发环境为CDN)

那么为啥出现不同的域出现不同的加载行为呢?

我们知道对于浏览器存在同源策略www.ruanyifeng.com/blog/2016/0…

那么得出结论ajax请求是无法直接跨域的,因此大部分开发者使用了jsonp的方式

对于jsonp实质上是通过script的方式来进行跨域的,顺便说到我们cdn的请求。

cdn的加载在正常画面上是OK的,因为由浏览器来串行执行对应的内容,此时没有问题。

但是当使用对话框插件之后实质上使用remote是通过ajax请求到对应的html,其次加载对应的js

无说明均为jQuery1.9.1

    if ( hasScripts ) {
       doc = scripts[ scripts.length - 1 ].ownerDocument;
     
       // Reenable scripts
       jQuery.map( scripts, restoreScript );
     
       // Evaluate executable scripts on first document insertion
       for ( i = 0; i < hasScripts; i++ ) {
          node = scripts[ i ];
          if ( rscriptType.test( node.type || "" ) &&
             !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
     
             if ( node.src ) {
                // Hope ajax is available...
                jQuery.ajax({
                   url: node.src,
                   type: "GET",
                   dataType: "script",
                   async: false,
                   global: false,
                   "throws": true
                });
             } else {
                jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
             }
          }
       }
复制代码

此时话分两头,当执行ajax时首先判断

    // A cross-domain request is in order when we have a protocol:host:port mismatch
    if ( s.crossDomain == null ) {
       parts = rurl.exec( s.url.toLowerCase() );
       s.crossDomain = !!( parts &&
          ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
             ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
                ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
       );
    }

    // Handle cache's special case and global
    jQuery.ajaxPrefilter( "script", function( s ) {
       if ( s.cache === undefined ) {
          s.cache = false;
       }
       if ( s.crossDomain ) {
          s.type = "GET";
          s.global = false;
       }
    });

    if ( !s.hasContent ) {
     
       // If data is available, append data to url
       if ( s.data ) {
          cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
          // #9682: remove data so that it's not used in an eventual retry
          delete s.data;
       }
     
       // Add anti-cache in url if needed
       if ( s.cache === false ) {
          s.url = rts.test( cacheURL ) ?
     
             // If there is already a '_' parameter, set its value
             cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
     
             // Otherwise add one to the end
             cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
       }
    }
复制代码

由上述代码可以看出ajax默认请求script都会放弃使用缓存,将会出现在网络面板看见形如_=ajax_nonce这样的的后缀使得无法使用缓存

关于jQuery加载transport为

    // Bind script tag hack transport
    jQuery.ajaxTransport( "script", function(s) {
     
       // This transport only deals with cross domain requests
       if ( s.crossDomain ) {
     
          var script,
             head = document.head || jQuery("head")[0] || document.documentElement;
     
          return {
     
             send: function( _, callback ) {
     
                script = document.createElement("script");
     
                script.async = true;
     
                if ( s.scriptCharset ) {
                   script.charset = s.scriptCharset;
                }
     
                script.src = s.url;
     
                // Attach handlers for all browsers
                script.onload = script.onreadystatechange = function( _, isAbort ) {
     
                   if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
     
                      // Handle memory leak in IE
                      script.onload = script.onreadystatechange = null;
     
                      // Remove the script
                      if ( script.parentNode ) {
                         script.parentNode.removeChild( script );
                      }
     
                      // Dereference the script
                      script = null;
     
                      // Callback if not abort
                      if ( !isAbort ) {
                         callback( 200, "success" );
                      }
                   }
                };
     
                // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
                // Use native DOM manipulation to avoid our domManip AJAX trickery
                head.insertBefore( script, head.firstChild );
             },
     
             abort: function() {
                if ( script ) {
                   script.onload( undefined, true );
                }
             }
          };
       }
    });

    jQuery.ajaxTransport(function( s ) {
       // Cross domain only allowed if supported through XMLHttpRequest
       if ( !s.crossDomain || jQuery.support.cors ) {
     
          var callback;
     
          return {
             send: function( headers, complete ) {
     
                // Get a new xhr
                var handle, i,
复制代码

对于同域的js如上文会设置crossDomain为false,此时由于jQuery默认的async为false,因此可以直接按照顺序加载

对于cdn,此时由于s.crossDomain为true,

    / Base inspection function for prefilters and transports
    function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
        var inspected = {},
            seekingTransport = ( structure === transports );
        function inspect( dataType ) {
            var selected;
            inspected[ dataType ] = true;
            jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
                var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
                if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
                    options.dataTypes.unshift( dataTypeOrTransport );
                    inspect( dataTypeOrTransport );
                    return false;
                } else if ( seekingTransport ) {
                    return !( selected = dataTypeOrTransport );
                }
            });
            return selected;
        }
        return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
    }

``` 

因此上端代码将会返回对象scriptTransport,此对象将会直接执行 

 ```javascrpt
    head.insertBefore( script, head.firstChild );
复制代码

由于未采用前端模块化依赖,导致加载的顺序不定(文件大小,网络状况),因此出现一系列的函数未定义

因此需要在部署cdn前优先完成前端模块化建设。

其它外国的倒霉蛋连接一枚Script File Exectuing After Inline Script With CDN or External Domain On HTML injection

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值