Scripts without async or defer attributes, as well as inline scripts, are fetched and executed immediately, before the browser continues to parse the page. - MDNjavascript
the blocking nature of JavaScript, which is to say that nothing else can happen while JavaScript code is being executed. In fact, most browsers use a single process for both user interface (UI) updates and JavaScript execution, so only one can happen at any given moment in time. The longer JavaScript takes to execute, the longer it takes before the browser is free to respond to user input. - Nicholas C. Zakas「High Performance JavaScript 」html
上面引用两段话的意思大体是,当浏览器解析DOM文档时,一旦遇到 script 标签(没有defer 和 async 属性)就会当即下载并执行,与此同时浏览器对文档的解析将会中止,直到 script 代码执行完成。出现这种堵塞行为一方面是由于浏览器的UI渲染,交互行为等都是单线程操做,另外一方是由于 script 里面的代码可能会影响到后面文档的解析,好比下面的代码:java
html<script type="text/javascript"> document.write("The date is " + (new Date()).toDateString()); </script>
这个堵塞特性会严重的影响用户体验,下面是几种优化方案:shell
</body>
前面。不过更好的方法是下面的非堵塞加载脚本(Nonblocking Scripts)的方法:跨域
Script 有一个 defer 属性,拥有这个属性的script代表这个script不会修改DOM,所以这段脚本会在文档树所有解析完成后触发( to be executed after the document has been parsed). 但这个属性并不被全部的浏览器支持。浏览器
原理就是使用脚本建立 script 元素,设置 src 需为要动态添加脚本的 URL,再把这个 script 添加到DOM中。有时咱们须要动态脚本加载完成后再执行某些操做,这就须要咱们在脚本加载完成后添加一个回调函数,这个能够经过 script 的 onload 事件实现。下面的实现代码:app
jsfunction loadJS(url, callback){ var script = document.createElement('script'); script.type = 'text/javascript'; if(script.readyState){ // 兼容IE的旧版本 script.onreadystatechange = function(){ if(script.readyState == 'loaded' || script.readyState == 'complete'){ script.onreadystatechange = null; callback(); } } } else{ script.onload = function(){ callback(); } } script.src = url; document.getElementsByTagName('head')[0].appendChild(script); }
有时咱们须要咱们动态加载的脚本按照咱们加载的顺序执行,但上面的实现并不能保证这一点,加载的脚本在下载完成后就会当即执行,而不会按照咱们定义的顺序。要解决这个问题也不难,能够参照下面的代码:async
jsloadJS('a.js', function(){ loadJS('b.js', function(){ loadJS('c.js', function(){ app.init(); }) }) })
当有大量的脚本须要动态添加时,这样写也会遇到问题;另外的解决方案是利用一些现成的库,好比 LABjs函数
原理是利用XMLHttpReques(XHR)对象,动态获取一段JS代码,而后插入文档。
相对其余方法来讲的一个优势是能够“懒执行”,也就是JS代码已经先下载好了并无执行,能够在须要的来执行(?)(以前的动态脚本在下载后会当即执行)。实现代码:fetch
jsfunction xhrLoadJS (url, callback){ var xhr = new XMLHttpRequest(); xhr.open('get', url, true); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){ var script = document.createElement('script'); script.type = 'text/script'; script.text = xhr.responseText; eval(xhr.responseText); // 执行代码 document.body.appendChild(script); callback(); } } } xhr.send(null); }
缺点是不能跨域请求