Dynamic Script Loading

近年来前端技术可谓发展迅猛,受益于计算机与浏览器内核的性能提升,如今网站前端架构可以设计得更加庞大,许多原本服务器端处理的业务逻辑都挪到了前端进行,因此也相应地出现了许多MVC(Backbone)、MVVM(KnockoutJS)等前端框架和CommonJSRequireJS等接口规范。而为了更好的用户体验,许多现代的站点都采用了B/S只通过数据接口通信的策略,用户一次加载,随后的浏览与操作中网页内容都由JS代码控制加载与渲染完成。这带来的挑战是,一个页面请求将带来更多的代码网络传输(若是没有进行压缩合并那影响就更明显了),同时在一个页面容器内浏览器必须解析更多的JS代码,这二者都严重地影响着页面加载时间。对此,一个比较好的解决办法是将前端结构模块化,然后在初始载入中只读取核心功能部分的代码,随后根据用户的操作加载对应的模块,也就是代码动态加载技术。除了通过Ajax配合eval实现外,一般脚本的动态加载都是通过生成Script节点完成,下面是一个简单的实现:

Dynamic Script Loading
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
;(function () {
"use strict"

var doc = document
var head = doc.head ||
doc.getElementsByTagName('head')[0] ||
doc.documentElement

function scriptOnload(node, callback) {
node.onload = node.onerror = node.onreadystatechange = function () {
if (/loaded|complete|undefined/.test(node.readyState)) {
node.onload = node.onerror = node.onreadystatechange = null
if (node.parentNode) { head.removeChild(node) }
node = undefined
if (typeof callback !== 'undefined') { callback() }
}
}
}

window.scriptOnload = scriptOnload
})()

其中在回调方法中head.removeChild(node)等移除节点的语句是为了避免IE8以下浏览器中的内存泄露。以加载jQuery为例,使用方式为:

Demo
1
2
3
4
5
6
7
8
9
var ns = {}

var jqueryNode = doc.createElement('script')
jqueryNode.async = 'async'
jqueryNode.src = '/path/to/jquery.js'
window.scriptOnload(jqueryNode, function () {
ns.$ = jQuery.noConflict(true)
})
head.appendChild(jqueryNode)