本文将为您提供关于关于requireJS的同步加载和异步加载的详细介绍,同时,我们还将为您提供关于04.01异步加载基础——关于异步加载&数据提交、04.02zTree的异步加载流程——关于异步加载&
本文将为您提供关于关于requireJS的同步加载和异步加载的详细介绍,同时,我们还将为您提供关于04.01 异步加载基础 —— 关于 异步加载 & 数据提交、04.02 zTree 的异步加载流程 —— 关于 异步加载 & 数据提交、AngularJS + ui-router + RequireJS异步加载注册controller/directive/filter/service、define 解析依赖,判断状态,初始化/触发加载 --------require 同步加载(直接返回)/异步加载(创建匿名模块,判断状态,初始化/触发加载)的实用信息。
本文目录一览:- 关于requireJS的同步加载和异步加载
- 04.01 异步加载基础 —— 关于 异步加载 & 数据提交
- 04.02 zTree 的异步加载流程 —— 关于 异步加载 & 数据提交
- AngularJS + ui-router + RequireJS异步加载注册controller/directive/filter/service
- define 解析依赖,判断状态,初始化/触发加载 --------require 同步加载(直接返回)/异步加载(创建匿名模块,判断状态,初始化/触发加载)
关于requireJS的同步加载和异步加载
这篇随笔主要记录require(''name'')和require([''name1'',''name2''])在同步和异步加载使用的区别
1、require(''name'')同步加载模块的形式
define(function(require, exports, module) {
var a = require(''a''),
b = require(''b'');
//Return the module value
return function () {};
}
);
(1)首先看上面的代码,使用了var a = require(''a'')这样的写法,这是一种同步调用模块的写法(因为加载a模块后直接返回而不必放在回调函数中使用), 说明此时在该define函数作用域里,模块a已被加载装配完毕,并可以通过require函数返回a模块,注意此时require函数中使用的参数是模块名而非数组;
(2)其次,该define函数是一种commonJS的简化写法,回调函数前并未声明依赖关系的数组,即并非define([''require'',''exports'',''module''],function(require, exports, module) {});这样的写法。此时require会使用
Function.prototype.toString() 对回调函数进行解析,并通过解析发现require(''a'')和require(''b'')。由于在调用回调函数之前,require会预先加载模块的所有依赖,所以在调用回调函数前会预先装配a模块和b模块,因此在调用require(''a'')和require(''b'')时,a和b模块已经提前装配好了;
(3)此外还需要注意的是,当define函数声明依赖关系数组时,当需要在回调函数使用var a = require(''a'')这样的写法时必须要将所需的模块a添加到依赖关系数组中,因为当define包含依赖关系数组时,require会将依赖关系数组中的依赖视为回调函数所需的所有依赖,即上面的代码就应改为如下形式,否则会报依赖关系没有加载的错误:
define([''require'',''exports'',''module'',''a'',''b''],function(require, exports, module) {
var a = require(''a'');
b = require(''b'');
//Return the module value
return function () {};
}
);
(4) 由于使用require(''name'')的原理是将所需的依赖预先加载然后再能进行调用,所以不能再全局作用域中使用var a = require(''a'')这样的写法,这样的写法必须包含在define或require([...],function(){})的回调函数中;
(5)由于使用commonJS写法时,require检测依赖关系的机制是通过调用Function.prototype.toString() ,所以不能使用以下写法,否则会报依赖关系没有加载的错误:
define(function(require, exports, module) {
var a = getRequireModule(require, ''a'');
b = getRequireModule(require, ''b'');
//Return the module value
return function () {};
}
);
function getRequireModule(require, moduleName) {
return require(moduleName);
}
2、 require([''name1'', ''name2''])异步加载模块的形式
(1)通过require([...])形式获取模块的依赖,和上面不同的是,调用require函数使用的参数是数组而非模块名,这种调用方式是异步调用的方式,即不能使用var a = require([''a''])这样的方式同步返回一个模块,require([''a''])返回的是一个函数,而非a模块向外部暴露的接口,应该在回调函数中使用模块a,即如下的方式才是正确的:
define([''a''], function(a) {
console.log(a);
})
(2)和require(''a'')的另一个区别是,由于require([''a''])是一种异步依赖模块的方法,所以在使用commonJS简化的define写法时,加载依赖关系时不会检测到[''a'']中所包含的a模块
(3)由于异步加载的关系,所以在使用时仍应注意有两种情况:
1、当依赖的模块遵循AMD规范时,该模块将会被包含在define函数中,会拥有自己的作用域和向外部暴露的接口,需要通过回调函数才能使用这些模块向外提供的接口;
2、当依赖的模块不遵循AMD规范时,该模块被加载后,由于模块中的js代码具有全局作用域,理论上能够在外部而不必在回调函数中使用模块的接口,但是由于模块是异步加载的,在使用依赖模块提供的接口时,在外部无法保证模块是否已加载完毕,所以仍然应在回调函数中使用依赖模块。
(4)当require调用需要使用回调函数时,正确的写法应该是:
require([''dependency''], function (dependency) {});
写成require(''dependency'', function (dependency) {});
是错误的
04.01 异步加载基础 —— 关于 异步加载 & 数据提交
简介
使用树结构,肯定会有很多同学直接提问:“我的页面如何异步加载节点数据呢?” 这一章将主要针对 zTree 的异步加载进行讲解。
看这篇文档时,你需要对照 API 文档进行学习(http://www.treejs.cn/v3/api.php)
什么是异步加载
使用 zTree 的不少朋友,不仅仅是 zTree 的初学者,更是 前端开发的初学者,对于这部分朋友,请你先去把以下基础内容学习一下:
- AJAX 教程
- jQuery 官网的 ajax 文档
- jQuery ajax 中文参考手册
zTree 与 异步加载
对于异步加载节点数据这一功能, 你可以使用 zTree 的 Ajax,也可以完全不使用。
使用自己的 ajax 方法
- 在代码中,根据自己的需求,使用 ajax 获取节点数据, 然后去 init 初始化 zTree 或 在 zTree init 后,使用 addNodes 方法去添加节点
- 如果需要实现逐级加载, 那么可以利用 onExpand 回调捕获展开的事件,然后 自己使用 ajax 加载数据,加载后使用 addNodes 方法即可 这里几个非常重要的关键点一定要注意,否则肯定会出问题:
某个父节点加载子节点时,一定要设置状态,避免其在加载过程中,用户反复折叠、展开操作,进行多次加载,导致子节点重复
父节点加载子节点成功后, 再次展开时,就不要再反复进行异步加载,除非你先把子节点清除,然后重新加载
zTree 的 异步加载模式配置
参考Demo:http://www.treejs.cn/v3/demo.php#_108
配置
阅读以下代码时,请务必结合 API 的说明进行理解。
var setting = {
async: {
// 开启 zTree 的异步加载模式
enable: true,
// 设置 ajax 的 url
// url 也可以设置为 function,可以针对不同的父节点从不同的接口获取子节点数据
url:"../asyncData/getNodes.php",
// 配置节点的属性参数,这些配置的参数将会当作 ajax 的参数传给后端
autoParam:["id", "name=n", "level=lv"],
// 配置url 的其他参数,这些参数也会直接当作 ajax 的参数传给后端
// 如果需要动态修改 otherParam,请利用 beforeAsync 回调 去修改 zTreeObj.setting.async.otherParam 内容
otherParam:{"otherParam":"zTreeAsyncTest"},
// contentType 和 dataType 和 type 都是与 ajax 传输 & 接受的数据类型相关
contentType: ''application/json'',
dataType: ''text'',
type: ''post'',
// dataFilrer 是专门用于在浏览器端获取数据后,将数据传给 zTree 前进行处理的过程。
// 尤其是当你的服务端无法直接生成 zTree 节点格式的数据时,这个方法尤为重要
dataFilter:null
}
};
- async.url 具体使用请参考 4.03 如何动态更换加载数据的 url
- async.contentType & dataType & type 具体使用请参考 4.04 如何处理 Ajax 参数提交
- async.dataFilter 具体使用请参考 4.05 如何预处理节点数据
后台数据接口
后台为 ajax 提供的接口,需要满足的要求就是:
- 正确接受 ajax 请求的参数,以便于根据不同的父节点生成不同的子节点数据
- 正确生成 js 可以阅读的 JSON 格式字符串
如果你是逐级生成节点数据,一定要给父节点设置属性 isParent = true,否则 zTree 不会把它当作父节点来处理的
//例如:
[
{ "id":"01", "name":"n1", "isParent":true},
{ "id":"02", "name":"n2", "isParent":false},
{ "id":"03", "name":"n3", "isParent":true},
{ "id":"04", "name":"n4", "isParent":false}
]
异步加载的相关回调
参考Demo:http://www.treejs.cn/v3/demo.php#_109
- setting.callback.beforeAsync 这是一个在出发 Ajax 之前被触发的回调,主要用于让程序在触发 Ajax 之前处理一些事情,例如修改 otherParam 参数、根据节点控制是否允许异步加载等
- setting.callback.onAsyncSuccess 当 Ajax 请求完毕,成功获取数据,并且添加到 zTree 后,会触发此回调。如果你需要在异步加载后做一系列操作,一定要记住此回调
- setting.callback.onAsyncError 当 Ajax 请求完毕,Ajax 请求出现错误时,触发此回调,正常情况下此回调基本无意义,但是对于异常处理非常关键!千万不要忽略它的重要性
异步加载的禁忌
1. AJAX 的同步加载
使用异步加载,本身就是为了提升效率,但有的朋友因为搞不清楚异步加载的工作流,从而产生许多程序错误。然后为了解决问题,就直接把异步加载设置为同步模式,这样最严重的问题就是会在加载时导致 js 代码阻塞,并且页面也是完全无法操作。这样做违背了 AJAX 的初衷!!
想具体了解异步加载流程的,请参考 4.02 zTree 的异步加载流程
2. 异步加载变成了无限循环
务必正常设置 异步加载的参数, 并且保证 服务端正常获取对应的参数,切莫每次请求都获取同样的数据! 否则每次都生成同样的一批数据,这只会让你的树无限的加载下去,每次加载的都是相同的节点,展开后又是相同的节点。。。于是你就崩溃了
3. 异步加载 & 全部展开
异步加载模式,一般有两种用法:
-
初次加载节点时采用异步加载,但直接加载全部节点数据
这种情况一般用于不在页面上生成数据,并且节点数据量不大,数据关系简单的情况
-
每次异步加载时,只加载当前这一级的节点数据
这种一般用于有较规范的数据格式,并且数据量较大的情况,所以既然你的数据量很大,也使用了异步加载来实现,就不要再同时使用全部展开功能了!!这也是
严重违背了 AJAX 的初衷
,你只会让你的程序越来越慢!不过,仍然是有一些极端需求的,所以一定要实现这种需求的,请参考Demo:http://www.treejs.cn/v3/demo.php#_512
另外,如果你有更深的需求,可以参考 4.07 如何自动加载多级节点 和 4.08 如何自动加载某路径的节点
04.02 zTree 的异步加载流程 —— 关于 异步加载 & 数据提交
简介
使用 zTree 的前端初学者太多了,所以我必须要仔细讲一下异步加载的代码执行过程。
如果你是个老司机,请忽略本篇文章
1. Javascript 的 AJAX 代码执行过程
jQuery 的 ajax 代码举例:(你可以利用浏览器的调试工具查看 console 的输出结果,代码执行顺序一目了然)
console.log(''code start'');
$.ajax({
url:"your server interface url", // example: "../asyncData/getNodes.php"
success: function(data) {
console.log(''success'');
},
error: function() {
console.log(''error'');
}
});
console.log(''code running'');
var i, j, k;
for (i=0; i< 99999; i++) {
for (j=0; j< 9999; j++) {
k = i * j / (i + j);
}
}
console.log(''code end'');
上面代码执行后的输出结果:
code start
code running
code end
success or error
如果你同时查看 network 你会发现,在输出 code running 的同时,浏览器已经发起了 ajax 请求。
综上所述:发起异步请求后,一定要等异步加载完成后,再执行与异步加载获取数据相关的活动
,否则肯定会出现找不到数据的错误结果!
2. zTree 的 异步加载流程
看了前面的讲解,对于那些总是说:“我的数据已经加载了,但是为什么 zTree 得不到数据??”的朋友,应该有了初步认识!
对于使用异步加载获取 zNodes 后,再初始化 zTree 的朋友,一定要在 ajax 的 success 回调里面先获取数据,然后再初始化 zTree!!!
下面讲一下 zTree 与 Ajax(异步加载)的结合方式:
- zTree 在内部把 jQuery 的 ajax 封包了一下,通过 setting.async.* 的配置可以对 Ajax 的基本参数进行设置
- zTree 提供了 setting.callback.beforeAsync 回调允许用户在 Ajax 请求之前修改 Ajax 的对应设置 或 禁止 Ajax 请求
- zTree 提供了 setting.callback.onAsyncSuccess / onAsyncError 两个回调作为 Ajax 的 成功 & 失败 的回调
- zTree 对初始化后无子节点的父节点,展开时主动进行异步加载操作,获取子节点
- zTree 对 父节点设置了 isAjaxing 属性,用于判断 该父节点是否正进行 Ajax 加载子节点, 从而避免重复发起 Ajax 请求
- zTree 对 父节点设置了 zAsync 属性,用于判断 该父节点是否已经进行过 Ajax 加载子节点,如果进行过,则在展开时不会进行异步加载操作
AngularJS + ui-router + RequireJS异步加载注册controller/directive/filter/service
一般情况下我们会将项目所用到的controller/directive/filter/sercive预先加载完再初始化AngularJS模块,但是当项目比较复杂的情况下,应该是打开对应的界面才加载对应的controller等资源,但是AngularJS一旦初始化,之后加载的controller/directive/filter/sercive是不会自动注册到模块上的。用AngularJS + ui-router + RequireJS来构建项目应该是比较常见的,所以我就基于这个条件来看看如何解决这个问题。
目录结构:
HTML结构非常简单,两个链接,分别改变路由切换到不同子页面:
main.js配置文件路径,初始化模块