ASP.NET站点性能提升-提高JavaScript加载
问题:JavaScript加载阻塞页面渲染
JavaScript是静态文件,和图片和CSS文件一样。但是,与图片不同,当使用<script>标签加载或执行JavaScript文件,页面渲染会暂停。
页面可能包含<script>后的依赖于<script>脚本块。如果加载JavaScript文件不阻塞页面渲染,其它脚本块可能在加载前已经执行了,导致标本错误。
方法1:在其它组件后开始加载
这个方法的是通过并行加载JavaScript和CSS文件、图像,而不是在JavaScript后,达到尽快渲染页面的目标。这样,当JavaScript完成加载时,CSS和图像也完成了加载,或至少减少在JavaScript加载后需要加载它们的时间。
为了并行加载JavaScript和CSS、图像,需要在开始加载JavaScript前加载它们。对于CSS文件很简单,将<link>标签放在<script>标签前:
<link rel="Stylesheet" type="text/css" href="css/style1.css" /> <script type="text/javascript" src="js/script1.js"></script>
开始加载图片需要一点技巧,因为图片通常在页面body中,而不是在页面head中。
<script type="text/javascript"> var img1 = new Image(); img1.src = "images/chemistry.png"; </script> <link rel="Stylesheet" type="text/css" href="css/style1.css" /> <script type="text/javascript" src="js/script1.js"></script>
第二个方法:
<body> <div style="display:none"> <img src="images/chemistry.png" /> </div> <script type="text/javascript" src="js/script1.js"></script>
也可以预加载多个图片。首先加载最重要的图片。这样当页面渲染时,它们可能会立刻显示。
<script type="text/javascript"> var img1 = new Image(); img1.src = "images/important.png"; var img1 = new Image(); img2.src = "images/notsoimportant.png"; var img1 = new Image(); img3.src = "images/unimportant.png"; </script>
方法2:更快地加载 JavaScript
图片使用的技术
JavaScript是静态文件,就像图片和CSS文件。所以许多应用于图片的技术也可以应用于JavaScript,包括无cookie子域、缓存和加速并行加载。
免费内容发布网络
- Google AJAX Libraries API
http://code.google.com/apis/ajaxlibs/ - Microsoft Ajax Content Delivery Network
http://www.asp.net/ajaxlibrary/cdn.ashx - jQuery CDN
http://docs.jquery.com/Downloading_jQuery
在ASP.NET 4.0及更高版本中,可以使用ScriptManager控件从Microsoft AJAX CDN加载ASP.NET AJAX脚本文件。设置EnableCdn属性为true:
<asp:ScriptManager ID="ScriptManager1" EnableCdn="true" runat="server" />
GZip压缩
并不是所有的文件都能从压缩中受益,例如,JPEG、PNG和GIF文件,因为它们已经被压缩了。IIS 7配置文件applicationHost.config中包括了当开启静态压缩后,被压缩的mime类型:
<staticTypes> <add mimeType="text/*" enabled="true" /> <add mimeType="message/*" enabled="true" /> <add mimeType="application/javascript" enabled="true" /> <add mimeType="*/*" enabled="false" /> </staticTypes>
为了允许IIS分辨某种文件的mime类型,applicationHost.config包括了默认的从文件扩展名到mime类型的映射,包括:
<staticContent lockAttributes="isDocFooterFileName"> ... <mimeMap fileExtension=".js" mimeType="application/x-javascript" /> ... </staticContent>
如果仔细看,会发现.js扩展名,默认映射到一个不能压缩的mime类型。
最简单的方法是修改站点的web.config:
<system.webServer> <staticContent> <remove fileExtension=".js" /> <mimeMap fileExtension=".js" mimeType="text/javascript" /> </staticContent> </system.webServer>
注意:IIS7只要压缩“频繁”访问的文件。
缩小JavaScript文件
- Microsoft Ajax Minifer
http://www.asp.net/ajaxlibrary/AjaxMinDocumentation.ashx - Google Closure Compiler
http://code.google.com/closure/compiler/ - ShrinkSafe
http://www.dojotoolkit.org/reference-guide/shrinksafe/index.html - YUI Compressor
http://developer.yahoo.com/yui/compressor/ - Yahoo! UI Library: YUI Compressor for .Net
http://yuicompressor.codeplex.com/ - JSMin
http://www.crockford.com/javascript/jsmin.html - CompressorRater
http://compressorrater.thruhere.net/
HTTP handler
using System.IO; using System.Web.UI.WebControls; using System.Web.Caching; using Yahoo.Yui.Compressor; using System.Web.UI; namespace MinifyingHttpHandler { public class JavaScriptHttpHandler : IHttpHandler { public bool IsReusable { get { return true; } } public void ProcessRequest(HttpContext context) { const string cacheKeyPrefix = "js__"; string compressedText = null; string uncompressedText = null; try { Uri url = context.Request.Url; string path = url.PathAndQuery; string cacheKey = cacheKeyPrefix + path; compressedText = (string)context.Cache[cacheKey]; if (compressedText == null) { string filePath = context.Server.MapPath(path); uncompressedText = File.ReadAllText(filePath); compressedText = JavaScriptCompressor.Compress(uncompressedText); CacheDependency cd = new CacheDependency(filePath); context.Cache.Insert(cacheKey, compressedText, cd); } } catch{} context.Response.AddHeader("Cache-Control", "public,max-age=31536000"); context.Response.Write(compressedText ?? (uncompressedText ?? "")); } } }
在web.config配置handler
如果使用IIS 7的集成管道模式,只需要在system.webServer的handlers节中加入新的handler:
<configuration> <system.webServer> <handlers> <add name="Minifying" verb="*" path="*.js" type="MinifyingHttpHandler.JavaScriptHttpHandler, MinifyingHttpHandler" resourceType="File"/> </handlers> </system.webServer> </configuration>
如果使用IIS6或IIS7经典模式,加入httpHandlers:
<configuration> <system.web> <httpHandlers> <add verb="*" path="*.js" type="MinifyingHttpHandler.JavaScriptHttpHandler, MinifyingHttpHandler" /> </httpHandlers> </system.web> </configuration>
开启动态文件压缩
使用ASP.NET产生JavaScript,需要开启动态文件压缩。
组合或分解
一个流行的减少JavaScript加载时间的方法是将多个JavaScript文件组合成单个文件,这样只需要加载和个大文件。另一个方法正相反,是将一个大的JavaScript文件分解成多个小文件,这样就可以并行加载了。
什么时候组合
使用IE6时,因为IE只能串行下载静态文件。
什么时候分解
使用IE7以上,和Firefox和Chrome时。
阻止304消息
当运行测试时,注意浏览器在缓存中存储JavaScript文件。所以,当按下Ctrl + F5刷新页面时,一些浏览器,包括Internet Explorer,会发送If-Modified-Sinced和If-None-Match头到服务器。如果文件没有修改,服务器会回复304消息,表示浏览器缓存中的文件还是最新的,而不是一个包括整个文件的200消息。
可以使用Fiddler开发代理工具(http://www.fiddler2.com/fiddler2)。它的过滤功能请允许使If-Modified-Sinced和If-None-Match头失效。
实现自动文件组合
实现自动文件组合的方法:
- ASP.NET ScriptManager控件
- ASP.NET中的压缩和HTML、CSS、JS缩小
- Combres 2.0
除了ASP.NET ScriptManager控件,这些方法也支持组合CSS文件。
ASP.NET ScriptManager控件
<asp:ScriptManager runat="server"> <CompositeScript> <Scripts> <asp:ScriptReference Path="js/script1.js" /> <asp:ScriptReference Path="js/script2.js" /> </Scripts> </CompositeScript> </asp:ScriptManager>
如果只使用ScriptManager组合自己的文件,而不加载ASP.NET Ajax库,按照以下方式使用ScriptManager指令:
- Using ScriptManager with other frameworks
http://weblogs.asp.net/bleroy/archive/2008/07/07/using-scriptmanager-with-other-frameworks.aspx
在ASP.NET中压缩和进行HTML、CSS、JS缩小
下载:
它的缺点是使用包含查询字符串的脚本和CSS文件地址,这会缓存功能。这些查询字符串包含组成组合文件的单个文件的名字,所以它们会很长。最大的缺点是它会在CSS文件在不同的目录并且使用相对路径时,会出现问题,例如:
background-image: url(../images/chemistry.png);
这是因为组合的CSS文件不能保证路径与原路径相同,所以相对路径不再指向同一位置。
Combres 2.0
下载:
这个方案最大的缺点是需要建立配置文件,当网站变化时,要修改配置文件。
移除不用的代码
有很多工具可以帮助识别不再使用的代码,但是要小心—JavaScript是高度动态的语言,所以工具也很难确保一段代码真正不被使用。
- JSLint
http://www.jslint.com/lint.html - Jsure
http://aurochs.fr/jsure.html - JSCoverage
http://siliconforks.com/jscoverage/
方法3:按需加载JavaScript
JavaScript分为两类—渲染页面的代码,处理用户界面事件的代码,例如按钮点击。
虽然渲染代码需要和页面一起加载和执行,用户界面代码可以延迟加载。
另一方面,这需要分离从渲染代码中用户界面代码。需要调用可能还没有加载的代码,告诉访问者代码正在加载,最后在加载后调用代码。
从渲染代码中分离用户界面代码
一个辨别哪些代码在加载页面时使用的工具是Page Speed,一个Firefox插件。
http://code.google.com/speed/page-speed/.
OndemandLoader library
// Only ever instantiate this object once on a page. // That is, do NOT do this: // var loader1 = new OnDemandLoader(scriptMap1, scriptDependencies1); // var loader2 = new OnDemandLoader(scriptMap2, scriptDependencies2); function OnDemandLoader(scriptMap, scriptDependencies) { // --------------------------------------------------- // Private functions and fields // Shows for each function which script file has that function. var _scriptMap = scriptMap; // Shows for each script file which other script files // need to be loaded to make it work. Also contains a test symbol // that will be present after the script file has loaded. var _scriptDependencies = scriptDependencies; // Store the this pointer to the loader object. Need it to find out whether // event handlers were attached. It is ok to do this here, because this class // is only ever instantiated once. var _thisObj = this; // The task list holds all outstanding tasks - essentially the functions // that need to be executed. var _taskList = new Array(); // Shows a "loading ..." box for a control. var showLoading = function(control) { if (((typeof (control)) == 'object') && (control != null) && ((typeof (control.offsetLeft)) == 'number')) { var loadingElem = document.createElement('div'); loadingElem.style.position = 'absolute'; loadingElem.style.left = (control.offsetLeft + 5) + 'px'; loadingElem.style.top = (control.offsetTop + 5) + 'px'; loadingElem.className = 'loading'; var loadingTextElem = document.createTextNode('Loading ...'); loadingElem.appendChild(loadingTextElem); var docBody = document.getElementsByTagName("body")[0]; docBody.appendChild(loadingElem); control.loadingElem = loadingElem; } } // Hides a "loading ..." box that is being shown for a control. var hideLoading = function(control) { if ((control != null) && (control.loadingElem)) { control.loadingElem.parentNode.removeChild(control.loadingElem); control.loadingElem = null; } } // Returns the path of the script that defines the given function. // Returns empty string if there is no script associated with the given function. var functionSource = function(fname) { var i = _scriptMap.length - 1; for (; i >= 0; i--) { if (_scriptMap[i].fname == fname) { return _scriptMap[i].src; } } return ''; } // Returns true if longString ends in shortString (or is equal). // For example, 'abcdef' ends in 'def'. var endsWith = function(longString, shortString) { var longLen = longString.length; var shortLen = shortString.length; if (longLen < shortLen) { return false; } return (longString.substring(longLen - shortLen) == shortString); } // Determines whether there is a script tag with the given script source. // If such a script tag exists, returns that script tag. // If no such script tag exists, returns null. var scriptWithSrc = function(src) { var scriptTags = document.getElementsByTagName('script'); var scriptTagsLen = scriptTags.length; for (var i = 0; i < scriptTagsLen; i++) { if (endsWith(scriptTags[i].src, src)) { return scriptTags[i]; } } return null; } // Returns true if one of the array elements in _taskList // matches task. To match, they need to have the same // function name and refer to the same object. var taskExists = function(task) { var taskListLen = _taskList.length; for (var i = 0; i < taskListLen; i++) { if ((_taskList[i].fname == task.fname) && (_taskList[i].thisObj == task.thisObj)) { return true; } } return false; } // Returns true if all scripts listed in scriptDependencies // have loaded. var allScriptsLoaded = function() { var i = _scriptDependencies.length - 1; for (; i >= 0; i--) { if (eval('typeof ' + _scriptDependencies[i].testSymbol) == 'undefined') { return false; } } return true; } var checkEventOnallscriptsloaded = function() { if ((typeof (_thisObj.onallscriptsloaded) == 'function') && allScriptsLoaded()) { _thisObj.onallscriptsloaded(); } } // Executed after a script tag has loaded. // Executes all tasks where the functions are now available. var onScriptLoaded = function() { var i = 0; while (i < _taskList.length) { var task = _taskList[i]; var fname = task.fname; var allSymbolsDefined = functionExists(fname); var testSymbols = task.testSymbols; var j = testSymbols.length - 1; for (; (j >= 0) && allSymbolsDefined; j--) { allSymbolsDefined = (eval('typeof ' + testSymbols[j]) != 'undefined'); } if (allSymbolsDefined) { // The function is available, and all scripts that are required // have been loaded. Execute it. // Remove task from the array. // By removing the task, it doesn't get executed twice. _taskList.splice(i, 1); hideLoading(task.thisObj); checkEventOnallscriptsloaded(); var fref = eval(fname); fref.apply(task.thisObj, task.args); } else { // function not available. Go to next task. i++; } } } // Attaches onload handler to a script element. var attachOnLoad = function(scriptElem) { scriptElem.onload = onScriptLoaded; scriptElem.onreadystatechange = function() { if ((scriptElem.readyState === "loaded") || (scriptElem.readyState === "complete")) { onScriptLoaded(); } } } // Returns record describing the given script // from _scriptDependencies. var getScriptRecord = function(src) { var i = _scriptDependencies.length - 1; for (; i >= 0; i--) { if (_scriptDependencies[i].src == src) { return _scriptDependencies[i]; } } return new Array(); } // Returns in array testSymbols all function names that need // to be defined to conclude that the script with the given src // has loaded. // // If a script is not dependent on other scripts, testSymbols will // have one entry. If it is dependent on say 3 scripts, it will contain // 4 entries (one for each script that needs to have loaded). var getTestSymbols = function(src, testSymbols) { // find the scripts that this script depends on var scriptRecord = getScriptRecord(src); var dependentOn = scriptRecord.dependentOn; var testSymbol = scriptRecord.testSymbol; // Visit all scripts that this script is dependent on var i = dependentOn.length - 1; for (; i >= 0; i--) { getTestSymbols(dependentOn[i], testSymbols); } testSymbols.push(testSymbol); } // Makes sure that script with given src is loaded. // After the script has loaded, the function whose name is in fname should have been // added. That function will then be executed, with thisObj as the this pointer // and the arguments passed in args. var ensureScriptLoad = function(src, fname, thisObj, args) { // Create a task object that has the name of the function to call, // its this pointer, and the arguments to pass to the function. var task = new Object(); task.fname = fname; task.thisObj = thisObj; task.args = args; var testSymbols = new Array(); getTestSymbols(src, testSymbols); task.testSymbols = testSymbols; // Add the task object to the _taskList array. // That way, the function will be executed after the // script has loaded. if (!taskExists(task)) { _taskList.push(task); showLoading(thisObj); } // Load the script, and any scripts that need to be loaded as well // so the script runs properly. loadScript2(src); } // Ensures the script, and all the scripts it depends // on, are loaded. var loadScript2 = function(src) { // find the scripts that this script depends on var scriptRecord = getScriptRecord(src); var dependentOn = scriptRecord.dependentOn; // Load all scripts that this script is dependent on, // by recurrently calling this function. var i = dependentOn.length - 1; for (; i >= 0; i--) { loadScript2(dependentOn[i]); } // Check whether the script is already being loaded. // If not, create a new script tag so it starts loading. // Otherwise, make sure the onload handlers are attached. var scriptElem = scriptWithSrc(src); if (scriptElem == null) { var scriptElem = document.createElement('script'); scriptElem.src = src; // attach handlers before starting the download. That way, you will never // miss the onload event. attachOnLoad(scriptElem); document.getElementsByTagName('head')[0].appendChild(scriptElem); } else { attachOnLoad(scriptElem); } } // Returns true if a function with the given name // exists, and is not a stub. var functionExists = function(fname) { var ftype; ftype = eval('typeof ' + fname); if (ftype != 'function') { return false; } var fref = eval(fname); if (fref.stub) { return false; } return true; } // --------------------------------------------------- // Public functions // Ensures the script, and all the scripts it depends // on, are loaded. this.loadScript = function(src) { loadScript2(src); } // Takes the name of a function to execute (as a string), // followed by the pointer to the control that the function is called for, // followed by // the arguments to the function. Accesses the arguments via the // arguments array. this.runf = function(fname, thisObj) { var args = []; // empty array // copy all other arguments we want to pass on to the function // whos name is in fname. The first 2 arguments passed to runf // are fname and thisObj, the rest of the arguments will be // the arguments to be passed on to the function. for (var i = 2; i < arguments.length; i++) { args.push(arguments[i]); } // If the function exists (ftype equals 'function'), execute it // by calling apply. Otherwise, call ensureScriptLoad, which ensures // that the script that contains the function is loaded, and the function // then executed. if (functionExists(fname)) { var fref = eval(fname); checkEventOnallscriptsloaded(); fref.apply(thisObj, args); } else { var src = functionSource(fname); if (src == '') { alert('No script for function ' + fname); } ensureScriptLoad(src, fname, thisObj, args) } } }
OnDemanderLoader的一个缺点是总是并行加载所有需要的脚本。如果一个脚本自动执行在另一个脚本中定义的函数,如果另一个脚本还没有加载,会报一个JavaScript错误,但是,如果库脚本文件只定义了函数和其它对象,OnDemanderLoader会工作地很好。
初始化OnDemanderLoader
示例代码中定义了两个数组:脚本映射数组和脚本依赖数组。脚本映射数组说明了脚本文件:
var scriptMap = [ { fname: 'btn1a_click', src: 'js/Button1Code.js' }, { fname: 'btn1b_click', src: 'js/Button1Code.js' }, { fname: 'btn2_click', src: 'js/Button2Code.js' } ];
函数btn1a_click和btn1b_click在js/Button1Code.js中定义,btn2_click在js/Button2Code.js中定义。
第二个数组定义了运行每个脚本文件需要的其它脚本文件:
var scriptDependencies = [ { src: '/js/Button1Code.js', testSymbol: 'btn1a_click', dependentOn: ['/js/UILibrary1.js', '/js/UILibrary2.js'] }, { src: '/js/Button2Code.js', testSymbol: 'btn2_click', dependentOn: ['/js/UILibrary2.js'] }, { src: '/js/UILibrary2.js', testSymbol: 'uifunction2', dependentOn: [] }, { src: '/js/UILibrary1.js', testSymbol: 'uifunction1', dependentOn: ['/js/UILibrary2.js'] } ];
Button1Code.js依赖UILibrary1.js和UILibrary2.js。Button2Code.js依赖UILibrary2.js。UILibrary1.js依赖UILibrary2.js,UILibrary2.js不需要其它任何脚本文件。
构造loader对象:
<script type="text/javascript" src="js/OnDemandLoader.js"> </script> var loader = new OnDemandLoader(scriptMap, scriptDependencies);
调用未加载函数
有两种方法调用未加载的函数:
- 调用loader函数。
- 调用stud函数。
第一种方法。OnDemandLoader对象暴露了一个加载函数runf,它接收调用的函数名、调用参数和当前this指针作为参数:
function runf(fname, thisObj) { // implementation }
例如:
<input id="btn1a" type="button" value="Button 1a" onclick="loader.runf('btn1a_click', this, this.value, 'more info')" />
如果按钮的处理函数是编程赋予的,那么:
<input id="btn1b" type="button" value="Button 1b" /> <script type="text/javascript"> window.onload = function() { document.getElementById('btn1b').onclick = function() { loader.runf('btn1b_click', this); } } </script>
预加载
现在,当用户点击一个按钮时,处理点击的代码还没有加载。如果代码加载需要很多时间,这就是一个问题了。
一个解决方法是在页面加载后初始化用户界面代码,而不是当用户界面事件触发时。
可以使用OnDemandLoader对象的loadScript函数实现预加载。这个函数加载JavaScript文件和它依赖的文件,而不会阻塞页面渲染。
<script type="text/javascript"> window.onload = function() { document.getElementById('btn1b').onclick = btn1b_click; loader.loadScript('js/Button1Code.js'); loader.loadScript('js/Button2Code.js'); } </script>
方法4:无阻塞加载JavaScript
这个方法的思想是加载所有的脚本文件而不阻塞页面渲染。
移动所有的<script>标签到页面尾部
将用户界面代码和渲染代码分离
渲染页面的代码一般要比处理用户界面事件的代码小得多。
使用Firefox的Page Speed插件可以找出哪些JavaScript函数不是渲染页面所必须的。
<head runat="server"> <script type="text/javascript" src="js/Beautify.js"></script> </head> <body> ... page contents <script type="text/javascript"> beautify(); // run code that helps render the page </script> <script type="text/javascript" src="js/UICode.js"></script> <script type="text/javascript"> attachEventHandlers(); </script> </body>
页面加载指示
<div id="pageloading" style="position:fixed; top: 10px; left: 50%;" >Loading ...</div> <script type="text/javascript"> beautify(); function disableButtons(disable) { var inputTags = document.getElementsByTagName('input'); var inputTagsLen = inputTags.length; for (var i = 0; i < inputTagsLen; i++) { inputTags[i].disabled = disable; } } disableButtons(true); // Disable all input elements </script> <script type="text/javascript"> attachEventHandlers(); document.getElementById('pageloading').style.display = 'none'; disableButtons(false); // Re-enable all input elements </script>
与页面同步加载代码
在前面的解决方案中,用户界面代码加载是在页面已经加载完成并渲染后初始化的。
但是,如果页面的HTML需要很长时间加载,可能需要在页面开始时启动加载,这样代码加载是与HTML同步加载的。
可以使用OnDemandLoader对象实现这个功能。可以在页面加载时加载一个或多个脚本文件集,在每个脚本集加载完成后,都可以调用一个方法。最后,它暴露了一个onallscriptsloaded事件,当所有脚本文件都加载完成后触发,可以用来移除页面加载指示。
初始化loader对象
var scriptMap = [ { fname: 'attachEventHandlers', src: 'js/UICode.js' }, { fname: 'beautify', src: 'js/Beautify.js' } ]; var scriptDependencies = [ { src: 'js/UICode.js', testSymbol: 'attachEventHandlers', dependentOn: [] }, { src: 'js/Beautify.js', testSymbol: 'beautify', dependentOn: [] } ]; <script type="text/javascript" src="js/OnDemandLoader.js"> </script> var loader = new OnDemandLoader(scriptMap, scriptDependencies);
当页面加载时,加载代码
<body> <div id="pageloading" ...>Loading ...</div> <script type="text/javascript"> loader.onallscriptsloaded = function() { document.getElementById('pageloading').style.display = 'none'; disableButtons(false); } loader.runf('beautify', null); loader.runf('attachEventHandlers', null); </script>
保证页面渲染后运行代码
最后一个问题,脚本文件可能在页面完成渲染前已经加载结束了。这样,代码就可能更新页面或者关联事件处理程序失败。
解决这个问题的方法是在它们加载后,和页面渲染结束后,都进行调用。
try { attachEventHandlers(); } catch (err) { } try { beautify(); } catch (err) { }
如何知道页面渲染完成?常用的方法包括:
- 创建页面的onload事件。这是最常用的方法,但有个大问题。当OnDemandLoader对象开始加载脚本文件,它会向DOM中插入一个<script>标签:
var se = document.createElement('script'); se.src = src; document.getElementsByTagName('head')[0].appendChild(se);
这个方法加载脚本文件,而不会阻塞页面渲染,除了在FireFox,它会阻塞onload事件。这意味着如果渲染代码加载很快,用户界面代码花费很长时间,渲染代码依然会被延迟,直到用户界面代码完成加载。
-
将<script>标签放在包含attachEventHandlers和beautify的页面的尾部。不幸的是,Firefox不仅阻塞onload,还会阻塞所有的script标签,直到所有的代码加载完成。
-
在页面的最后部放置一个隐藏元素,定时检查那个元素是否存在。如果存在,整个页面加载完成。
可以使用XMLHttpRequest而不是在DOM中插入<script>标签异步加载JavaScript代码的方式使用前两个方法。但是,这样就不能从其它主机上加载脚本文件了。例如,不能从Google CDN上加载JQuery库。
在这个例子中,使用第三种方法。
在页面结尾放置一个隐藏元素:
<div id="lastelement" style="display: none;"></div> </body>
在页面开始处运行检查代码:
function pollpage() { var lastelement = document.getElementById('lastelement'); if (lastelement == null) { setTimeout("pollpage();", 100); return; } try { attachEventHandlers(); } catch (err) { } try { beautify(); } catch (err) { } if (document.getElementById('pageloading'). style.display != 'none') { disableButtons(true); } } pollpage();
提升广告加载
如果使用的广告网络,例如DoubleClick或Google AdWords,它们会提供一段代码放在页面上:
<script src="http://adserver.js?....."></script>
通常这没问题。但是,有时广告服务器会变慢,就会延迟广告下的页面。
解决这处问题的方法是在事件现在渲染完成后加载广告。
<div id="ad" style="width: 486px; height: 60px; background: #ffffff url('/images/throbber.gif') no-repeat center;"> </div> <script type="text/javascript"> var scriptloaded = false; var divloaded = false; function adloaded() { ... } </script> <div id="tempad" style="display:none"> <script type="text/javascript" src="http://adserver.js?....." onload="scriptloaded=true; adloaded()" onreadystatechange="if (this.readyState=='complete') { scriptloaded=true; adloaded(); }"> </script> </div> <script type="text/javascript"> divloaded = true; adloaded(); </script> function adloaded() { if ((!scriptloaded) || (!divloaded)) { return; } var et = document.getElementById("tempad"); et.parentNode.removeChild(et); var d = document.getElementById("ad"); d.appendChild(et); et.style.display = "block"; }
提升CSS加载
移除未使用的CSS选择器
使用Firefox插件 https://addons.mozilla.org/en-US/firefox/addon/5392/。
加载CSS而不阻塞渲染
<script type="text/javascript"> var scriptElem = document.createElement('link'); scriptElem.type = "text/css"; scriptElem.rel = "Stylesheet"; scriptElem.href = "css/style1.css"; document.getElementsByTagName('head')[0].appendChild(scriptElem); </script>
使用这个方法时,页面首先加载。当CSS加载完成后,改变页面外观。这可能不是你想要的效果,即使页面渲染时间更短。
更多资源
- What ASP.NET Developers Should Know About JavaScript
http://odetocode.com/Articles/473.aspx - Function.apply and Function.call in JavaScript
http://odetocode.com/Blogs/scott/archive/2007/07/05/function-apply-and-function-call-in-javascript.aspx - JavaScript and HTML DOM Reference
http://www.w3schools.com/jsref/default.asp - Ecmascript reference
http://www.devguru.com/Technologies/ecmascript/quickref/javascript_index.html - JavaScript Kit - JavaScript Tutorials
http://www.javascriptkit.com/javatutors/ - Object Oriented Programming in JavaScript
http://mckoss.com/jscript/object.htm - QuirksMode – JavaScript
http://www.quirksmode.org/js/contents.html - HowToCreate - JavaScript Tutorial
http://www.howtocreate.co.uk/tutorials/javascript/ - Microsoft CDN for JQuery or Google CDN?
http://stackoverflow.com/questions/1447184/microsoft-cdn-for-jquery-or-google-cdn - Speeding up website load times: Using a public CDN for Javascript libraries such as jQuery
http://gem-session.com/2010/06/speeding-up-website-load-times-using-a-public-cdn-for-javascript-libraries-such-as-jquery - CompressorRater
http://compressorrater.thruhere.net/ - On-Demand Javascript
http://ajaxpatterns.org/On-Demand_Javascript