jsNote
Promise 的详细使用方法总结
Promise
是 JavaScript 用于处理 异步操作 的重要机制。它表示一个未来可能会完成(fulfilled)、拒绝(rejected)或仍在进行中(pending)的操作。
1. Promise 的基本语法
1 | const promise = new Promise((resolve, reject) => { |
执行过程
new Promise()
创建一个Promise
对象。executor
函数(第一个参数)接收两个参数resolve
和reject
,用于异步任务的成功或失败处理。promise.then()
处理成功的resolve
结果。promise.catch()
处理失败的reject
结果。
2. Promise 的三种状态
状态 | 描述 |
---|---|
pending (进行中) |
初始状态,异步任务尚未完成 |
fulfilled (已成功) |
任务成功完成,调用 resolve() |
rejected (已失败) |
任务失败,调用 reject() |
一旦 Promise
变为 fulfilled
或 rejected
,它的状态就不会再改变。
3. Promise 链式调用
1 | new Promise((resolve) => { |
说明
then()
返回一个新的 Promise,可以继续调用then()
处理下一个异步任务。- 如果
then()
里面抛出错误,会跳到catch()
进行错误处理。
4. Promise 的 catch()
处理错误
如果 Promise
发生错误,就会进入 catch()
:
1 | new Promise((resolve, reject) => { |
注意:
catch()
只捕获前面链式调用中的错误。catch()
相当于.then(null, errorHandler)
,但更简洁。
5. Promise 的 finally()
无论 Promise
成功或失败,finally()
都会执行:
1 | new Promise((resolve, reject) => { |
✅ 常见用途:
- 关闭加载动画
- 清理资源
6. 并行执行多个 Promise
6.1 Promise.all()
一次性执行多个 Promise
,等所有任务完成后再返回:
1 | Promise.all([ |
✅ 特点:
- 全部成功 ✅ → 返回所有
Promise
结果的数组[res1, res2, ...]
- 任何一个失败 ❌ → 立即返回错误,不执行后续任务
6.2 Promise.allSettled()
等待所有 Promise
结束(无论成功或失败):
1 | Promise.allSettled([Promise.resolve("成功"), Promise.reject("失败")]).then( |
✅ 特点:
- 不会中断,返回所有
Promise
状态{ status, value/reason }
- 适合需要收集所有任务结果的场景
6.3 Promise.race()
返回最快完成的 Promise
:
1 | Promise.race([ |
✅ 适用场景:
- 超时控制(例如 HTTP 请求超时)
- 获取最快响应的请求
6.4 Promise.any()
返回第一个**成功的 Promise
**:
1 | Promise.any([ |
✅ 特点:
- 只要有 1 个成功就返回,忽略失败
- 所有都失败时才会
catch()
7. Promise 结合 async/await
async/await
让异步代码看起来像同步代码:
1 | async function fetchData() { |
✅ 优点:
- 代码更清晰
- 自动返回 Promise
- 错误处理简单(使用
try...catch
)
8. 自己封装 Promise
8.1 封装 setTimeout
1 | function wait(ms) { |
✅ 可用于:防抖、防止短时间多次请求
8.2 封装 AJAX 请求
1 | function fetchData(url) { |
✅ 可以替代 axios
进行 API 请求
9. 总结
✅ 基本用法
new Promise((resolve, reject) => {...})
.then()
处理成功,.catch()
处理失败.finally()
统一执行清理任务
✅ 高级用法
Promise.all()
✅:所有成功才返回,任意失败则抛错Promise.allSettled()
✅:所有都返回结果,不会中断Promise.race()
✅:返回第一个完成的Promise
Promise.any()
✅:返回第一个成功的Promise
✅ 推荐使用 async/await
- 让代码更清晰
- 用
try...catch
处理错误
💡 Promise
是现代 JavaScript 异步编程的核心,掌握它可以提升你的开发能力!🚀
2.async & await
async
和 await
并 不是严格一一对应 的。虽然 await
只能在 async
函数内使用,但一个 async
函数中可以有 **多个 await
**,也可以 **不使用 await
**。
1. 一个 async
函数可以有多个 await
1 | async function fetchData() { |
解释:
await
等待Promise
解决(resolve),然后继续执行下一行代码。- 这里
async
对应多个await
。
2. async
函数可以不使用 await
1 | async function noAwait() { |
解释:
async
函数默认会返回一个Promise
,即使内部没有await
。
3. await
也可以嵌套在非 async
函数中
虽然 await
必须在 async
函数中使用,但可以通过 async
包裹:
1 | function normalFunction() { |
解释:
- 这里
normalFunction
不是async
,但内部用 立即执行的async
函数 来使用await
。
4. await
只会等待当前 Promise
,不会阻塞整个程序
1 | async function fetchData() { |
输出顺序:
1 | 开始 |
解释:
await
只会阻塞当前async
函数,不会影响外部同步代码。
5. await
不一定需要 async
,在顶层可用(ES2022)
在 ES2022(ES13) 及更新版本中,await
也能在顶层作用域直接使用:
1 | await new Promise((resolve) => |
(但在旧环境中,必须用 async
包裹)
总结
用法 | 是否需要 async |
---|---|
await 在 async 函数中使用 |
✅ 必须 |
async 函数可以不含 await |
✅ 允许 |
一个 async 函数可有多个 await |
✅ 允许 |
await 在非 async 函数中 |
❌(但可用 async 立即执行函数) |
await 在顶层(ES2022+) |
✅ 允许 |
所以,**async
和 await
并不是一一对应** 的,而是 await
依赖 async
,但 async
**不一定需要 await
**。
3.Debounce & Throttle
防抖(Debounce)和节流(Throttle)是两种常见的优化函数执行频率的技术,适用于监听 resize
、scroll
、input
、click
等事件,避免频繁触发回调函数导致性能问题。
1. 防抖(Debounce)
特点:
- 只有在 停止触发事件后一段时间,回调函数才会执行。
- 如果在等待时间内又触发了事件,则重新计时。
适用场景:
- 搜索框输入(用户停止输入后再触发请求)。
- 窗口调整大小(用户调整完毕后再触发计算)。
- 按钮防止重复点击(短时间内多次点击,只触发最后一次)。
封装防抖函数
1 | function debounce(fn, delay = 300) { |
使用示例
1 | const input = document.querySelector("#search"); |
2. 节流(Throttle)
特点:
- 一定时间间隔内 只允许执行一次回调,即使事件连续触发。
- 适用于 控制高频触发的事件,保证在一定时间内执行一次。
适用场景:
- 滚动事件(
scroll
监听,防止频繁触发)。 - 鼠标移动事件(
mousemove
监听,减少计算频率)。 - 按钮防止重复提交(限制一定时间内只能点击一次)。
封装节流函数
1 | function throttle(fn, delay = 300) { |
使用示例
1 | window.addEventListener( |
3. 结合 leading
和 trailing
优化防抖和节流
优化防抖:支持 immediate
选项
1 | function debounce(fn, delay = 300, immediate = false) { |
用法:
1 | const handleInput = debounce(() => console.log("立即执行"), 500, true); |
优化节流:支持 leading
和 trailing
1 | function throttle(fn, delay = 300, { leading = true, trailing = true } = {}) { |
用法:
1 | const handleScroll = throttle(() => console.log("节流优化"), 1000, { |
总结
防抖(Debounce) | 节流(Throttle) | |
---|---|---|
触发方式 | 停止触发后 等待一段时间执行 | 固定间隔时间 执行 |
核心机制 | 多次触发只执行最后一次 | 间隔时间内最多执行一次 |
适用场景 | 输入框、搜索、窗口调整大小 | 滚动、鼠标移动、按钮防连点 |
优化方式 | 支持 immediate 立即执行 |
支持 leading 和 trailing |
这两种技术可以根据不同需求组合使用,例如:
- 搜索框:防抖,避免频繁请求。
- 按钮点击:节流,防止短时间多次提交。
- 滚动监听:节流,减少
scroll
事件执行次数。
你可以根据实际需求选择适合的方法 🚀!
4.递归组件
递归组件是 Vue.js 中非常强大的功能,特别适合处理嵌套数据结构的场景,比如树形结构、嵌套菜单、骨架屏等。递归组件的核心思想是组件在其模板中调用自身,从而实现嵌套渲染。
以下是递归组件的详细说明和使用方法:
1. 递归组件的基本概念
递归组件是指一个组件在其模板中调用自身。为了实现递归,组件需要满足以下条件:
- 命名:组件必须有一个
name
属性,以便在模板中调用自身。 - 终止条件:递归必须有一个终止条件,否则会导致无限递归。
2. 递归组件的实现步骤
步骤 1:定义组件
在 Vue 中,定义一个递归组件非常简单。只需在组件的模板中调用自身即可。
1 | Vue.component("recursive-component", { |
步骤 2:使用组件
在父组件中使用递归组件,并传入嵌套数据。
1 | new Vue({ |
步骤 3:渲染结果
上述代码会渲染出以下结构:
1 | <div> |
3. 递归组件的关键点
1. name
属性
递归组件必须定义 name
属性,因为 Vue 需要通过 name
来识别组件自身。
1 | Vue.component("recursive-component", { |
2. 终止条件
递归必须有一个终止条件,否则会导致无限递归。通常通过 v-if
或 v-for
来控制递归的终止。
1 | <recursive-component |
3. props
传递数据
递归组件通过 props
接收数据,并将数据传递给下一层递归。
1 | props: { |
4. 递归组件的应用场景
递归组件非常适合处理以下场景:
- 树形结构:如文件目录、组织架构等。
- 嵌套菜单:如多级导航菜单。
- 骨架屏:如动态生成嵌套的骨架屏结构。
- 评论系统:如嵌套的评论回复。
5. 完整示例:树形结构渲染
以下是一个完整的树形结构渲染示例:
数据结构
1 | const treeData = { |
递归组件
1 | Vue.component("tree-node", { |
使用组件
1 | new Vue({ |
渲染结果
1 | <div> |
6. 注意事项
性能问题:
- 递归组件的嵌套层级不宜过深,否则可能导致性能问题。
- 可以通过
v-if
或v-for
的优化来减少不必要的渲染。
终止条件:
- 必须确保递归有终止条件,否则会导致无限递归和栈溢出。
key
的使用:- 在
v-for
中必须为每个子元素设置唯一的key
,以便 Vue 高效地更新 DOM。
- 在
7. 总结
递归组件是 Vue.js 中处理嵌套数据的强大工具。通过定义 name
属性、设置终止条件和传递 props
,可以轻松实现树形结构、嵌套菜单等复杂场景的渲染。在实际开发中,递归组件的使用需要结合具体场景,注意性能和终止条件。
如果有其他问题,欢迎随时提问!
Props
在 Vue 3 的 defineProps()
中,可以对 props
进行类型校验、默认值和必填验证。
📌 1. props
属性详细解析
1 | props: { |
📌 2. Vue 3 `` 版本
在 `` 里,defineProps()
不能直接写对象校验,而是用 TypeScript 或defineProps()
传递对象。
1 | <script setup> |
📌 3. 各参数作用
参数 | 作用 |
---|---|
type |
指定 props 的类型,如 String 、Number 、Boolean 、Object 等 |
required |
是否必须传递,true = 必填 |
default |
默认值(对象/数组要用箭头函数) |
validator |
自定义校验规则,返回 true 代表合法,false 代表报错 |
📌 4. 示例
1 | <script setup> |
📌 5. type
可选值
type |
说明 |
---|---|
String |
字符串 |
Number |
数字 |
Boolean |
布尔值 |
Object |
对象 |
Array |
数组 |
Function |
函数 |
📌 6. validator
自定义校验
1 | <script setup> |
- 如果
age
小于 0 或大于 150,Vue 不会报错,但会在 控制台警告⚠️。
🚀 最佳实践
- **尽量指定
type
**,避免意外类型 - 如果是对象或数组,
default
需要用函数返回 - 使用
validator
进行额外校验 required: true
适用于必须传的props
这样可以让 props
更加健壮,避免意外数据导致错误!🎯
组件通信
在 Vue 3 中,组件通信方式主要包括以下几种:
1. Props / Emit(父子组件通信)
- 父组件 → 子组件:使用
props
传递数据。 - 子组件 → 父组件:使用
emit
触发自定义事件,向父组件发送数据。
示例:
1 | <!-- Parent.vue --> |
2. Provide / Inject(跨层级组件通信)
适用于深层嵌套的组件,可以让祖先组件提供数据,后代组件注入数据。
示例:
1 | <!-- App.vue --> |
3. Event Bus(兄弟组件通信)
Vue 3 没有内置的事件总线,但可以使用 mitt(推荐):
1 | npm install mitt |
创建事件总线 (eventBus.js
):
1 | import mitt from "mitt"; |
组件通信:
1 | <!-- Sender.vue --> |
4. Pinia(全局状态管理)
Vue 3 推荐使用 Pinia
作为状态管理工具(比 Vuex 更轻量)。
1 | npm install pinia |
创建 store
:
1 | // stores/counter.js |
在组件中使用:
1 | <!-- Counter.vue --> |
5. $attrs 和 v-bind(高阶组件通信)
适用于透传 props 到子组件。
1 | <!-- Parent.vue --> |
6. Ref / Reactive 共享状态
多个组件共享响应式数据:
1 | // store.js |
结论
- 父子通信 →
props
/emit
- 兄弟通信 →
mitt
/Pinia
- 跨层级 →
provide
/inject
- 全局状态 →
Pinia
/reactive
- 透传属性 →
$attrs
根据实际场景选择最合适的方式!🚀
Date
你提供的是ISO 8601 格式的时间字符串("2024-03-21T13:00:00"
),在 JavaScript 中,可以使用 Date
对象来解析、格式化或转换它。
1. 解析 ISO 时间字符串
JavaScript Date
构造函数可以直接解析这种格式:
1 | const dateStr = "2024-03-21T13:00:00"; |
📌 new Date(dateStr)
会自动解析 ISO 格式,并转换为本地时间。
2. 提取日期、时间信息
你可以使用 getFullYear()
、getMonth()
、getDate()
等方法:
1 | console.log(date.getFullYear()); // 2024 |
3. 格式化为 YYYY-MM-DD HH:mm:ss
1 | function formatDate(date) { |
📌 padStart(2, "0")
用于补零,确保格式一致。
4. 转换为 UTC 时间
如果你想以 UTC 时间显示,而不是本地时间:
1 | console.log(date.toISOString()); |
📌 toISOString()
返回标准 UTC
格式(末尾带 Z
表示 UTC
时区)。
如果要手动获取 UTC 时间:
1 | console.log(date.getUTCFullYear()); // 2024 |
5. 转换为时间戳(毫秒数)
如果需要将日期转换为时间戳(毫秒):
1 | console.log(date.getTime()); |
📌 getTime()
返回自 1970-01-01 00:00:00 UTC 以来的毫秒数。
如果需要秒级时间戳:
1 | console.log(Math.floor(date.getTime() / 1000)); |
6. 使用 day.js
或 moment.js
进行处理
如果需要更强大的时间处理库,可以使用 day.js
(轻量)或 moment.js
(较重)。
📌 使用 day.js
1 | import dayjs from "dayjs"; |
📌 使用 moment.js
1 | import moment from "moment"; |
📌 总结
操作 | 方法 |
---|---|
解析 ISO 8601 日期 | new Date(dateStr) |
获取年份 | date.getFullYear() |
获取月份 | date.getMonth() + 1 (注意 +1 ) |
获取日期 | date.getDate() |
获取时间(小时、分钟、秒) | date.getHours() , date.getMinutes() , date.getSeconds() |
格式化 YYYY-MM-DD HH:mm:ss |
自定义 padStart() 方法 / day.js / moment.js |
转换 UTC 时间 | date.toISOString() |
获取时间戳(毫秒) | date.getTime() |
获取时间戳(秒) | Math.floor(date.getTime() / 1000) |
如果只是简单处理,**Date
内置方法就够用**,如果需要更强大的时间操作(时区转换、相对时间等),推荐 day.js
。
sort
是的,**sort()
方法会修改原数组,它是原地排序(in-place sorting**),不会创建新数组。
1. sort()
修改原数组
1 | const arr = [3, 1, 4, 1, 5, 9]; |
⚠️ 注意:sort()
不会返回新数组,而是修改 arr
本身!
2. 如何避免 sort()
修改原数组?
如果想要排序但不修改原数组,可以先创建副本:
1 | const arr = [3, 1, 4, 1, 5, 9]; |
📌 方法解析
[...]
扩展运算符 复制数组,避免sort()
修改原数组。sort()
只作用于副本sortedArr
,不会影响arr
。
3. 使用 toSorted()
(ES2023+)
在 ES2023+ 中,JavaScript 提供了 toSorted()
方法,它不会修改原数组,而是返回排序后的新数组:
1 | const arr = [3, 1, 4, 1, 5, 9]; |
🚀 toSorted()
的优点
- 不会修改原数组
- 代码更简洁(不需要
[...]
复制数组)
但 toSorted()
目前只在最新的浏览器支持,如果你的项目需要兼容旧版浏览器,仍然需要用 sort()
+ [...]
。
📌 总结
方法 | 是否修改原数组 | 适用场景 |
---|---|---|
arr.sort() |
✅ 修改原数组 | 直接对原数组排序 |
[...arr].sort() |
❌ 不修改原数组 | 需要排序但保留原数组 |
arr.toSorted() (ES2023+) |
❌ 不修改原数组 | 更简洁的方法,但仅支持现代浏览器 |
如果你需要排序但不影响原数组,建议用 [...arr].sort()
或 toSorted()
(如果环境支持)。
regex
正则表达式(Regular Expression)在 JavaScript 中是一个强大的工具,用于字符串的匹配、搜索、替换和验证。以下是 JavaScript 中正则表达式的详细用法,包括捕获组(Capturing Groups)的应用。
一、正则表达式的基本语法
在 JavaScript 中,正则表达式可以通过两种方式创建:
- 字面量形式:
/pattern/modifiers
- 构造函数形式:
new RegExp("pattern", "modifiers")
示例:
1 | // 字面量形式 |
二、正则表达式的常用方法
1. test()
:测试字符串是否匹配
返回布尔值,表示是否找到匹配。
1 | const str = "Hello World"; |
2. exec()
:执行搜索,返回匹配结果
返回一个数组(包含匹配信息)或 null
。
1 | const str = "2023-10-25"; |
3. match()
:字符串方法,返回匹配结果
类似于 exec()
,但属于字符串的方法。
1 | const str = "2023-10-25"; |
4. replace()
:替换匹配内容
替换字符串中匹配的部分。
1 | const str = "2023-10-25"; |
5. search()
:返回匹配的起始位置
返回匹配项的索引,未找到返回 -1
。
1 | const str = "Hello World"; |
三、捕获组(Capturing Groups)
捕获组是正则表达式中用括号 ()
包裹的部分,用于从匹配的字符串中提取特定部分。
1. 基本捕获组
通过索引访问捕获组,索引从 1
开始。
1 | const str = "John Doe, 30"; |
2. 命名捕获组(ES2018+)
通过 ?<name>
语法为捕获组命名,通过 groups
属性访问。
1 | const str = "2023-10-25"; |
3. 非捕获组
使用 ?:
表示非捕获组,匹配但不捕获。
1 | const str = "foo123bar"; |
四、捕获组的实际应用
1. 提取日期中的年月日
1 | const str = "2023-10-25"; |
2. 替换日期格式
1 | const str = "2023-10-25"; |
3. 解析 URL 参数
1 | const url = "https://example.com?name=John&age=30"; |
4. 动态替换模板字符串
1 | const template = "Hello, {name}! You are {age} years old."; |
五、常见问题与技巧
1. 贪婪匹配 vs 非贪婪匹配
贪婪匹配:默认模式,尽可能匹配更长的字符串。
1
2
3const str = "foo123bar";
const regex = /foo(\d+)bar/;
console.log(regex.exec(str)[1]); // "123"非贪婪匹配:使用
?
,尽可能匹配更短的字符串。1
2
3const str = "foo123bar";
const regex = /foo(\d+?)bar/;
console.log(regex.exec(str)[1]); // "1"
2. 捕获组的性能
避免在复杂的正则表达式中使用过多捕获组,可能会影响性能。
六、总结
- 基础用法:
test()
、exec()
、match()
、replace()
。 - 捕获组:通过
()
提取匹配内容,支持命名捕获组(?<name>)
。 - 应用场景:日期解析、URL 参数提取、模板替换等。
- 注意事项:贪婪匹配与非贪婪匹配的区别,性能优化。
通过灵活使用正则表达式和捕获组,可以高效处理复杂的字符串操作。
Ajax
使用原生的 Ajax 可以通过 JavaScript 来实现,以下是一个简单的示例,展示了如何使用原生 JavaScript 发起一个 Ajax 请求:
1 | // 创建一个 XMLHttpRequest 对象 |
在这个例子中:
- 创建了一个
XMLHttpRequest
对象,它是处理 Ajax 请求的核心。 - 使用
xhr.open()
方法配置了请求的方法(GET、POST 等)、URL 和是否使用异步(true 表示异步)。 - 通过监听
xhr.onreadystatechange
事件,可以处理请求状态的变化。当readyState
变为XMLHttpRequest.DONE
时,表示请求完成。 - 在请求完成时,可以检查
xhr.status
来确定请求是否成功(状态码 200 表示成功),然后处理返回的数据(xhr.responseText
)或者处理错误情况。
这是一个基本的 Ajax 请求示例,可以根据具体需求和情境进行进一步的定制和扩展。
fs
Node.js 的 fs
模块是处理文件系统操作的核心模块,它提供了许多方法来进行文件的读取、写入、删除、重命名、监控等操作。以下是 fs
模块的详细总结,涵盖了常用方法和适用场景。
1. 文件读取
异步读取文件:fs.readFile(path, encoding, callback)
异步地读取文件内容,callback
回调函数会在文件读取完毕后执行。
语法
:
1
fs.readFile(path, encoding, callback);
参数
:
path
: 要读取的文件路径。encoding
: 编码格式(如'utf8'
)。如果不指定,返回Buffer
类型的数据。callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
: 回调函数,接收两个参数:
- `err`: 如果有错误,包含错误信息。
- `data`: 读取到的文件内容。
- 使用示例
:
```js
const fs = require("fs");
fs.readFile("example.txt", "utf8", (err, data) => {
if (err) {
console.log("Error:", err);
} else {
console.log("File content:", data);
}
});
同步读取文件:fs.readFileSync(path, encoding)
同步地读取文件内容,直到文件读取完成,程序才会继续执行。
语法:
1
const data = fs.readFileSync(path, encoding);
参数:
path
: 要读取的文件路径。encoding
: 编码格式(如'utf8'
),如果不指定,返回Buffer
类型的数据。
使用示例:
1
2
3
4
5
6
7
8const fs = require("fs");
try {
const data = fs.readFileSync("example.txt", "utf8");
console.log("File content:", data);
} catch (err) {
console.log("Error:", err);
}
返回类型:
- 如果指定编码(如
'utf8'
),返回的将是一个字符串。 - 如果没有指定编码,则返回的是
Buffer
对象。
2. 文件写入
异步写入文件:fs.writeFile(path, data, encoding, callback)
异步地将数据写入文件。如果文件已存在,则会覆盖原文件。
语法:
1
fs.writeFile(path, data, encoding, callback);
参数:
path
: 要写入的文件路径。data
: 要写入的数据,可以是字符串或Buffer
。encoding
: 编码格式(可选,默认为'utf8'
)。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.writeFile("example.txt", "Hello, Node.js!", "utf8", (err) => {
if (err) {
console.log("Error writing file:", err);
} else {
console.log("File written successfully");
}
});
同步写入文件:fs.writeFileSync(path, data, encoding)
同步地将数据写入文件,直到文件写入完成,程序才会继续执行。
语法:
1
fs.writeFileSync(path, data, encoding);
参数:
path
: 要写入的文件路径。data
: 要写入的数据。encoding
: 编码格式(可选,默认为'utf8'
)。
使用示例:
1
2
3
4
5
6
7
8const fs = require("fs");
try {
fs.writeFileSync("example.txt", "Hello, Node.js!", "utf8");
console.log("File written successfully");
} catch (err) {
console.log("Error writing file:", err);
}
3. 追加文件内容
异步追加:fs.appendFile(path, data, encoding, callback)
将数据追加到文件末尾。如果文件不存在,则会创建新文件。
语法:
1
fs.appendFile(path, data, encoding, callback);
参数:
path
: 要写入的文件路径。data
: 要追加的数据。encoding
: 编码格式(可选,默认为'utf8'
)。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.appendFile("example.txt", "\nHello again!", "utf8", (err) => {
if (err) {
console.log("Error appending to file:", err);
} else {
console.log("Content appended successfully");
}
});
同步追加:fs.appendFileSync(path, data, encoding)
同步地将数据追加到文件末尾,直到文件写入完成,程序才会继续执行。
语法:
1
fs.appendFileSync(path, data, encoding);
参数:
path
: 要写入的文件路径。data
: 要追加的数据。encoding
: 编码格式(可选,默认为'utf8'
)。
使用示例:
1
2
3
4
5
6
7
8const fs = require("fs");
try {
fs.appendFileSync("example.txt", "\nHello again!", "utf8");
console.log("Content appended successfully");
} catch (err) {
console.log("Error appending to file:", err);
}
4. 文件和目录检查
检查文件是否存在:fs.exists(path, callback)
检查指定路径的文件或目录是否存在,返回一个布尔值。
语法:
1
fs.exists(path, callback);
参数:
path
: 要检查的路径。callback
: 回调函数,接收一个参数exists
,布尔值,表示文件或目录是否存在。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.exists("example.txt", (exists) => {
if (exists) {
console.log("File exists");
} else {
console.log("File does not exist");
}
});
检查文件状态:fs.stat(path, callback)
获取文件或目录的状态信息,包括文件大小、修改时间等。
语法:
1
fs.stat(path, callback);
参数:
path
: 要检查的文件路径。callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
: 回调函数,接收两个参数:
- `err`: 如果有错误,包含错误信息。
- `stats`: 包含文件信息的 `fs.Stats` 对象。
- **使用示例**:
```js
const fs = require("fs");
fs.stat("example.txt", (err, stats) => {
if (err) {
console.log("Error checking file:", err);
} else {
console.log("File stats:", stats);
console.log("Is directory?", stats.isDirectory());
console.log("Is file?", stats.isFile());
}
});
5. 删除文件
删除文件:fs.unlink(path, callback)
删除指定路径的文件。
语法:
1
fs.unlink(path, callback);
参数:
path
: 要删除的文件路径。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.unlink("example.txt", (err) => {
if (err) {
console.log("Error deleting file:", err);
} else {
console.log("File deleted successfully");
}
});
6. 创建和删除目录
创建目录:fs.mkdir(path, callback)
创建一个新目录。
语法:
1
fs.mkdir(path, callback);
参数:
path
: 要创建的目录路径。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.mkdir("newdir", (err) => {
if (err) {
console.log("Error creating directory:", err);
} else {
console.log("Directory created successfully");
}
});
删除目录:fs.rmdir(path, callback)
删除一个空目录。
语法:
1
fs.rmdir(path, callback);
参数:
path
: 要删除的目录路径。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.rmdir("newdir", (err) => {
if (err) {
console.log("Error removing directory:", err);
} else {
console.log("Directory removed successfully");
}
});
总结
- 异步与同步方法:
fs
模块提供了异步和同步的方法,通常推荐使用异步方法(避免阻塞事件循环),同步方法用于简单场景或者脚本执行。 - 文件和目录操作:文件的读取、写入、删除、检查、目录的创建与删除等是最常用的操作,基于这些基本功能可以构建文件管理系统、日志记录等功能。
- 错误处理:所有的异步方法都提供了回调函数来处理错误,确保在文件操作中捕获异常并做出适当反应。
fs
Node.js 的 fs
模块是处理文件系统操作的核心模块,它提供了许多方法来进行文件的读取、写入、删除、重命名、监控等操作。以下是 fs
模块的详细总结,涵盖了常用方法和适用场景。
1. 文件读取
异步读取文件:fs.readFile(path, encoding, callback)
异步地读取文件内容,callback
回调函数会在文件读取完毕后执行。
语法
:
1
fs.readFile(path, encoding, callback);
参数
:
path
: 要读取的文件路径。encoding
: 编码格式(如'utf8'
)。如果不指定,返回Buffer
类型的数据。callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
: 回调函数,接收两个参数:
- `err`: 如果有错误,包含错误信息。
- `data`: 读取到的文件内容。
- 使用示例
:
```js
const fs = require("fs");
fs.readFile("example.txt", "utf8", (err, data) => {
if (err) {
console.log("Error:", err);
} else {
console.log("File content:", data);
}
});
同步读取文件:fs.readFileSync(path, encoding)
同步地读取文件内容,直到文件读取完成,程序才会继续执行。
语法:
1
const data = fs.readFileSync(path, encoding);
参数:
path
: 要读取的文件路径。encoding
: 编码格式(如'utf8'
),如果不指定,返回Buffer
类型的数据。
使用示例:
1
2
3
4
5
6
7
8const fs = require("fs");
try {
const data = fs.readFileSync("example.txt", "utf8");
console.log("File content:", data);
} catch (err) {
console.log("Error:", err);
}
返回类型:
- 如果指定编码(如
'utf8'
),返回的将是一个字符串。 - 如果没有指定编码,则返回的是
Buffer
对象。
2. 文件写入
异步写入文件:fs.writeFile(path, data, encoding, callback)
异步地将数据写入文件。如果文件已存在,则会覆盖原文件。
语法:
1
fs.writeFile(path, data, encoding, callback);
参数:
path
: 要写入的文件路径。data
: 要写入的数据,可以是字符串或Buffer
。encoding
: 编码格式(可选,默认为'utf8'
)。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.writeFile("example.txt", "Hello, Node.js!", "utf8", (err) => {
if (err) {
console.log("Error writing file:", err);
} else {
console.log("File written successfully");
}
});
同步写入文件:fs.writeFileSync(path, data, encoding)
同步地将数据写入文件,直到文件写入完成,程序才会继续执行。
语法:
1
fs.writeFileSync(path, data, encoding);
参数:
path
: 要写入的文件路径。data
: 要写入的数据。encoding
: 编码格式(可选,默认为'utf8'
)。
使用示例:
1
2
3
4
5
6
7
8const fs = require("fs");
try {
fs.writeFileSync("example.txt", "Hello, Node.js!", "utf8");
console.log("File written successfully");
} catch (err) {
console.log("Error writing file:", err);
}
3. 追加文件内容
异步追加:fs.appendFile(path, data, encoding, callback)
将数据追加到文件末尾。如果文件不存在,则会创建新文件。
语法:
1
fs.appendFile(path, data, encoding, callback);
参数:
path
: 要写入的文件路径。data
: 要追加的数据。encoding
: 编码格式(可选,默认为'utf8'
)。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.appendFile("example.txt", "\nHello again!", "utf8", (err) => {
if (err) {
console.log("Error appending to file:", err);
} else {
console.log("Content appended successfully");
}
});
同步追加:fs.appendFileSync(path, data, encoding)
同步地将数据追加到文件末尾,直到文件写入完成,程序才会继续执行。
语法:
1
fs.appendFileSync(path, data, encoding);
参数:
path
: 要写入的文件路径。data
: 要追加的数据。encoding
: 编码格式(可选,默认为'utf8'
)。
使用示例:
1
2
3
4
5
6
7
8const fs = require("fs");
try {
fs.appendFileSync("example.txt", "\nHello again!", "utf8");
console.log("Content appended successfully");
} catch (err) {
console.log("Error appending to file:", err);
}
4. 文件和目录检查
检查文件是否存在:fs.exists(path, callback)
检查指定路径的文件或目录是否存在,返回一个布尔值。
语法:
1
fs.exists(path, callback);
参数:
path
: 要检查的路径。callback
: 回调函数,接收一个参数exists
,布尔值,表示文件或目录是否存在。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.exists("example.txt", (exists) => {
if (exists) {
console.log("File exists");
} else {
console.log("File does not exist");
}
});
检查文件状态:fs.stat(path, callback)
获取文件或目录的状态信息,包括文件大小、修改时间等。
语法:
1
fs.stat(path, callback);
参数:
path
: 要检查的文件路径。callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
: 回调函数,接收两个参数:
- `err`: 如果有错误,包含错误信息。
- `stats`: 包含文件信息的 `fs.Stats` 对象。
- **使用示例**:
```js
const fs = require("fs");
fs.stat("example.txt", (err, stats) => {
if (err) {
console.log("Error checking file:", err);
} else {
console.log("File stats:", stats);
console.log("Is directory?", stats.isDirectory());
console.log("Is file?", stats.isFile());
}
});
5. 删除文件
删除文件:fs.unlink(path, callback)
删除指定路径的文件。
语法:
1
fs.unlink(path, callback);
参数:
path
: 要删除的文件路径。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.unlink("example.txt", (err) => {
if (err) {
console.log("Error deleting file:", err);
} else {
console.log("File deleted successfully");
}
});
6. 创建和删除目录
创建目录:fs.mkdir(path, callback)
创建一个新目录。
语法:
1
fs.mkdir(path, callback);
参数:
path
: 要创建的目录路径。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.mkdir("newdir", (err) => {
if (err) {
console.log("Error creating directory:", err);
} else {
console.log("Directory created successfully");
}
});
删除目录:fs.rmdir(path, callback)
删除一个空目录。
语法:
1
fs.rmdir(path, callback);
参数:
path
: 要删除的目录路径。callback
: 回调函数,接收一个参数err
,表示错误信息。
使用示例:
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.rmdir("newdir", (err) => {
if (err) {
console.log("Error removing directory:", err);
} else {
console.log("Directory removed successfully");
}
});
总结
- 异步与同步方法:
fs
模块提供了异步和同步的方法,通常推荐使用异步方法(避免阻塞事件循环),同步方法用于简单场景或者脚本执行。 - 文件和目录操作:文件的读取、写入、删除、检查、目录的创建与删除等是最常用的操作,基于这些基本功能可以构建文件管理系统、日志记录等功能。
- 错误处理:所有的异步方法都提供了回调函数来处理错误,确保在文件操作中捕获异常并做出适当反应。
Path
path
模块是 Node.js 的核心模块之一,主要用于处理和操作文件路径。它提供了多种方法来处理路径字符串,以保证在不同操作系统(如 Windows、Linux、macOS)上的兼容性。
1. 引入 path
模块
path
是 Node.js 内置模块,无需安装,可以直接 require
引入:
1 | const path = require("path"); |
2. path
常用方法
(1) path.join(...paths)
将多个路径片段拼接成一个完整的路径,并自动处理路径分隔符(/
或 \
)。
1 | const filePath = path.join("user", "documents", "file.txt"); |
✅ 适用于:
- 构建文件路径,适用于不同操作系统,自动处理路径分隔符。
(2) path.resolve(...paths)
返回绝对路径,类似 cd
命令解析路径的方式:
1 | console.log(path.resolve("user", "docs", "file.txt")); |
✅ 适用于:
- 生成绝对路径,避免路径不明确导致的错误。
(3) path.basename(path, ext?)
获取路径中的 文件名,可选地去掉扩展名。
1 | const file = "/user/documents/file.txt"; |
✅ 适用于:
- 获取文件名,比如
file.txt
而不是完整路径。
(4) path.dirname(path)
获取路径中的 目录名(不包含文件名)。
1 | const file = "/user/documents/file.txt"; |
✅ 适用于:
- 获取文件所在的目录路径。
(5) path.extname(path)
获取文件的 扩展名,包括 .
号。
1 | console.log(path.extname("file.txt")); // '.txt' |
✅ 适用于:
- 判断文件类型,如检查文件是否是
.jpg
、.json
等。
(6) path.parse(path)
解析路径,返回一个对象,包含路径的各个部分:
1 | const parsed = path.parse("/user/docs/file.txt"); |
✅ 适用于:
- 提取路径信息(目录、文件名、扩展名等)。
(7) path.format(pathObject)
与 path.parse()
相反,它将一个路径对象转换回路径字符串:
1 | const formatted = path.format({ |
✅ 适用于:
- 动态拼接路径。
(8) path.isAbsolute(path)
判断路径是否为 绝对路径:
1 | console.log(path.isAbsolute("/user/docs/file.txt")); // true |
✅ 适用于:
- 判断路径是否需要转换成绝对路径。
(9) path.relative(from, to)
计算 from
到 to
之间的相对路径:
1 | console.log(path.relative("/user/docs", "/user/docs/file.txt")); |
✅ 适用于:
- 构建相对路径(如从
docs
目录到images
目录)。
(10) path.normalize(path)
规范化路径,处理 ..
和 .
:
1 | console.log(path.normalize("/user/docs/../images/photo.jpg")); |
✅ 适用于:
- 确保路径正确,避免
..
和.
影响路径解析。
(11) path.sep
返回当前系统的路径分隔符:
1 | console.log(path.sep); |
✅ 适用于:
- 跨平台路径处理。
(12) path.delimiter
返回环境变量分隔符:
1 | console.log(process.env.PATH.split(path.delimiter)); |
✅ 适用于:
- 解析
PATH
变量(Windows 使用;
,Linux/macOS 使用:
)。
3. path
典型使用场景
✅ 读取当前脚本所在目录
1 | const scriptDir = __dirname; |
✅ 获取绝对路径
1 | const absolutePath = path.resolve("data", "file.txt"); |
✅ 读取文件扩展名
1 | const ext = path.extname("data/file.txt"); |
✅ 动态拼接路径
1 | const fullPath = path.join(__dirname, "data", "file.txt"); |
✅ 解析路径
1 | const fileInfo = path.parse("/user/docs/file.txt"); |
✅ 计算两个路径之间的相对路径
1 | const relativePath = path.relative("/user/docs", "/user/images/photo.jpg"); |
4. path
vs. fs
功能 | path 模块 |
fs 模块 |
---|---|---|
处理路径字符串 | ✅ | ❌ |
读取/写入文件 | ❌ | ✅ |
获取文件信息 | ❌ | ✅ |
解析路径 | ✅ | ❌ |
操作文件 | ❌ | ✅ |
5. 总结
path
主要用于路径解析、拼接、转换,不会对文件系统进行读写操作。fs
主要用于文件的读写、创建、删除,与path
结合使用时非常强大。path.join()
和path.resolve()
是最常用的方法,建议使用path.join()
处理路径拼接,确保跨平台兼容性。
如果你在 Node.js 项目中需要处理文件路径,path
模块是必不可少的工具! 🚀
set
Set
是 JavaScript 中的一种 集合(Collection)数据结构,它允许你存储 唯一值,即不允许重复元素。它的主要作用是去重、集合运算(交集、并集、差集)等。
1. 创建 Set
(1) 空 Set
1 | const mySet = new Set(); |
创建一个空的 Set
,可以用 .add()
方法添加元素。
(2) 从数组创建 Set
(自动去重)
1 | const mySet = new Set([1, 2, 3, 3, 4, 5, 5]); |
Set
会自动去掉重复的 3
和 5
。
2. Set
的基本方法
(1) .add(value)
- 添加元素
1 | const mySet = new Set(); |
特点:
Set
只能存储 唯一值,重复的值会被自动忽略。
(2) .delete(value)
- 删除元素
1 | mySet.delete(2); |
返回值:如果删除成功,返回
true
,否则返回false
。
(3) .has(value)
- 检查是否存在
1 | console.log(mySet.has(1)); // true |
适用于: 快速查找某个值是否存在,比数组
.includes()
更高效。
(4) .clear()
- 清空 Set
1 | mySet.clear(); |
作用:清空所有元素。
(5) .size
- 获取元素个数
1 | console.log(mySet.size); // 2 |
**区别于数组
length
**,Set
使用.size
而不是.length
。
3. 遍历 Set
Set
是可迭代对象,可以使用 forEach()
、for...of
、解构等方式遍历。
(1) forEach()
遍历
1 | const mySet = new Set(["apple", "banana", "cherry"]); |
(2) for...of
遍历
1 | for (let item of mySet) { |
(3) 使用 Array.from()
转数组
1 | const myArray = Array.from(mySet); |
4. Set
与数组的转换
(1) Set
→ 数组
1 | const mySet = new Set([1, 2, 3]); |
(2) 数组去重
1 | const numbers = [1, 2, 2, 3, 4, 4, 5]; |
(3) 数组 → Set
1 | const uniqueSet = new Set(numbers); |
5. Set
进行集合运算
(1) 并集(Set
合并)
1 | const setA = new Set([1, 2, 3]); |
(2) 交集(获取共有的值)
1 | const intersectionSet = new Set([...setA].filter((x) => setB.has(x))); |
(3) 差集(获取 A 有但 B 没有的值)
1 | const differenceSet = new Set([...setA].filter((x) => !setB.has(x))); |
6. Set
适用场景
需求 | 推荐用 Set 还是数组? |
理由 |
---|---|---|
数组去重 | Set |
Set 自动去重,代码简洁高效 |
快速查找值 | Set |
.has(value) 速度比 .includes() 快 |
存储唯一值集合 | Set |
例如存储用户 ID、唯一标识符等 |
需要索引查找 | 数组 | Set 没有索引,只能用迭代遍历 |
7. 总结
✅ Set
的主要特点:
- 不会存储重复值(自动去重)。
- 可以快速判断某个值是否存在(比
Array.includes()
更快)。 - 支持遍历(
forEach
、for...of
)。 - 适用于数组去重、集合运算(并集、交集、差集)。
- 不能通过索引访问元素(不像数组
arr[0]
这样操作)。
✅ 常见用法
1 | const uniqueValues = [...new Set([1, 2, 2, 3, 4, 4, 5])]; // 数组去重 |
💡 适用于数据集合管理,尤其是去重、查找和集合运算! 🚀
Map
Map
在 JavaScript 中的使用方法详解
Map
是 JavaScript 的 键值对(key-value)存储数据结构,类似于对象 {}
,但提供了更多的特性,如 键可以是任意类型、元素有序、获取性能更好 等。
1. 创建 Map
(1) 创建空 Map
1 | const myMap = new Map(); |
创建一个空的
Map
,可以使用.set()
方法添加键值对。
(2) 直接初始化 Map
1 | const myMap = new Map([ |
✅ 适用于:快速创建 Map
并初始化键值对。
2. Map
的基本方法
(1) .set(key, value)
- 添加或更新键值对
1 | const myMap = new Map(); |
✅ 适用于:存储数据并保持唯一性。
(2) .get(key)
- 获取键对应的值
1 | console.log(myMap.get("name")); // 'Alice' |
✅ **比对象 {}
的 obj[key]
更安全,不存在的键不会报错,只会返回 undefined
。
(3) .has(key)
- 检查 Map
是否包含某个键
1 | console.log(myMap.has("name")); // true |
✅ **比对象 obj.hasOwnProperty(key)
更直观。
(4) .delete(key)
- 删除键值对
1 | myMap.delete("age"); |
✅ 删除特定的键值对,true
表示删除成功,false
表示不存在该键。
(5) .clear()
- 清空 Map
1 | myMap.clear(); |
✅ 适用于: 彻底清空 Map
。
(6) .size
- 获取 Map
的大小
1 | console.log(myMap.size); // 2 |
✅ 适用于: 获取 Map
的键值对数量(比 Object.keys(obj).length
更高效)。
3. 遍历 Map
(1) forEach()
遍历
1 | myMap.forEach((value, key) => { |
✅ 适用于: 方便对 Map
进行迭代操作。
(2) for...of
遍历
1 | for (let [key, value] of myMap) { |
✅ **比 forEach()
语法更简洁,可用于解构赋值。
(3) keys()
/ values()
/ entries()
1 | console.log([...myMap.keys()]); // ['name', 'age'] |
✅ 适用于: 快速获取 Map
的键、值或键值对。
4. Map
vs Object
特性 | Map |
Object |
---|---|---|
键类型 | 任意类型(对象、数组、函数等) | 只能是字符串或 Symbol |
键的顺序 | 按插入顺序存储 | 无序(ES6 之后 Object 也是有序的) |
获取大小 | size |
Object.keys(obj).length |
迭代方式 | forEach() 、for...of |
for...in (需 hasOwnProperty 过滤) |
性能 | 适用于大量数据存取 | 适用于小型数据结构 |
5. Map
实际应用场景
(1) 统计字符串中字符出现次数
1 | function countChars(str) { |
✅ 比对象 {}
更直观,且不受原型链干扰。
(2) 记住对象的状态
1 | const user1 = { name: "Alice" }; |
✅ 比对象 {}
更适合存储对象作为键的数据。
(3) Map
实现 LRU 缓存
1 | class LRUCache { |
✅ 比数组或对象更适用于 LRU(最近最少使用)缓存。
6. Map
与 Set
对比
特性 | Map |
Set |
---|---|---|
存储结构 | 键值对(key-value) | 值的集合(无重复) |
访问方式 | .get(key) |
.has(value) |
迭代方式 | .keys() .values() .entries() |
.values() |
适用场景 | 关联数据(缓存、映射、计数) | 唯一值集合(去重、集合运算) |
7. 总结
✅ Map
适用于存储:
- 唯一键值对(不重复)
- 键可以是任何类型(对象、数组、函数)
- 需要有序存储数据
- 需要高效读取和删除数据
✅ 常见用法
1 | const map = new Map(); |
💡 Map
是对象 {}
的增强版,适用于需要高效存储键值对的场景!🚀
媒体查询
CSS 媒体查询详细使用方法总结
CSS 媒体查询(Media Query)用于根据设备的特性(如屏幕宽度、高度、分辨率、方向等)应用不同的样式,实现响应式设计,使网页适配不同的设备,如手机、平板、桌面端等。
1. 媒体查询基本语法
1 | @media [media-type] and (media-feature) { |
- media-type(媒体类型,可选):
all
(默认):适用于所有设备screen
:屏幕设备(电脑、手机、平板等)print
:打印设备speech
:语音阅读器
- media-feature(媒体特性):
width
/height
:指定视口的宽度/高度min-width
/max-width
:最小/最大宽度(用于响应式设计)orientation
:设备方向(portrait
竖屏 /landscape
横屏)aspect-ratio
:屏幕宽高比resolution
:屏幕分辨率(min-resolution: 2dppx
适用于 Retina 屏)hover
:是否支持悬停(hover: hover
适用于鼠标设备)pointer
:指针精度(pointer: coarse
适用于触摸屏)
2. 常见媒体查询使用示例
(1)适配不同屏幕宽度
1 | /* 小屏幕(手机端) */ |
(2)适配设备方向
1 | /* 竖屏(portrait)模式 */ |
(3)高分辨率(Retina 屏幕)适配
1 | /* 适用于高分辨率屏幕,如 Retina 设备 */ |
(4)判断是否支持悬停(鼠标)
1 | /* 仅当设备支持鼠标悬停时生效 */ |
(5)组合多个条件
1 | /* 屏幕宽度在 600px 到 1200px 之间,且为横屏 */ |
3. 响应式设计策略
移动优先(Mobile First)
- 先为小屏幕(手机)编写 CSS 样式,再使用
min-width
媒体查询为更大屏幕(平板、电脑)添加适配规则。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15body {
font-size: 14px; /* 默认小屏幕 */
}
@media screen and (min-width: 768px) {
body {
font-size: 16px;
}
}
@media screen and (min-width: 1024px) {
body {
font-size: 18px;
}
}- 先为小屏幕(手机)编写 CSS 样式,再使用
桌面优先(Desktop First)
- 先为大屏幕(桌面端)编写 CSS 样式,再使用
max-width
媒体查询调整小屏幕(平板、手机)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15body {
font-size: 18px; /* 默认大屏幕 */
}
@media screen and (max-width: 1024px) {
body {
font-size: 16px;
}
}
@media screen and (max-width: 768px) {
body {
font-size: 14px;
}
}- 先为大屏幕(桌面端)编写 CSS 样式,再使用
4. 媒体查询的优化建议
✅ 避免过多的断点:只针对关键的屏幕尺寸设置断点,例如:
- 移动端(≤ 600px)
- 平板端(601px - 1024px)
- 桌面端(≥ 1025px)
✅ 优先使用 em
或 rem
作为单位:让页面更具伸缩性,不依赖固定像素值。
✅ 使用 flexbox
或 grid
结合媒体查询:提高布局适配能力。
✅ **避免 !important
**:会影响后续样式的覆盖。
✅ 测试不同设备和浏览器:使用 Chrome DevTools(F12 → 设备模式)或在线工具(如 Responsive Design Checker)检查适配情况。
5. 结论
- CSS 媒体查询是一种强大的技术,可以根据不同设备特性动态调整样式,实现响应式设计。
- 基本语法:
@media media-type and (media-feature) { /* 样式 */ }
- 常用特性:
width
、min-width
、max-width
、orientation
、resolution
、hover
- 推荐策略:采用 移动优先(Mobile First)进行开发,更有利于适配不同设备。
- 优化建议:合理选择断点、使用
em/rem
、结合flexbox/grid
,提升适配能力。
这样,你就可以轻松掌握 CSS 媒体查询并灵活运用于项目中啦!🚀 如果你有更具体的需求,比如如何与 Vue.js 或 Tailwind CSS 结合使用,也可以告诉我哦~ 😃
axios
axios
的配置对象可以用于 axios.get
、axios.post
或 axios.create
,下面是详细的写法:
1. 基本的 axios
配置对象
1 | const axios = require("axios"); |
2. axios.create
配置全局实例
如果你要创建一个 默认配置的 axios
实例,可以使用 axios.create
:
1 | const apiClient = axios.create({ |
3. 拦截器(请求和响应)
你可以用 interceptors
在请求或响应前后进行处理:
1 | const api = axios.create({ |
4. 发送 FormData
如果要发送 multipart/form-data
(比如文件上传),可以这样:
1 | const formData = new FormData(); |
5. 取消请求
可以用 AbortController
或 CancelToken
取消请求:
1 | const controller = new AbortController(); |
总结
axios(config)
适用于单个请求。axios.create(config)
适用于全局默认配置。interceptors
适用于 拦截请求或响应。FormData
适用于 上传文件。AbortController
适用于 取消请求。
这样你可以根据不同需求灵活使用 axios
。 🚀
fetch
fetch
是现代 JavaScript 用于进行 HTTP 请求的 API,相比 XMLHttpRequest
更加简洁、灵活,并且基于 Promise
进行异步操作。下面详细介绍 fetch
的用法,包括基础用法、请求参数、错误处理以及 async/await
方式等。
1. 基本用法
1 | fetch("https://jsonplaceholder.typicode.com/posts/1") |
解析
fetch(url)
返回一个Promise
,解析后返回Response
对象。response.json()
解析 JSON 数据(也是Promise
)。.then(data => ...)
处理解析后的数据。.catch(error => ...)
捕获请求失败(如网络错误)。
2. 使用 async/await
1 | async function getData() { |
解析
await fetch(url)
获取Response
对象。- 检查
response.ok
(状态码是否 200-299)。 await response.json()
解析 JSON 数据。try-catch
捕获异常(如网络错误、JSON 解析错误)。
3. GET 请求(带参数)
1 | let params = new URLSearchParams({ userId: 1 }); |
解析
URLSearchParams
用于构造查询字符串,如?userId=1
。fetch(url + '?' + params)
发送带参数的GET
请求。
4. POST 请求(提交 JSON 数据)
1 | fetch("https://jsonplaceholder.typicode.com/posts", { |
解析
method: "POST"
指定请求方法。headers
设定Content-Type
为application/json
。body: JSON.stringify({...})
发送 JSON 数据。
5. PUT 请求(更新数据)
1 | fetch("https://jsonplaceholder.typicode.com/posts/1", { |
解析
PUT
用于更新资源(通常需要提供id
)。- 需要完整的对象数据,否则可能会丢失字段。
6. PATCH 请求(部分更新数据)
1 | fetch("https://jsonplaceholder.typicode.com/posts/1", { |
解析
PATCH
只更新提供的字段,而不是整个对象。
7. DELETE 请求
1 | fetch("https://jsonplaceholder.typicode.com/posts/1", { |
解析
DELETE
方法通常不需要body
。- 服务器返回
200
或204
代表删除成功。
8. 超时处理
fetch
本身不支持超时,但可以使用 AbortController
来实现:
1 | const controller = new AbortController(); |
解析
AbortController
允许中止fetch
请求。- 5 秒后
controller.abort()
触发请求中止。
9. 处理二进制数据(如图片/文件)
1 | fetch("https://via.placeholder.com/150") |
解析
response.blob()
处理图片/文件下载。
10. 并行多个请求
1 | Promise.all([ |
解析
Promise.all([...])
并行执行多个fetch
请求,等所有请求完成后返回结果。
11. 使用 fetch
进行跨域请求
JSONP(如果服务器支持)
1 | function jsonpRequest(url, callbackName) { |
CORS 方案
如果是 跨域 请求,后端需要支持 Access-Control-Allow-Origin
。
总结
方法 | 作用 | 说明 |
---|---|---|
GET |
获取数据 | 可带查询参数 |
POST |
提交新数据 | body 发送 JSON |
PUT |
更新整个资源 | 需提供完整数据 |
PATCH |
局部更新资源 | 只修改部分字段 |
DELETE |
删除资源 | 一般不带 body |
.json() |
解析 JSON 响应 | Promise 方式解析 |
.text() |
解析文本响应 | 适用于 HTML 或纯文本 |
.blob() |
解析二进制数据 | 适用于图片/文件 |
现在你可以灵活使用 fetch
进行各种 HTTP 请求了!🚀
bind&call&apply
bind
、call
和 apply
都是用于改变 this
指向的方法,它们的主要区别如下:
1. call
语法:
1 | func.call(thisArg, arg1, arg2, ...) |
特点:
- 立即调用函数
- 参数直接传递
thisArg
指定this
指向
示例:
1 | function sayHello(greeting) { |
2. apply
语法:
1 | func.apply(thisArg, [arg1, arg2, ...]) |
特点:
- 立即调用函数
- 参数必须以数组形式传递
thisArg
指定this
指向
示例:
1 | function sum(a, b) { |
apply
适用于参数数量不确定的场景,例如:
1 | const numbers = [1, 2, 3, 4, 5]; |
3. bind
语法:
1 | const newFunc = func.bind(thisArg, arg1, arg2, ...) |
特点:
- 不会立即调用,而是返回一个新的函数
- 参数可以预先传入
thisArg
指定this
指向
示例:
1 | function greet(greeting) { |
对比总结
方法 | 是否立即执行 | 参数传递方式 | 是否返回新函数 |
---|---|---|---|
call |
是 | 逗号分隔传递参数 | 否 |
apply |
是 | 以数组传递参数 | 否 |
bind |
否 | 逗号分隔传递参数 | 是 |
什么时候用?
call
:立即调用,适用于改变this
并传递参数的情况。apply
:立即调用,适用于参数以数组形式传递的情况(如Math.max.apply(null, arr)
)。bind
:返回新函数,适用于需要稍后执行,或部分参数预设的情况。