在大型 Web 应用中,高效的数据交互 和按需资源加载 是提升性能与用户体验的关键。YUI 作为企业级框架,提供了完善的 AJAX 解决方案(YAHOO.util.Connect)、动态资源加载工具(Get Utility/YUILoader)以及跨域数据获取方案,解决了传统开发中 "请求冗余""加载阻塞""跨域限制" 等痛点。本节课将深入解析 YUI 的 AJAX 核心方法、动态加载策略、跨域解决方案,并通过实战实现一个融合跨域请求与动态组件加载的页面,掌握企业级应用的资源与数据管理逻辑。

一、AJAX 请求
YUI 的YAHOO.util.Connect模块是处理异步 HTTP 请求的核心,相比原生XMLHttpRequest,它封装了跨浏览器兼容逻辑、请求状态管理、错误处理等功能,尤其适合构建需要高可靠性的企业级应用。
1. 基础请求
asyncRequest是发送 AJAX 请求的核心方法,支持 GET、POST 等 HTTP 方法,通过配置参数实现灵活的请求控制。
语法:
javascript
YAHOO.util.Connect.asyncRequest(
method, // 请求方法:'GET'/'POST'等
url, // 请求URL
callback, // 回调配置对象
postData // POST请求的数据(可选)
);
回调配置对象(callback)核心属性:
success:请求成功(状态码 200)时的回调函数(参数为response对象);failure:请求失败(非 200 状态码或网络错误)时的回调函数;timeout:超时时间(毫秒,默认 30 秒);scope:回调函数中的this指向。
示例 1:GET 请求获取 JSON 数据
javascript
YUI().use('connection', function(Y) { // 加载connection模块
// 回调配置
var callback = {
success: function(response) {
// 解析JSON响应(response.responseText为原始文本)
var data = YAHOO.lang.JSON.parse(response.responseText);
console.log('获取数据成功:', data);
// 渲染数据到页面
renderData(data);
},
failure: function(response) {
console.error('请求失败,状态码:', response.status);
},
timeout: 5000 // 5秒超时
};
// 发送GET请求(带查询参数)
YAHOO.util.Connect.asyncRequest(
'GET',
'/api/users?page=1&size=10',
callback
);
});
// 渲染数据函数
function renderData(users) {
var list = YAHOO.util.Dom.get('userList');
users.forEach(function(user) {
var item = document.createElement('li');
item.innerHTML = user.name;
list.appendChild(item);
});
}
示例 2:POST 请求提交表单数据
javascript
YUI().use('connection', 'dom', function(Y) {
// 序列化表单数据(使用YUI的Dom工具)
var formData = YAHOO.util.Dom.get('userForm').serialize();
var callback = {
success: function() {
alert('提交成功!');
},
failure: function() {
alert('提交失败,请重试');
}
};
// 发送POST请求
YAHOO.util.Connect.asyncRequest(
'POST',
'/api/user/save',
callback,
formData // POST数据(key=value&...格式)
);
});
2. 错误重试机制
网络波动可能导致临时请求失败,YUI 可通过封装实现 "失败自动重试" 机制,尤其适合对数据可靠性要求高的场景(如支付、提交表单)。
实现思路:
- 在
failure回调中判断失败原因(如非 404/500 等致命错误); - 记录重试次数,达到最大次数后停止;
- 重试时使用相同的请求参数重新发送。
示例:带重试的请求封装
javascript
YUI().use('connection', function(Y) {
// 封装带重试的AJAX请求
function requestWithRetry(method, url, callback, postData, maxRetries = 3) {
var retries = 0;
// 内部回调(包装用户传入的callback)
var wrappedCallback = {
success: callback.success,
failure: function(response) {
retries++;
// 判断是否需要重试:未达最大次数,且非致命错误(如500可能是临时的)
if (retries < maxRetries && [500, 502, 503].includes(response.status)) {
console.log(`第${retries}次重试...`);
// 重试请求(复用参数)
YAHOO.util.Connect.asyncRequest(method, url, wrappedCallback, postData);
} else {
// 达到最大次数,调用用户的failure回调
callback.failure(response);
}
},
timeout: callback.timeout || 5000
};
// 发起首次请求
YAHOO.util.Connect.asyncRequest(method, url, wrappedCallback, postData);
}
// 使用封装的重试请求
requestWithRetry(
'GET',
'/api/critical-data',
{
success: function(res) { console.log('获取关键数据成功'); },
failure: function(res) { console.error('多次重试后仍失败'); }
}
);
});
二、动态加载
传统开发中,一次性加载所有 JS/CSS 会导致首屏加载缓慢。YUI 提供两种动态加载方案:Get Utility(简单资源加载)和YUILoader(智能依赖管理),实现 "按需加载",提升首屏性能。
1. Get Utility
YAHOO.util.Get(简称 Get Utility)用于动态加载外部脚本(.js)和样式表(.css),支持并行加载、加载完成回调,适合加载非 YUI 的第三方资源(如统计脚本、地图 SDK)。
核心方法:Get.script()与Get.css()
| 方法名 | 功能 | 示例 |
|---|---|---|
Get.script(url, callback) |
加载单个 JS 文件 | Get.script('chart.js', onScriptLoad) |
Get.css(url, callback) |
加载单个 CSS 文件 | Get.css('theme.css', onCssLoad) |
Get.add(urls, callback) |
加载多个资源(混合) | Get.add(['a.js', 'b.css'], onLoad) |
示例:动态加载图表脚本与样式
javascript
<div id="chartContainer" style="width: 600px; height: 400px;"></div>
javascript
YUI().use('get', function(Y) {
// 动态加载图表库JS和样式
YAHOO.util.Get.add([
'https://cdn.example.com/chart.js', // 第三方图表JS
'https://cdn.example.com/chart-theme.css' // 配套样式
], {
onSuccess: function() {
// 加载完成后初始化图表
if (window.Chart) {
new Chart(YAHOO.util.Dom.get('chartContainer'), {
type: 'bar',
data: { labels: ['A', 'B'], datasets: [{ data: [10, 20] }] }
});
}
},
onFailure: function() {
alert('图表资源加载失败');
}
});
});
- 并行加载:多个资源会同时请求,减少加载时间;
- 顺序保证:若资源有依赖(如 B.js 依赖 A.js),需分两次调用(先加载 A,再在 A 的回调中加载 B)。
2. YUILoader
YUILoader是 YUI 的 "模块加载器",能自动分析模块依赖关系,按需加载 YUI 核心模块及自定义模块,避免手动管理复杂依赖(如加载button控件时,自动加载其依赖的dom、event模块)。
核心特性:
- 依赖自动解析:加载目标模块时,自动加载所有依赖模块;
- 组合请求:将多个模块合并为一个 HTTP 请求(减少请求数);
- 版本控制:支持指定 YUI 版本,确保模块兼容性。
示例:按需加载 YUI 的button控件
javascript
<button id="customBtn">自定义按钮</button>
javascript
// 初始化YUILoader
var loader = new YAHOO.util.YUILoader({
base: 'http://yui.yahooapis.com/2.9.0/build/', // YUI资源基础路径
require: ['button'], // 需要加载的模块(button控件)
loadOptional: false, // 不加载可选依赖
combine: true, // 合并请求(减少HTTP请求)
onSuccess: function() { // 加载完成回调
// 使用button控件初始化按钮
var btn = new YAHOO.widget.Button('customBtn', {
label: 'YUI按钮',
style: 'primary'
});
// 绑定按钮点击事件
btn.on('click', function() {
alert('YUI按钮被点击');
});
}
});
// 启动加载
loader.insert();
- 依赖解析:
button控件依赖dom、event、element等模块,YUILoader会自动加载这些模块,无需手动指定; - 合并请求:
combine: true时,所有模块会通过一个请求加载(如http://yui.yahooapis.com/2.9.0/build/combo?button+dom+event+...),大幅减少请求数。
自定义模块加载:
YUILoader支持加载项目自定义模块(需定义模块信息):
javascript
// 定义自定义模块(myModule依赖dom和event)
YAHOO.util.YUILoader.defineModule('myModule', {
fullname: 'myModule',
version: '1.0.0',
requires: ['dom', 'event'], // 依赖模块
path: 'js/myModule.js' // 模块路径
});
// 加载自定义模块
var loader = new YAHOO.util.YUILoader({
require: ['myModule'],
onSuccess: function() {
// 使用自定义模块功能
YAHOO.myModule.init();
}
});
loader.insert();
三、跨域解决方案
浏览器的同源策略(Same-Origin Policy)限制了 AJAX 请求跨域资源(协议、域名、端口任一不同即为跨域)。YUI 通过ScriptNodeDataSource实现跨域数据获取,其核心是利用<script>标签不受同源策略限制的特性(即 JSONP 原理)。
1. JSONP 原理与ScriptNodeDataSource
- JSONP 核心 :通过动态创建
<script>标签,请求跨域 URL,服务器返回 "函数调用 + 数据" 的脚本(如callback({name:"张三"})),客户端通过预定义的回调函数接收数据; - YUI 封装 :
YAHOO.util.ScriptNodeDataSource简化了 JSONP 流程,自动管理<script>标签的创建与销毁,处理回调函数命名冲突。
2. 跨域请求示例
假设需要从https://api.otherdomain.com/users(跨域)获取用户数据,服务器支持 JSONP(接收callback参数指定回调函数名)。
实现步骤:
javascript
YUI().use('datasource-scriptnode', 'datatable', function(Y) {
// 1. 初始化ScriptNodeDataSource(跨域数据源)
var dataSource = new YAHOO.util.ScriptNodeDataSource('https://api.otherdomain.com/users');
// 配置数据源:指定服务器接收的回调参数名(通常为"callback")
dataSource.configure({
scriptCallbackParam: 'callback', // 服务器用此参数获取回调函数名
responseType: YAHOO.util.DataSource.TYPE_JSON // 响应类型为JSON
});
// 2. 发送跨域请求(获取数据)
dataSource.sendRequest(
'?page=1', // 查询参数(可选)
{
success: function(request, response) {
// response.results为服务器返回的JSON数据
console.log('跨域数据:', response.results);
// 可结合DataTable组件展示数据
renderTable(response.results);
},
failure: function() {
alert('跨域请求失败');
}
}
);
// 3. 用DataTable展示数据(可选)
function renderTable(data) {
var columns = [
{ key: 'id', label: 'ID' },
{ key: 'name', label: '姓名' }
];
new YAHOO.widget.DataTable('tableContainer', columns, dataSource);
}
});
- 服务器响应格式:需返回
callback函数名(数据)的形式,例如:YUI.Env.DataSource.callbacks[0]({ "results": [{ "id":1, "name":"张三" }] }) - 自动回调管理:
ScriptNodeDataSource会自动生成唯一回调函数名(如YUI.Env.DataSource.callbacks[0]),避免全局函数冲突。
四、跨域数据请求与动态组件加载页面
需求说明
实现一个 "热门新闻" 页面,包含以下功能:
- 页面初始只加载核心 CSS 和基础 JS,提升首屏速度;
- 滚动到页面中部时,动态加载 YUI 的
DataTable组件(用于表格展示)和新闻样式 CSS; - 加载完成后,通过跨域请求从第三方 API 获取新闻数据;
- 用
DataTable展示新闻列表,并添加 "加载更多" 按钮(点击时再次跨域请求下一页数据)。
实现步骤
1. HTML 结构与基础样式
javascript
<!DOCTYPE html>
<html>
<head>
<title>动态加载新闻页</title>
<!-- 只加载基础YUI核心和CSS -->
<script src="http://yui.yahooapis.com/2.9.0/build/yahoo/yahoo-min.js"></script>
<script src="http://yui.yahooapis.com/2.9.0/build/dom/dom-min.js"></script>
<script src="http://yui.yahooapis.com/2.9.0/build/event/event-min.js"></script>
<link rel="stylesheet" href="http://yui.yahooapis.com/2.9.0/build/reset/reset-min.css">
<style>
.container { width: 800px; margin: 0 auto; padding: 20px; }
.news-section { margin-top: 500px; } /* 让内容在滚动后可见 */
.loading { color: #666; padding: 20px; text-align: center; }
#loadMore { margin: 20px 0; padding: 10px 20px; background: #0066cc; color: white; border: none; cursor: pointer; }
</style>
</head>
<body>
<div class="container">
<h1>热门新闻</h1>
<div class="news-section">
<div class="loading" id="initialLoading">滚动加载新闻列表...</div>
<div id="tableContainer"></div>
<button id="loadMore" style="display: none;">加载更多</button>
</div>
</div>
</body>
</html>
2. 动态加载逻辑与跨域请求
javascript
// 全局变量:当前页码
var currentPage = 1;
// 标记是否正在加载
var isLoading = false;
// 页面加载完成后初始化
YAHOO.util.Event.onDOMReady(function() {
// 监听滚动事件:滚动到新闻区域时加载组件
YAHOO.util.Event.on(window, 'scroll', checkScrollAndLoad);
});
// 检查滚动位置并加载组件
function checkScrollAndLoad() {
var newsSection = YAHOO.util.Dom.get('initialLoading');
if (!newsSection) return; // 已加载则退出
// 获取新闻区域位置
var sectionTop = YAHOO.util.Dom.getY(newsSection);
var viewportHeight = YAHOO.util.Dom.getViewportHeight();
var scrollTop = YAHOO.util.Dom.getDocumentScrollTop();
// 当新闻区域进入视图(顶部 <= 视口底部)
if (sectionTop <= scrollTop + viewportHeight && !isLoading) {
isLoading = true;
newsSection.innerHTML = '正在加载新闻组件...';
// 动态加载DataTable组件和新闻样式
loadNewsComponents();
}
}
// 加载DataTable组件和样式
function loadNewsComponents() {
// 使用YUILoader加载DataTable(自动处理依赖)
var loader = new YAHOO.util.YUILoader({
base: 'http://yui.yahooapis.com/2.9.0/build/',
require: ['datatable', 'datasource-scriptnode'], // 需要的模块
combine: true,
onSuccess: function() {
// 同时加载新闻专用CSS
YAHOO.util.Get.css('news-table.css', function() {
// 组件和样式加载完成后,请求第一页数据
fetchNewsData(currentPage);
// 显示加载更多按钮
YAHOO.util.Dom.get('loadMore').style.display = 'block';
// 绑定加载更多事件
YAHOO.util.Event.on('loadMore', 'click', function() {
currentPage++;
fetchNewsData(currentPage);
});
});
},
onFailure: function() {
YAHOO.util.Dom.get('initialLoading').innerHTML = '组件加载失败';
}
});
loader.insert();
}
// 跨域请求新闻数据
function fetchNewsData(page) {
var loadingEl = YAHOO.util.Dom.get('initialLoading') ||
document.createElement('div');
loadingEl.className = 'loading';
loadingEl.innerHTML = '加载第' + page + '页数据...';
YAHOO.util.Dom.get('tableContainer').appendChild(loadingEl);
// 初始化跨域数据源
var dataSource = new YAHOO.util.ScriptNodeDataSource(
'https://api.otherdomain.com/news'
);
dataSource.configure({
scriptCallbackParam: 'callback',
responseType: YAHOO.util.DataSource.TYPE_JSON
});
// 发送请求(带页码参数)
dataSource.sendRequest(
'?page=' + page + '&size=10',
{
success: function(request, response) {
// 移除加载提示
loadingEl.remove();
// 渲染新闻表格
renderNewsTable(response.results);
},
failure: function() {
loadingEl.innerHTML = '第' + page + '页数据加载失败';
}
}
);
}
// 用DataTable渲染新闻列表
function renderNewsTable(newsData) {
var tableContainer = YAHOO.util.Dom.get('tableContainer');
// 首次渲染:创建表格
if (!window.newsTable) {
var columns = [
{ key: 'title', label: '标题', sortable: true },
{ key: 'source', label: '来源' },
{ key: 'pubTime', label: '发布时间' }
];
window.newsTable = new YAHOO.widget.DataTable(
tableContainer,
columns,
new YAHOO.util.DataSource(newsData)
);
} else {
// 非首次:追加数据
window.newsTable.addRows(newsData);
}
}
代码解析
- 按需加载触发 :通过监听滚动事件,当新闻区域进入视图时才加载
DataTable组件和样式,减少首屏加载资源; - 组件依赖管理 :
YUILoader自动加载DataTable依赖的datasource、dom等模块,无需手动引入; - 跨域数据获取 :使用
ScriptNodeDataSource发送 JSONP 请求,突破同源限制获取第三方新闻数据; - 渐进式交互:加载过程中显示状态提示("加载组件""加载数据"),加载更多按钮动态出现,提升用户体验。
这个案例完整模拟了企业级应用的 "资源按需加载 + 跨域数据交互" 场景,体现了 YUI 在性能优化和复杂场景处理上的优势。
五、YUI AJAX 与动态加载的历史价值
YUI 的 AJAX 与动态加载机制在前端发展史上具有重要意义:
- 模块化加载先驱 :
YUILoader的 "依赖自动解析" 和 "组合请求" 思想,直接影响了后来的 RequireJS、Webpack 等模块打包工具; - 企业级可靠性:错误重试、超时控制、跨域方案等设计,为大型应用的稳定运行提供了保障;
- 性能优化实践:动态加载、按需加载的理念,至今仍是前端性能优化的核心策略(如现代框架的代码分割、路由懒加载)。
尽管现代前端已采用 Fetch API(替代 AJAX)、ES 模块(替代 YUILoader)和 CORS(跨域方案),但 YUI 解决问题的思路 ------通过系统化设计简化复杂场景------ 仍值得学习。
最后小结
本节课梳理了 YUI 的 AJAX 核心方法(Connect.asyncRequest)、动态加载工具(Get Utility/YUILoader)和跨域解决方案(ScriptNodeDataSource),并通过实战实现了一个融合这些技术的新闻页面。YUI 的这些功能通过封装复杂逻辑、提供统一接口,大幅降低了企业级应用的开发难度。未完待续........