js实现动态加载脚本的方法实例汇总


Posted in Javascript onNovember 02, 2015

本文实例讲述了js实现动态加载脚本的方法。分享给大家供大家参考,具体如下:

最近公司的前端地图产品需要做一下模块划分,希望用户用到哪一块的功能再加载哪一块的模块,这样可以提高用户体验。

所以到处查资料研究js动态脚本的加载,不过真让人伤心啊!,网上几乎都是同一篇文章,4种方法,讨厌其中拷贝别人成果的人,也不加个原文的链接。哎!关键是最后一种方法还有点错误。经过两天的研究查阅资料,在这里和大家分享一下。

首先我们需要一个被加载的js文件,我在一个固定文件夹下创建了一个package.js,打开后在里面写一个方法functionOne,很简单,代码如下:

function functionOne(){
  alert("成功加载");
}

后面的html文件都创建在同一个目录下。

方法一:直接document.write

在同一个文件夹下面创建一个function1.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      //加载js脚本
      document.write("<script src='package.js'><\/script>");
      //加载一个按钮
      document.write("<input type=\"button\" value=\"测试运行效果\" onclick=\"operation()\"\/>");
      //如果马上使用会找不到,因为还没有加载进来,此处会报错
      functionOne();
    }
    function operation()
    {
      //可以运行,显示“成功加载”
      functionOne();
    }
  </script>
</head>
<body>
  <input type="button" value="初始化加载" onclick="init()"/>
</body>
</html>

通过document.write的方式可以往页面写入脚本,如代码所示,点击按钮“初始化加载”后可以加载package.js文件,但是立即运行里面的方法functionOne会找不到此方法,报告错误,而点击第二个按钮(通过document.write动态创建的“测试运行效果”)发现可以执行,此时脚本已经加载完毕了。由于这种方式是异步加载(一边继续执行后面的代码,一边额外开一个线程执行需要加载的脚本),并且document.write会重写界面,明显不实用。

方法二:动态改变已有script的src属性

在同一个文件夹下面创建一个function2.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript" id="yy" src=""></script>
  <script type="text/javascript">
    function init()
    {
      yy.src = "package.js";
      //如果马上使用会找不到,因为还没有加载进来,此处会报错
      functionOne();
    }
    function operation()
    {
      //可以运行,显示“成功加载”
      functionOne();
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
  <input type="button" value="测试运行效果" onclick="operation()"/>
</body>
</html>

此种方法的好处在于不会改变界面元素,不至于重写界面元素,但是同样是异步加载,会有同样的问题。

方法三:动态创建script元素(异步)

在同一个文件夹下面创建一个function3.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      var myScript= document.createElement("script");
      myScript.type = "text/javascript";
      myScript.src="package.js";
      document.body.appendChild(myScript);
      //如果马上使用会找不到,因为还没有加载进来
      functionOne();
    }
    function operation()
    {
      //可以运行,显示“成功加载”
      functionOne();
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
  <input type="button" value="测试运行效果" onclick="operation()"/>
</body>
</html>

此办法的优势相对于第二种而言就是不需要最开始就在界面写一个script标签,缺点还是异步加载,存在同样的问题。

这三种方法都是异步执行的,所以在加载这些脚本的同时,主页面的脚本继续运行,如果用以上的方法,那下面的代码将得不到预期的效果。

不过可以在functionOne前面加一个alert就可以堵塞一下主页面脚本的运行,然后你发现functionOne就可以运行了,或者你的后期代码需要在另一个按钮下执行,一步一步的来,要不就定义一个计时器,在固定时间后再执行后面的代码,不过在项目里面肯定不可能使用这些方法。

其实第三种方法改一点就变成同步加载了。

方法四:动态创建script元素(同步)

在同一个文件夹下面创建一个function4.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      var myScript= document.createElement("script");
      myScript.type = "text/javascript";
      myScript.appendChild(document.createTextNode("function functionOne(){alert(\"成功运行\"); }"));
      document.body.appendChild(myScript);
      //此处发现可以运行
      functionOne();
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
</body>
</html>

此方法并没有加载外部的js文件,而是给myScript添加了子项。在Firefox、Safari、Chrome、Opera和IE9中,这些代码可以正常运行。但是在IE8以及以下的版本中会导致错误。IE将<script>视为一个特殊的元素,不允许DOM访问其子节点。不过可以用<script>元素的text属性来制定js代码,想下面的例子这样:

var myScript= document.createElement("script");
myScript.type = "text/javascript";
myScript.text = "function functionOne(){alert(\"成功运行\"); }";
document.body.appendChild(myScript);
//此处可以运行
functionOne();

经过这样修改之后的代码可以在IE、Firefox、Opera和Safari3及之后版本中运行。Safari3.0之前的版本虽然不能正确地支持text属性,但却允许使用文本节点技术来指定代码。如果需要兼容早期版本的Safari,可以使用下面代码:

var myScript= document.createElement("script");
myScript.type = "text/javascript";
var code = "function functionOne(){alert(\"成功运行\"); }";
try{
    myScript.appendChild(document.createTextNode(code));
}
catch (ex){
    myScript.text = code;
}
document.body.appendChild(myScript);
//此处发现可以运行
functionOne();

这里,首先尝试标准的DOM文本节点方法,因为除了IE8以及以下,所有浏览器都支持这种方式。如果这行代码抛出了错误,那么说明是IE8以及以下,于是就必须使用text属性了。整个过程可以用以下函数来表示:

function loadScriptString(code)
{
  var myScript= document.createElement("script");
  myScript.type = "text/javascript";
  try{
    myScript.appendChild(document.createTextNode(code));
  }
  catch (ex){
    myScript.text = code;
  }
  document.body.appendChild(myScript);
}

然后你可以在其他地方使用此方法来加载需要使用的代码。实际上,这样执行代码与在全局作用于中把相同字符串传递给eval()是一样的。但是我们这里只能使用字符串形式的代码,也有局限性,用户一般希望提供的方法形如loadScriptAddress("package.js")的方式,所以我们还需要继续讨论。

方法五:XMLHttpRequest/ActiveXObject异步加载

在同一个文件夹下面创建一个function5.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      //加载package.js文件,设置script的id为yy
      ajaxPage("yy","package.js");
      //此方法为package.js里面的方法,此处执行方法成功
      functionOne();
    }
    function ajaxPage(sId,url)
    {
      var oXmlHttp = getHttpRequest();
      oXmlHttp.onreadystatechange = function()
      {
        //4代表数据发送完毕
        if ( oXmlHttp.readyState == 4 )
        {
          //0为访问的本地,200代表访问服务器成功,304代表没做修改访问的是缓存
          if(oXmlHttp.status == 200 || oXmlHttp.status == 0 || oXmlHttp.status == 304)
          {
            includeJS(sId,oXmlHttp.responseText);
          }
          else
          {
          }
        }
      }
      oXmlHttp.open("GET",url,true);
      oXmlHttp.send(null);
    }
    function getHttpRequest()
    {
      if(window.ActiveXObject)//IE
      {
        return new ActiveXObject("MsXml2.XmlHttp");
      }
      else if(window.XMLHttpRequest)//其他
      {
        return new XMLHttpRequest();
      }
    }
    function includeJS(sId,source)
    {
      if((source != null)&&(!document.getElementById(sId)))
      {
        var myHead = document.getElementsByTagName("HEAD").item(0);
        var myScript = document.createElement( "script" );
        myScript.language = "javascript";
        myScript.type = "text/javascript";
        myScript.id = sId;
        try{
          myScript.appendChild(document.createTextNode(source));
        }
        catch (ex){
          myScript.text = source;
        }
        myHead.appendChild( myScript );
      }
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
</body>
</html>

ActiveXObject只有IE里面才有,其他浏览器大部分支持XMLHttpRequest,通过此办法我们可以实现动态加载脚本了,不过是异步加载,也没法运行functionOne,第二次就可以运行了,但是可惜的是在IE、Firefox、Safari下可以运行,在Opera、Chrome下会出错,Chrome下的错误如下:

js实现动态加载脚本的方法实例汇总

不过只要发布之后在Chrome和Opera下就不会出现错误了。

其实这里把open里面设置为false就是同步加载了,同步加载不需要设置onreadystatechange事件。

方法六:XMLHttpRequest/ActiveXObject同步加载 

在这里我把一些情况考虑在内,写成了一个方法,封装为loadJS.js,方便以后直接调用,代码如下:

/**
 * 同步加载js脚本
 * @param id  需要设置的<script>标签的id
 * @param url  js文件的相对路径或绝对路径
 * @return {Boolean}  返回是否加载成功,true代表成功,false代表失败
 */
function loadJS(id,url){
  var xmlHttp = null;
  if(window.ActiveXObject)//IE
  {
    try {
      //IE6以及以后版本中可以使用
      xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e) {
      //IE5.5以及以后版本可以使用
      xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
  }
  else if(window.XMLHttpRequest)//Firefox,Opera 8.0+,Safari,Chrome
  {
    xmlHttp = new XMLHttpRequest();
  }
  //采用同步加载
  xmlHttp.open("GET",url,false);
  //发送同步请求,如果浏览器为Chrome或Opera,必须发布后才能运行,不然会报错
  xmlHttp.send(null);
  //4代表数据发送完毕
  if ( xmlHttp.readyState == 4 )
  {
    //0为访问的本地,200到300代表访问服务器成功,304代表没做修改访问的是缓存
    if((xmlHttp.status >= 200 && xmlHttp.status <300) || xmlHttp.status == 0 || xmlHttp.status == 304)
    {
      var myHead = document.getElementsByTagName("HEAD").item(0);
      var myScript = document.createElement( "script" );
      myScript.language = "javascript";
      myScript.type = "text/javascript";
      myScript.id = id;
      try{
        //IE8以及以下不支持这种方式,需要通过text属性来设置
        myScript.appendChild(document.createTextNode(xmlHttp.responseText));
      }
      catch (ex){
        myScript.text = xmlHttp.responseText;
      }
      myHead.appendChild( myScript );
      return true;
    }
    else
    {
      return false;
    }
  }
  else
  {
    return false;
  }
}

此处考虑到了浏览器的兼容性以及当为Chrome、Opera时必须是发布,注释还是写的比较清楚的,以后需要加载某个js文件时,只需要一句话就行了,如loadJS("myJS","package.js")。方便实用。

如果想要实现不发布还非要兼容所有浏览器,至少我还没找出这样的同步加载的办法,我们只能通过异步加载开出回调函数来实现。

方法七:回调函数方式

在同一个文件夹下面创建一个function7.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      //加载package.js文件,设置script的id为yy
      loadJs("yy","package.js",callbackFunction);
    }
    function callbackFunction()
    {
      functionOne();
    }
    function loadJs(sid,jsurl,callback){
      var nodeHead = document.getElementsByTagName('head')[0];
      var nodeScript = null;
      if(document.getElementById(sid) == null){
        nodeScript = document.createElement('script');
        nodeScript.setAttribute('type', 'text/javascript');
        nodeScript.setAttribute('src', jsurl);
        nodeScript.setAttribute('id',sid);
        if (callback != null) {
          nodeScript.onload = nodeScript.onreadystatechange = function(){
            if (nodeScript.ready) {
              return false;
            }
            if (!nodeScript.readyState || nodeScript.readyState == "loaded" || nodeScript.readyState == 'complete') {
              nodeScript.ready = true;
              callback();
            }
          };
        }
        nodeHead.appendChild(nodeScript);
      } else {
        if(callback != null){
          callback();
        }
      }
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
</body>
</html>

这种方式所有浏览器都支持,但是后面的代码必须放在回调函数里面,也就是异步加载了。看需求使用把!我还是比较喜欢第六种方法的。如果是异步加载的话,方法还有好几种,不过我的出发点是希望实现同步加载,这里就不对异步加载做总结了。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
js脚本学习 比较实用的基础
Sep 07 Javascript
动态刷新 dorado树的js代码
Jun 12 Javascript
使用js+jquery实现无限极联动
May 23 Javascript
浏览器页面区域大小的js获取方法
Sep 21 Javascript
javascript实现获取cookie过期时间的变通方法
Aug 14 Javascript
jQuery实现预加载图片的方法
Mar 17 Javascript
基于jQuery实现多标签页切换的效果(web前端开发)
Jul 24 Javascript
禁用backspace网页回退功能的实现代码
Nov 15 Javascript
详解JS去重及字符串奇数位小写转大写
Dec 29 Javascript
ejsExcel模板在Vue.js项目中的实际运用
Jan 27 Javascript
详解Nuxt.js 实战集锦
Nov 19 Javascript
vue-amap根据地址回显地图并mark的操作
Nov 03 Javascript
Jquery时间轴特效(三种不同类型)
Nov 02 #Javascript
Jquery全屏相册插件zoomvisualizer具有调节放大与缩小功能
Nov 02 #Javascript
关注jquery技巧提高jquery技能(前端开发必学)
Nov 02 #Javascript
18个非常棒的jQuery代码片段
Nov 02 #Javascript
js实现文件上传表单域美化特效
Nov 02 #Javascript
非常实用的12个jquery代码片段
Nov 02 #Javascript
jQuery+CSS3折叠卡片式下拉列表框实现效果
Nov 02 #Javascript
You might like
一个目录遍历函数
2006/10/09 PHP
PHP爆绝对路径方法收集整理
2012/09/17 PHP
Yii配置文件用法详解
2014/12/04 PHP
codeigniter显示所有脚本执行时间的方法
2015/03/21 PHP
php使用yield对性能提升的测试实例分析
2019/09/19 PHP
javascript 必知必会之closure
2009/09/21 Javascript
JS+ACTIVEX实现网页选择本地目录路径对话框
2013/03/18 Javascript
javascript中Object使用详解
2015/01/26 Javascript
jquery $(document).ready()和window.onload的区别浅析
2015/02/04 Javascript
moment.js轻松实现获取当前日期是当年的第几周
2015/02/05 Javascript
ECMAScript6函数默认参数
2015/06/12 Javascript
JS脚本实现动态给标签控件添加事件的方法
2016/06/02 Javascript
Vue关于数据绑定出错解决办法
2017/05/15 Javascript
JS实现图片手风琴效果
2020/04/17 Javascript
详解JS数值Number类型
2018/02/07 Javascript
vue.js 图片上传并预览及图片更换功能的实现代码
2018/08/27 Javascript
Vue+webpack项目配置便于维护的目录结构教程详解
2018/10/14 Javascript
JS回调函数简单易懂的入门实例分析
2019/09/29 Javascript
vue+vant-UI框架实现购物车的复选框全选和反选功能
2019/11/05 Javascript
JS端基于download.js实现图片、视频时直接下载而不是打开预览
2020/05/09 Javascript
详解JavaScript数据类型和判断方法
2020/09/04 Javascript
python del()函数用法
2013/03/24 Python
详解Django中的form库的使用
2015/07/18 Python
Python中的命令行参数解析工具之docopt详解
2017/03/27 Python
Python使用正则表达式实现文本替换的方法
2017/04/18 Python
python编程通过蒙特卡洛法计算定积分详解
2017/12/13 Python
Python 3 实现定义跨模块的全局变量和使用教程
2019/07/07 Python
Python 元组操作总结
2019/09/18 Python
python GUI库图形界面开发之PyQt5菜单栏控件QMenuBar的详细使用方法与实例
2020/02/28 Python
django API 中接口的互相调用实例
2020/04/01 Python
python如何爬取网页中的文字
2020/07/28 Python
python中使用asyncio实现异步IO实例分析
2021/02/26 Python
高级技校毕业生自荐信
2013/11/18 职场文书
硕士研究生个人求职信
2013/12/04 职场文书
水毁工程实施方案
2014/04/01 职场文书
JS前端可扩展的低代码UI框架Sunmao使用详解
2022/07/23 Javascript