DOM 就绪与异步请求:从 jQuery Ajax/JSONP 迁移到 Fetch
本文目录
旧笔记把 DOM 就绪、jQuery 选择器、事件绑定、Ajax 与 JSONP 混在一起。维护时先看页面是经典脚本还是 ES module:模块默认延后执行,defer 脚本在 DOM 解析完成后按顺序执行;DOMContentLoaded 表示文档解析完成,load 还会等待图片等依赖资源。多数新页面无需再给所有脚本包一层 jQuery ready。
原生 DOM 与 jQuery 对象
document.querySelector 返回 DOM Element;jQuery 选择器返回 jQuery 集合。要交给原生 API 时明确取出节点,要交给 jQuery 方法时明确包装节点。事件委托应绑定在稳定的祖先上;组件卸载或列表替换时移除监听。不要把链式 API 当成无需理解返回值的语法糖。
Fetch 的状态、错误与取消
Fetch 只有网络层失败等情况才会 reject;HTTP 404/500 仍会得到 Response,调用方要检查 ok。输入和响应都按契约验证,失败文案不暴露敏感服务细节。搜索框请求还要处理旧响应晚到覆盖新结果的竞态;AbortController 可以请求取消,但后端仍需自行处理已到达的请求。
async function loadResults(query, signal) {
const url = new URL('/api/search', location.origin);
url.searchParams.set('q', query);
const response = await fetch(url, {
headers: { Accept: 'application/json' },
signal,
});
if (!response.ok) throw new Error('搜索暂不可用');
const payload = await response.json();
if (!Array.isArray(payload.items)) throw new TypeError('响应格式无效');
return payload.items;
}
let activeController;
function search(query) {
activeController?.abort();
activeController = new AbortController();
return loadResults(query, activeController.signal).catch((error) => {
if (error.name !== 'AbortError') console.error(error);
});
}
function disposeSearch() {
activeController?.abort();
activeController = undefined;
}
search('浏览器缓存');
实际输入联想通常先做防抖,再为每次有效请求分配控制器;即使取消,也可用递增 requestId 在渲染前丢弃过时结果。服务端要对关键词长度、权限、速率和查询成本做校验。
CORS 与 JSONP
CORS 是服务器通过响应头允许浏览器读取跨源响应的机制,不是授权系统。凭据模式、预检请求、允许来源和 Cookie 策略需要服务器明确配置。JSONP 通过插入 script 执行第三方返回的 JavaScript,只支持 GET,响应代码在页面权限内执行;它扩大脚本供应链信任边界,不能作为通用 API 跨域方案,也不应传密码、令牌或隐私数据。
迁移步骤:先让服务端返回 JSON 并配置精确 CORS;再将 jQuery $.ajax 改为 Fetch 或保留旧浏览器项目的 XHR 适配;最后删除动态 script 回调、清理超时和全局回调名。若暂时无法迁移,限制 JSONP 到可信同源/受控域名的公开只读数据,并设置严格 CSP 与回滚期限。
发布前检查
检查 204、非 JSON 错误页、网络超时、断网、重复提交、旧响应竞态、请求取消、跨域凭据和组件卸载。用户输入展示用 textContent;任何富文本都需明确的 sanitizer allowlist。入口时机、请求协议、展示安全和权限验证是四件独立的事。
参考:MDN DOMContentLoaded、MDN window load、MDN Fetch、MDN AbortController、MDN CORS、OWASP DOM XSS Prevention。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。