尝试创建一个简单的JSONP调用,但它并不总是有效,我不知道为什么。这是代码:
服务器端(http://server/server.php
):
<?php
$res = json_encode("It works!");
if(isset($_GET['callback']) === TRUE) {
header('Content-Type: text/javascript;');
header('Access-Control-Allow-Origin: http://client');
header('Access-Control-Max-Age: 3628800');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
echo $_GET['callback']."(".$res.");";
} else {
echo $res;
}
?>
客户端(http://client/client.html
):
<html>
<head><title>JSONP</title></head>
<body>
<h1>JSONP Experiment</h1>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
function process(data) {
$('#result').text(data);
}
$.getJSON(
'http://server/server.php?callback=?',
{'callback': 'process'}
);
</script>
<p id="result"></p>
</body>
</html>
此代码有效并显示“It works!”在我的
块中。
当我不使用{'callback': 'process'}
并将?callback = process直接放入$ .getJSON()网址时,为什么它不起作用?
如果我使用<script src="http://server/server.php?callback=process"></script>
代替$ .getJSON()调用,为什么它不起作用?
两个非工作案例实际上都返回process("It works");
但是这没有被执行,为什么?
由于
答案 0 :(得分:5)
这里有两个不同的问题,答案不同
当您使用$.getJSON()
并将回调的名称字面上放在URL中时它不起作用的原因是因为jQuery的工作方式。
首先让我们看一下jQuery如果检测到你的$.getJSON()
调用期望JSONP,如果你没有在设置对象中明确地传递任何明确的选项 - uses这个正则表达式:
/(=)\?(?=&|$)|\?\?/
这明确地查找查询字符串中的=?
或仅包含单个?
的查询字符串 - 实质上,检测URL将返回JSONP时需要问号。
没有它,jQuery使用XHR发出Ajax请求,服务器返回正确的数据。接下来会发生什么取决于同源策略。如果像您显示的代码一样,服务器通过Access-Control-*
标头指示允许请求,那么数据将是可访问的,只要它是对客户端的源服务器的Ajax请求。如果这些标头不存在,则客户端代码将无法访问返回的数据。
但是,至关重要的是,因为它只是制作标准的Ajax请求并且没有向DOM添加<script>
元素,这意味着响应文本永远不会被评估为Javascript代码 - 这是关键的最后一步JSONP机制。
第二个版本回答起来有点棘手,并且知道答案对所提供的信息是正确的,但是对于上面显示的HTML布局,我有理由相信这是原因:您的HTML元素是在错误的订单。
在页面加载时处理DOM时,处理在每个<script>
元素处停止,同时相关的Javascript被同步加载和处理。这是为了确保所有Javascript按照预期的顺序执行,并确保以特定位置方式直接修改DOM的任何调用 - 例如document.write()
(您应该从不如果您不知道的话,请使用 - 得到正确的尊重。
这样做的结果是,如果您在<script>
标记之前将<p id="result">
元素放在DOM 中,那么结果标记实际上并不存在于调用process()
函数的点。并且因为您使用了jQuery选择器而不是document.getElementById()
,所以如果您尝试直接修改它,则会吞下可能导致的错误。
这就是许多Javascript开发人员(包括我自己)现在告诉您应该将<script>
标记作为<body>
中最后一个元素的原因,因为它可以提高感知性能 - 尽管实际上加载和处理所有页面资源所需的网络时间完全相同,但它允许浏览器更快地呈现页面。这样做也无需使用$(document).ready()
/ DOMContentLoaded
事件来延迟执行(顺便提一下,这也会解决这个问题,但是会稍微麻烦一点)。