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