JavaScript BOM 详解:从基础概念到 React 实战
适合人群:有后端开发经验,正在学习 JavaScript、React、TypeScript 的开发者。
学习目标:理解 BOM 的作用,掌握
window、location、history、navigator、浏览器存储和定时器等常用 API。
一、什么是 BOM?
BOM 全称 Browser Object Model(浏览器对象模型) 。
简单理解:
BOM 是 JavaScript 访问和操作浏览器窗口及浏览器功能的一组对象和 API。
之前学习过 DOM,可以这样区分:
| 对比 | DOM | BOM |
|---|---|---|
| 全称 | Document Object Model | Browser Object Model |
| 主要作用 | 操作网页内容 | 访问浏览器功能 |
| 核心对象 | document | window |
| 典型操作 | 修改 HTML、操作节点 | 页面跳转、历史记录、存储 |
| 示例 | document.querySelector() |
window.location.href |
例如:
javascript
// DOM:修改页面内容
document.querySelector("h1").textContent = "Hello";
// BOM:获取当前网页地址
console.log(window.location.href);
一句话记忆:
DOM 管网页内容,BOM 管浏览器功能。
需要注意,BOM 是一个历史上常用的统称,并不是像 DOM 那样独立定义的统一标准。
二、BOM 的核心对象:window
window 是浏览器页面中的全局对象,也是学习 BOM 最重要的入口。
它提供或关联了很多常用功能:
bash
window
├── document # DOM 文档
├── location # 当前 URL、页面跳转
├── history # 浏览器历史记录
├── navigator # 浏览器环境信息
├── screen # 屏幕信息
├── localStorage # 本地持久化存储
├── sessionStorage # 会话存储
├── setTimeout() # 延迟执行
├── setInterval() # 定时重复执行
├── alert() # 提示弹窗
└── innerWidth # 浏览器视口宽度
1. window 的基本使用
javascript
// 浏览器视口宽度
console.log(window.innerWidth);
// 浏览器视口高度
console.log(window.innerHeight);
// 弹出提示框
window.alert("Hello BOM");
// 当前网页地址
console.log(window.location.href);
2. window 为什么可以省略?
例如:
javascript
window.alert("Hello");
通常也可以写成:
scss
alert("Hello");
因为浏览器中的许多全局函数和属性都可以通过 window 访问。
类似地:
dart
window.document === document; // true
重点: window 是浏览器的全局对象,而 document 是它的一个属性。
三、location:URL 与页面跳转(P0)
location 是工作中最常用的浏览器 API 之一。
假设当前页面地址:
bash
https://example.com:8080/tasks/list?id=123&status=running#detail
可以这样获取 URL 信息:
javascript
console.log(window.location.href);
// 完整 URL
console.log(window.location.protocol);
// https:
console.log(window.location.hostname);
// example.com
console.log(window.location.port);
// 8080
console.log(window.location.pathname);
// /tasks/list
console.log(window.location.search);
// ?id=123&status=running
console.log(window.location.hash);
// #detail
1. URL 参数解析
实际开发中,经常需要获取 URL 上的查询参数。
例如:
bash
https://example.com/tasks?id=123&status=running
代码:
csharp
const params = new URLSearchParams(
window.location.search
);
const taskId = params.get("id");
const status = params.get("status");
console.log(taskId); // "123"
console.log(status); // "running"
注意:URLSearchParams.get() 返回字符串或 null。
2. 页面跳转
javascript
// 方式一:直接赋值
window.location.href = "/login";
// 方式二:跳转到新地址
window.location.assign("/login");
// 方式三:替换当前历史记录
window.location.replace("/login");
// 刷新当前页面
window.location.reload();
其中 assign() 和 replace() 的区别:
assign():正常导航,会产生相应的历史记录。replace():替换当前历史记录条目,返回时通常不会回到被替换的页面。
业务场景:
javascript
function logout() {
// 清理登录相关状态...
window.location.replace("/login");
}
四、history:浏览器历史记录(P1)
history 用来操作浏览器的会话历史记录。
1. 前进与后退
javascript
// 返回上一页
window.history.back();
// 前进一页
window.history.forward();
// 后退两步
window.history.go(-2);
2. 修改 URL,但不刷新页面
javascript
window.history.pushState(
{ taskId: 123 },
"",
"/tasks/detail/123"
);
执行后,浏览器地址栏会变化,但不会因为 pushState 自动重新加载页面。
也可以使用:
javascript
window.history.replaceState(
{ taskId: 123 },
"",
"/tasks/detail/123"
);
区别:
pushState():增加一个历史记录条目。replaceState():替换当前历史记录条目。
注意:这两个方法通常要求目标 URL 与当前页面同源,而且修改 URL 本身不会自动更新页面内容。
3. React Router 和 history 的关系
在 React 单页应用(SPA)中:
javascript
import { useNavigate } from "react-router";
function TaskPage() {
const navigate = useNavigate();
return (
<button onClick={() => navigate("/tasks")}>
查看任务
</button>
);
}
React Router 可以利用浏览器 History API 管理路由导航与页面渲染。
实际 React 开发中,应用内部跳转通常优先使用路由库,而不是直接操作 window.location。
五、localStorage 与 sessionStorage(P0)
浏览器提供了两种常用的客户端存储 API。
| 对比 | localStorage | sessionStorage |
|---|---|---|
| 保存位置 | 浏览器本地 | 浏览器会话存储 |
| 刷新页面 | 通常保留 | 通常保留 |
| 关闭标签页 | 通常保留 | 通常清除 |
| 数据类型 | 字符串 | 字符串 |
| 常见用途 | 主题、用户偏好 | 单标签页临时状态 |
1. localStorage
javascript
// 存储
localStorage.setItem("theme", "dark");
// 读取
const theme = localStorage.getItem("theme");
console.log(theme); // dark
// 删除指定数据
localStorage.removeItem("theme");
// 清空当前源的全部 localStorage
localStorage.clear();
注意:localStorage 主要用于保存非敏感的客户端数据,不建议直接存储密码或敏感身份凭证。
2. 保存对象
Storage 只能直接存储字符串。
如果需要保存对象:
javascript
const user = {
name: "Kevin",
age: 25
};
// 保存对象
localStorage.setItem(
"user",
JSON.stringify(user)
);
// 读取对象
const raw = localStorage.getItem("user");
const savedUser = raw ? JSON.parse(raw) : null;
console.log(savedUser?.name);
这里涉及两个方法:
JSON.stringify():对象转 JSON 字符串。JSON.parse():JSON 字符串转对象。
实际业务中,读取不可信或可能损坏的数据时,应通过 try/catch 处理 JSON 解析错误。
3. sessionStorage
使用方式基本相同:
ini
sessionStorage.setItem("taskId", "123");
const taskId = sessionStorage.getItem("taskId");
sessionStorage.removeItem("taskId");
通常用于标签页会话期间的临时数据。
六、定时器:setTimeout 与 setInterval(P0)
1. setTimeout:延迟执行一次
javascript
setTimeout(() => {
console.log("2秒后执行");
}, 2000);
这里:
- 第一个参数:回调函数。
- 第二个参数:延迟时间,单位毫秒。
可以取消:
javascript
const timer = setTimeout(() => {
console.log("执行任务");
}, 2000);
clearTimeout(timer);
2. setInterval:定时重复执行
javascript
const timer = setInterval(() => {
console.log("定时检查任务状态");
}, 5000);
表示每隔大约 5 秒执行一次回调。
停止定时器:
scss
clearInterval(timer);
3. 实际场景:任务状态轮询
例如每隔 5 秒查询一次任务状态:
javascript
async function getTaskStatus() {
const response = await fetch("/api/task/status");
if (!response.ok) {
throw new Error("查询失败");
}
return response.json();
}
const timer = setInterval(async () => {
try {
const data = await getTaskStatus();
console.log(data);
} catch (error) {
console.error(error);
}
}, 5000);
注意:如果一次请求超过 5 秒,setInterval 可能产生请求重叠。
实际项目中可以考虑在上一次请求结束后再通过 setTimeout 安排下一次轮询,或者使用 SWR、TanStack Query 等数据请求工具。
七、navigator:获取浏览器环境信息(P1)
navigator 提供浏览器运行环境相关的信息。
例如:
arduino
// 浏览器是否报告在线
console.log(navigator.onLine);
// 浏览器语言
console.log(navigator.language);
// 用户代理信息
console.log(navigator.userAgent);
实际使用场景:
- 获取浏览器语言。
- 判断是否可能处于离线状态。
- 访问剪贴板等浏览器功能。
例如:
csharp
await navigator.clipboard.writeText("任务ID:123");
这会尝试向剪贴板写入文本。
注意:剪贴板 API 通常需要安全上下文(HTTPS 等),并受到浏览器权限和用户交互限制。
另外,navigator.onLine 不能可靠证明业务后端接口一定可访问。
八、React 中如何使用 BOM?
React 本质上仍然运行在 JavaScript 环境中,因此可以使用浏览器 API。
例如:
javascript
import { useEffect, useState } from "react";
function WindowInfo() {
const [width, setWidth] = useState(
() => window.innerWidth
);
useEffect(() => {
function handleResize() {
setWidth(window.innerWidth);
}
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return <div>窗口宽度:{width}px</div>;
}
核心流程:
- 通过
window.innerWidth获取窗口宽度。 - 通过
addEventListener()监听窗口变化。 - 使用
setWidth()更新 React 状态。 - React 根据新状态重新渲染页面。
- 在
useEffect清理函数中解除监听。
注意:上面示例适用于浏览器客户端渲染。如果项目涉及 SSR,初始渲染时可能没有 window,需要额外处理。
九、常见问题
1. window 和 document 有什么区别?
window 是浏览器页面的全局对象。
document 表示当前 HTML 文档,提供 DOM 操作能力。
例如:
dart
window.location.href; // BOM
document.querySelector("div"); // DOM
2. BOM 和 DOM 是独立的吗?
概念上可以区分,但实际 API 之间有联系。
例如:
dart
window.document
就是通过 window 访问 DOM 文档。
3. BOM 和 Node.js 有什么区别?
Node.js 并不是浏览器环境。
例如:
javascript
console.log(window);
在普通 Node.js 环境中,window 通常不存在。
Node.js 和浏览器都支持 JavaScript,但它们提供的运行环境 API 不完全一样。
4. React 中需要直接操作 window 吗?
需要,但建议根据场景选择:
- 操作 URL:优先考虑 React Router。
- 管理状态:优先使用 React State。
- 页面存储:根据需要使用 Storage API。
- 浏览器事件:可以配合 useEffect 注册和清理。
十、学习重点总结
| 知识点 | 优先级 | 需要掌握 |
|---|---|---|
| window | P0 | 全局对象的含义 |
| location | P0 | URL 获取、页面跳转 |
| localStorage | P0 | setItem、getItem |
| setTimeout | P0 | 延迟执行 |
| setInterval | P0 | 定时执行与清理 |
| history | P1 | pushState、back |
| sessionStorage | P1 | 临时会话存储 |
| navigator | P1 | 常用环境 API |
| screen | P2 | 了解即可 |
最后总结
BOM 主要解决 JavaScript 如何访问浏览器功能的问题。
最值得记住的是:
javascript
// 当前地址
window.location.href;
// 页面跳转
window.location.assign("/login");
// 浏览器存储
localStorage.setItem("key", "value");
// 延迟执行
setTimeout(() => {}, 1000);
// 定时执行
setInterval(() => {}, 5000);
// 获取窗口宽度
window.innerWidth;
对于 React + TypeScript 业务开发,重点掌握 window、location、localStorage、定时器和浏览器事件即可。
至于其他 BOM API,实际工作中遇到了再查文档,不需要一次性全部学习。