普通视图

发现新文章,点击刷新页面。
昨天 — 2026年1月2日首页

面试官 : “ Vue 选项式api 和 组合式api 什么区别? “

作者 千寻girling
2026年1月2日 18:12

Vue 的选项式 API (Options API)  和组合式 API (Composition API)  是两种核心的代码组织方式,前者侧重 “按选项分类”,后者侧重 “按逻辑分类”,核心差异体现在设计理念、代码结构、复用性等维度,以下是全面对比和实用解读:

一、核心区别对比表

维度 选项式 API (Options API) 组合式 API (Composition API)
设计理念 按 “选项类型” 组织代码(data、methods、computed 等) 按 “业务逻辑” 组织代码(同一逻辑的代码聚合)
代码结构 分散式:同一逻辑的代码分散在不同选项中 聚合式:同一逻辑的代码集中在 setup 或 <script setup>
适用场景 小型组件、简单业务(快速上手) 中大型组件、复杂业务(逻辑复用 / 维护)
逻辑复用 依赖 mixin(易命名冲突、来源不清晰) 依赖组合式函数(Composables,纯函数复用,清晰可控)
类型推导 对 TypeScript 支持弱(需手动标注) 天然适配 TS,类型推导更完善
响应式写法 声明式:data 返回对象,Vue 自动响应式 手动式:用 ref/reactive 创建响应式数据
生命周期 直接声明钩子(created、mounted 等) setup 中通过 onMounted 等函数调用(无 beforeCreate/created)
代码可读性 简单场景清晰,复杂场景 “碎片化” 复杂场景逻辑聚合,可读性更高
上手成本 低(符合传统前端思维) 稍高(需理解 ref/reactive/setup 等概念)

二、代码示例直观对比

1. 选项式 API(Vue 2/3 兼容)

<template>
  <div>{{ count }} <button @click="add">+1</button></div>
</template>

<script>
export default {
  // 数据(响应式)
  data() {
    return {
      count: 0
    };
  },
  // 方法
  methods: {
    add() {
      this.count++;
    }
  },
  // 生命周期
  mounted() {
    console.log('组件挂载:', this.count);
  }
};
</script>

特点:代码按 data/methods/mounted 等选项拆分,同一 “计数逻辑” 分散在不同区块。

2. 组合式 API(Vue 3 推荐,<script setup> 语法糖)

<template>
  <div>{{ count }} <button @click="add">+1</button></div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

// 响应式数据(ref 用于基本类型)
const count = ref(0);

// 方法(与数据聚合)
const add = () => {
  count.value++; // ref 需通过 .value 访问
};

// 生命周期
onMounted(() => {
  console.log('组件挂载:', count.value);
});
</script>

特点:“计数逻辑” 的数据、方法、生命周期全部聚合在一处,逻辑边界清晰。

三、核心差异深度解读

1. 逻辑复用:从 mixin 到 Composables(组合式函数)

  • 选项式 API 的痛点(mixin) :复用逻辑需写 mixin 文件,多个 mixin 易出现命名冲突,且无法清晰知道属性来源:

    // mixin/countMixin.js
    export default {
      data() { return { count: 0 }; },
      methods: { add() { this.count++; } }
    };
    // 组件中使用
    export default {
      mixins: [countMixin], // 引入后,count/add 混入组件,但来源不直观
    };
    
  • 组合式 API 的优势(Composables) :复用逻辑封装为纯函数,按需导入,属性来源清晰,无命名冲突:

    // composables/useCount.js
    import { ref } from 'vue';
    export const useCount = () => {
      const count = ref(0);
      const add = () => count.value++;
      return { count, add };
    };
    // 组件中使用
    <script setup>
    import { useCount } from './composables/useCount';
    const { count, add } = useCount(); // 明确导入,来源清晰
    </script>
    

2. 响应式原理:声明式 vs 手动式

  • 选项式 API:data 返回的对象会被 Vue 递归劫持(Object.defineProperty/Proxy),自动变成响应式,直接通过 this.xxx 访问;
  • 组合式 API:需手动用 ref(基本类型)/reactive(引用类型)创建响应式数据,ref 需通过 .value 访问(模板中自动解包),更灵活且可控。

3. 大型项目适配性

  • 选项式 API:组件复杂度提升后,同一业务逻辑的代码会分散在 data/methods/computed/watch 等多个选项中,形成 “面条代码”,维护成本高;
  • 组合式 API:可将复杂业务拆分为多个 Composables(如 useUser/useCart/useOrder),每个函数负责一个逻辑模块,代码结构清晰,便于多人协作和维护。

四、选型建议

场景 推荐 API
小型组件 / 快速原型 选项式 API
中大型项目 / 复杂逻辑 组合式 API
需兼容 Vue 2 + Vue 3 选项式 API(过渡)
用 TypeScript 开发 组合式 API

总结

  • 选项式 API 是 “面向选项” 的思维,适合入门和简单场景,符合传统前端的代码组织习惯;
  • 组合式 API 是 “面向逻辑” 的思维,解决了选项式 API 在复杂场景下的复用、维护痛点,是 Vue 3 的核心升级,也是大型项目的最佳实践。

两者并非互斥,Vue 3 完全兼容选项式 API,可根据项目规模和团队习惯灵活选择(甚至同一项目中混合使用)。

面试官 : “ 说一下 Map 和 WeakMap 的区别 ? ”

作者 千寻girling
2026年1月2日 12:08

一、Map vs WeakMap

特性 Map WeakMap
键的类型 任意类型(基本类型 / 引用类型) 仅支持引用类型(对象)
键的引用特性 强引用:键对象不会被 GC 回收 弱引用:键对象无其他引用时,会被 GC 自动回收(键值对随之消失)
遍历性 支持(keys ()/values ()/entries ()/forEach/for...of) 不支持(无遍历方法、无 size 属性)
键的枚举 / 获取 可获取所有键(如 Array.from (map.keys ())) 无法获取 / 枚举所有键(无 API)
常用 API set/get/has/delete/clear/size set/get/has/delete(无 clear/size)
内存占用 键对象未手动删除则一直占用 自动回收无引用的键,内存更友好
使用场景 需遍历 / 枚举、键为基本类型、长期存储键值对 临时关联数据(如 DOM 元素→元数据)、避免内存泄漏

核心差异:弱引用

  • Map 对键是强引用:即使键对象外部无引用,Map 仍持有该对象,GC 不会回收,可能导致内存泄漏;
  • WeakMap 对键是弱引用:键对象仅被 WeakMap 引用时,GC 会回收该对象,同时 WeakMap 中对应的键值对也会被移除(无需手动删除)。

示例

// Map:强引用导致内存泄漏风险
const map = new Map();
let obj = { id: 1 };
map.set(obj, "data");
obj = null; // 手动置空,但map仍引用obj,GC不会回收

// WeakMap:弱引用自动回收
const weakMap = new WeakMap();
let obj2 = { id: 2 };
weakMap.set(obj2, "data");
obj2 = null; // obj2无其他引用,GC回收后,weakMap中该键值对消失

二、Set vs WeakSet

特性 Set WeakSet
值的类型 任意类型(基本类型 / 引用类型) 仅支持引用类型(对象)
值的引用特性 强引用:值对象不会被 GC 回收 弱引用:值对象无其他引用时,会被 GC 自动回收(值随之移除)
遍历性 支持(keys ()/values ()/entries ()/forEach/for...of) 不支持(无遍历方法、无 size 属性)
值的枚举 / 获取 可获取所有值(如 Array.from (set)) 无法获取 / 枚举所有值(无 API)
常用 API add/has/delete/clear/size add/has/delete(无 clear/size)
内存占用 值对象未手动删除则一直占用 自动回收无引用的值,内存更友好
使用场景 需遍历 / 枚举、值为基本类型、存储唯一值集合 存储临时对象(如 DOM 元素集合)、避免内存泄漏

核心差异:弱引用

  • Set 对值是强引用:值对象即使外部无引用,Set 仍持有,GC 不回收;
  • WeakSet 对值是弱引用:值对象仅被 WeakSet 引用时,GC 会回收该对象,WeakSet 中对应的项也会被移除。

示例

// Set:强引用
const set = new Set();
let obj = { id: 1 };
set.add(obj);
obj = null; // set仍引用obj,GC不回收

// WeakSet:弱引用
const weakSet = new WeakSet();
let obj2 = { id: 2 };
weakSet.add(obj2);
obj2 = null; // obj2无其他引用,GC回收后,weakSet中该值消失

三、Map vs Set 区别(补充知识)

Map 和 Set 都是 ES6 新增的有序集合(迭代顺序为插入顺序) ,均为强引用、支持遍历、可存储唯一值,但核心定位和数据结构完全不同,以下是详细对比:

特性 Map Set
核心定位 键值对集合(键→值映射) 值的集合(仅存储唯一值,无键)
存储形式 [key, value] 键值对,键唯一、值可重复 单个值(value),值必须唯一
重复判定规则 键唯一(NaN 视为相同,对象引用不同则视为不同) 值唯一(规则同 Map 键的判定)
核心 API(增) set(key, value):按键存值 add(value):添加值
核心 API(查) get(key):按键取值;has(key):判断键是否存在 has(value):判断值是否存在(无 get
核心 API(删) delete(key):按键删除键值对 delete(value):按值删除项
遍历方式 可遍历键(keys())、值(values())、键值对(entries() 可遍历值(keys()/values() 等价,entries() 返回 [value, value]
长度 / 大小 size 属性:返回键值对数量 size 属性:返回唯一值数量
使用场景 1. 键值映射(如 ID→用户信息)2. 需要通过 “键” 快速查找 “值”3. 存储关联数据 1. 存储不重复的唯一值集合(如去重数组)2. 仅需判断 “值是否存在”3. 过滤重复数据

1. Map:键值对存储与查找

const map = new Map();
// 存:键唯一,值可重复
map.set("id1", { name: "张三" });
map.set("id2", { name: "李四" });
map.set("id1", { name: "张三2" }); // 覆盖id1的旧值

// 查:按键取值
console.log(map.get("id1")); // { name: "张三2" }
console.log(map.has("id2")); // true

// 遍历:键、值、键值对
for (const key of map.keys()) console.log(key); // id1、id2
for (const value of map.values()) console.log(value); // {name: "张三2"}、{name: "李四"}
for (const [k, v] of map.entries()) console.log(k, v);

2. Set:唯一值集合(无键)

const set = new Set();
// 存:值唯一,重复添加无效
set.add(1);
set.add(2);
set.add(1); // 无效果,1已存在

// 查:仅能判断值是否存在,无get
console.log(set.has(2)); // true
// console.log(set.get(2)); // 报错:Set 无get方法

// 遍历:keys/values等价,entries返回[值, 值]
for (const val of set.values()) console.log(val); // 1、2
for (const [v1, v2] of set.entries()) console.log(v1, v2); // 1 1、2 2

// 典型场景:数组去重
const arr = [1, 2, 2, 3];
const uniqueArr = [...new Set(arr)]; // [1,2,3]

3.核心总结

维度 Map Set
数据结构 键值对(字典) 单值集合(集合)
核心操作 按 “键” 存 / 取 / 删 按 “值” 增 / 判 / 删(无取值操作)
重复处理 键唯一(值可重复) 值唯一(无重复)
核心用途 键值映射、关联数据存储 去重、唯一值判断

简单记:

  • 需要 “通过一个标识找对应数据”→ 用 Map;
  • 只需要 “存储不重复的一组值,或判断值是否存在”→ 用 Set。

三、通用总结

类型 核心特点 适用场景
Map/Set 强引用、支持遍历、键 / 值可存任意类型 需持久存储、遍历、键 / 值为基本类型的场景
WeakMap/WeakSet 弱引用、不支持遍历、仅存引用类型 临时关联数据、避免内存泄漏(如 DOM / 临时对象)

关键提醒

WeakMap/WeakSet 无法遍历 / 获取 size,因为其内部数据会被 GC 动态修改,无法保证数据的稳定性;

而 Map/Set 是 “可预测” 的静态集合(除非手动修改)。

昨天以前首页

面试官 : “ 说一下 localhost 和127.0.0.1 的区别 ? ”

作者 千寻girling
2026年1月1日 17:01

localhost 是主机名(域名) ,属于应用层概念;

127.0.0.1 是IPv4 回环地址,属于网络层概念。

两者都用于访问本机服务,但 localhost 必须通过解析才能映射到具体 IP(默认是 127.0.0.1 或 IPv6 的 ::1),而 127.0.0.1 是直接的网络层标识,无需解析。


一、本质定义与协议层次

概念 localhost 127.0.0.1
本质 互联网标准规定的特殊主机名(RFC 6761 定义) IPv4 协议规定的回环地址(RFC 5735 定义)
协议层次 应用层(DNS 协议解析范畴) 网络层(IP 协议寻址范畴)
归属 属于域名系统(DNS) 属于 IP 地址体系
默认映射 IPv4: 127.0.0.1;IPv6: ::1 仅 IPv4 回环网段(127.0.0.0/8)的第一个地址

关键补充

  1. 127.0.0.0/8 网段:不只是 127.0.0.1,整个 127.x.x.x 网段(共 16777216 个地址)都属于回环地址,访问任何一个都会指向本机。
  2. localhost 的特殊性:它是一个保留主机名,不能被注册为公共域名,且操作系统会优先通过 hosts 文件解析,而非公共 DNS 服务器。

二、解析流程的根本差异

这是两者最核心的区别 ——是否需要解析,以及解析的顺序

1. localhost 的解析流程(应用层 → 网络层)

当你在浏览器输入 http://localhost:3000 时,操作系统会执行以下步骤:

  1. 检查本地 hosts 文件

    • Windows 路径:C:\Windows\System32\drivers\etc\hosts
    • Linux/macOS 路径:/etc/hosts
    • 如果 hosts 文件中有如下映射:127.0.0.1 localhost 或 ::1 localhost,则直接使用对应的 IP。
  2. 若 hosts 文件无映射,查询本地 DNS 缓存

    • 操作系统会检查之前是否解析过 localhost,若有缓存则直接使用。
  3. 若缓存无结果,查询本地 DNS 服务器

    • 但由于 localhost 是保留主机名,公共 DNS 服务器通常也会返回 127.0.0.1 或 ::1
  4. 解析完成后,转换为 IP 地址进行网络请求

    • 此时才进入网络层,使用解析后的 IP 连接本机服务。

2. 127.0.0.1 的访问流程(直接进入网络层)

当你输入 http://127.0.0.1:3000 时,跳过所有解析步骤

  1. 操作系统直接识别这是一个 IPv4 回环地址。
  2. 直接将网络请求发送到本机的网络接口(回环接口,lo 接口)。
  3. 目标服务监听 127.0.0.1 或 0.0.0.0 时,即可响应请求。

三、功能与使用上的具体差异

1. 协议支持差异

  • localhost:支持 IPv4 和 IPv6 双协议

    • 若你的系统开启了 IPv6,localhost 可能优先解析为 ::1(IPv6 回环地址)。
    • 例如:在 Node.js 中,server.listen(3000, 'localhost') 会同时监听 IPv4 的 127.0.0.1:3000 和 IPv6 的 ::1:3000
  • 127.0.0.1仅支持 IPv4

    • 无论系统是否开启 IPv6,使用 127.0.0.1 都只会走 IPv4 协议。
    • 例如:server.listen(3000, '127.0.0.1') 仅监听 IPv4 地址。

2. 性能差异

  • 127.0.0.1 略快:因为跳过了 DNS 解析流程(即使是本地 hosts 文件解析,也需要一次文件读取和匹配)。
  • 差异极小:在开发环境中,这种性能差异几乎可以忽略不计,除非是高频次的请求(如每秒上万次)。

3. 服务监听的差异

服务端程序的监听地址,会影响是否能被 localhost 或 127.0.0.1 访问:

监听地址 能否被 localhost 访问 能否被 127.0.0.1 访问 能否被局域网其他设备访问
localhost ✅(IPv4 解析时)
127.0.0.1 ✅(解析为 127.0.0.1 时)
0.0.0.0 ✅(通过本机局域网 IP)
::1(IPv6) ✅(解析为 ::1 时)

4. 自定义映射的差异

  • localhost 可以被自定义映射

    • 你可以修改 hosts 文件,将 localhost 映射到任意 IP,例如:

      192.168.1.100   localhost
      
    • 此时访问 localhost 会指向局域网的 192.168.1.100,而不是本机。

  • 127.0.0.1 无法被自定义

    • 它是 IPv4 协议规定的回环地址,无论如何修改配置,访问 127.0.0.1 都只会指向本机。

5. 兼容性差异

  • 老旧系统 / 服务:某些非常古老的程序(如早期的 DOS 程序、嵌入式设备程序)可能不识别 localhost 主机名,但一定能识别 127.0.0.1
  • IPv6 专属服务:某些服务仅监听 IPv6 的 ::1,此时只能通过 localhost 访问(解析为 ::1),而 127.0.0.1 无法访问。

四、实际开发中的选择建议

  1. 优先使用 localhost

    • 理由:兼容性更好,支持双协议,符合开发习惯,且无需关心 IPv4/IPv6 配置。
    • 场景:本地开发、测试环境、前端代理配置(如 Vite、Webpack 的 devServer.host: 'localhost')。
  2. 使用 127.0.0.1 的场景

    • 强制使用 IPv4:当服务仅监听 IPv4 地址,或系统 IPv6 配置有问题时。
    • 避免自定义映射:当你怀疑 hosts 文件被修改,localhost 被映射到非本机地址时。
    • 某些工具的特殊要求:部分 CLI 工具或服务(如某些数据库客户端)默认只识别 127.0.0.1
  3. 特殊场景:0.0.0.0

    • 这不是回环地址,而是通配地址,表示监听本机所有网络接口(包括回环接口、局域网接口、公网接口)。
    • 场景:需要让局域网其他设备访问本机服务时(如手机测试前端页面)。

五、验证两者差异的小实验

实验 1:修改 hosts 文件,观察 localhost 映射

  1. 打开 /etc/hosts(Linux/macOS)或 C:\Windows\System32\drivers\etc\hosts(Windows)。
  2. 添加一行:192.168.1.1 localhost
  3. 执行 ping localhost,会发现 ping 的是 192.168.1.1,而非 127.0.0.1
  4. 执行 ping 127.0.0.1,仍然 ping 本机。
  5. 恢复 hosts 文件默认配置:127.0.0.1 localhost 和 ::1 localhost

实验 2:查看服务监听的地址

  1. 在 Node.js 中运行以下代码:

    const http = require('http');
    const server = http.createServer((req, res) => {
      res.end('Hello World!');
    });
    // 监听 localhost
    server.listen(3000, 'localhost', () => {
      console.log('Server running on localhost:3000');
    });
    
  2. 执行 netstat -tulpn | grep 3000(Linux/macOS)或 netstat -ano | findstr 3000(Windows)。

  3. 会发现服务同时监听 127.0.0.1:3000 和 ::1:3000(IPv4 + IPv6)。

  4. 若将监听地址改为 127.0.0.1,则仅监听 127.0.0.1:3000


六、总结:核心区别一览表

对比维度 localhost 127.0.0.1
本质 主机名(域名) IPv4 回环地址
协议层次 应用层(DNS) 网络层(IP)
解析需求 必须解析(hosts → DNS) 无需解析
协议支持 IPv4 + IPv6 仅 IPv4
自定义映射 可通过 hosts 文件修改 不可修改,固定指向本机
服务监听 可同时监听 IPv4/IPv6 仅监听 IPv4
兼容性 现代系统支持,老旧系统可能不支持 所有支持 IPv4 的系统都支持
性能 略慢(解析开销) 略快(无解析开销)

我是千寻, 这期内容到这里就结束了,我们有缘再会😂😂😂 !!!

面试官: “ 说一下怎么做到前端图片尺寸的响应式适配 ”

作者 千寻girling
2025年12月31日 15:26

前端开发中,图片的尺寸适配是响应式设计的核心部分之一,需要结合图片类型、容器场景、设备特性来选择方案。以下是常见的图片尺寸策略和多窗口适配方法:

一、先明确:前端常用的图片尺寸场景

不同场景下,图片的 “合适尺寸” 差异很大:

场景 建议尺寸范围 示例
图标 / 小图标 24×24 ~ 128×128(2 倍图) 按钮图标、头像缩略图
列表缩略图 300×200 ~ 600×400(2 倍图) 商品列表、文章封面缩略图
详情页主图 800×600 ~ 1920×1080(2 倍图) 商品详情图、Banner 图
背景图 1920×1080 ~ 3840×2160 全屏背景、页面 Banner
移动端适配图 750×1334(2 倍图)、1242×2208(3 倍图) 移动端页面元素图

二、多窗口适配的核心方法

1. 基础适配:max-width: 100%(通用)

最常用的适配方式,让图片不超过容器宽度,自动缩放高度:

img {
  max-width: 100%; /* 图片宽度不超过父容器 */
  height: auto;    /* 高度自动按比例缩放,避免变形 */
}

✅ 适用场景:大部分内联图片、列表图、详情图。

2. 背景图适配:background-size

针对背景图,通过 CSS 属性控制缩放逻辑:

.bg-img {
  width: 100%;
  height: 300px;
  background: url("bg.jpg") center/cover no-repeat; 
  /* 或单独设置: */
  background-size: cover; /* 覆盖容器,可能裁剪 */
  /* background-size: contain; 完整显示,可能留白 */
}
  • cover:优先覆盖容器,保持比例(常用全屏背景);
  • contain:优先完整显示,保持比例(常用图标背景)。

3. 响应式图片:srcset + sizes(精准加载)

让浏览器根据设备尺寸 / 像素比,自动选择合适的图片(减少加载体积):

<img 
  src="img-800.jpg"  <!-- 默认图 -->
  srcset="
    img-400.jpg 400w,  <!-- 400px宽的图 -->
    img-800.jpg 800w,  <!-- 800px宽的图 -->
    img-1200.jpg 1200w <!-- 1200px宽的图 -->
  "
  sizes="(max-width: 600px) 400px, 800px" <!-- 告诉浏览器容器宽度 -->
  alt="响应式图片"
>

✅ 适用场景:对加载性能要求高的大图(如 Banner、详情主图)。

4. 移动端高清图:2 倍图 / 3 倍图

针对 Retina 屏,提供高分辨率图,避免模糊:

<!-- 方法1:srcset 按像素比适配 -->
<img 
  src="img@2x.png" 
  srcset="
    img@1x.png 1x,  <!-- 普通屏 -->
    img@2x.png 2x,  <!-- Retina屏 -->
    img@3x.png 3x   <!-- 超高清屏 -->
  "
  alt="高清图"
>

<!-- 方法2:CSS 背景图(针对图标) -->
.icon {
  background: url("icon@2x.png") no-repeat;
  background-size: 24px 24px; /* 实际显示尺寸是24×24,图片是48×48 */
  width: 24px;
  height: 24px;
}

5. 容器限制:object-fit(控制图片在容器内的显示方式)

当图片宽高比与容器不一致时,避免变形:

.img-container {
  width: 300px;
  height: 300px;
  overflow: hidden;
}
.img-container img {
  width: 100%;
  height: 100%;
  object-fit: cover; /* 覆盖容器,裁剪多余部分(常用头像、卡片图) */
  /* object-fit: contain; 完整显示,留白 */
  /* object-fit: fill; 拉伸变形(不推荐) */
}

6. 媒体查询:针对特定窗口尺寸切换图片

强制在不同屏幕下使用不同图片(适合差异较大的场景):

/* 移动端用小图 */
@media (max-width: 768px) {
  .banner {
    background-image: url("banner-mobile.jpg");
  }
}
/* 桌面端用大图 */
@media (min-width: 769px) {
  .banner {
    background-image: url("banner-desktop.jpg");
  }
}

三、总结适配思路

  1. 优先用 max-width: 100% + height: auto:覆盖 80% 的基础场景;
  2. 背景图用 background-size: cover/contain
  3. 大图用 srcset + sizes:兼顾性能和清晰度;
  4. 固定容器用 object-fit:避免图片变形;
  5. 移动端用 2 倍 / 3 倍图:保证高清显示。

面试官: “ 请你讲一下 package.json 文件 ? ”

作者 千寻girling
2025年12月30日 23:49

1. package.json 的作用

package.json 是 Node.js/npm 项目的核心配置文件,位于项目根目录,它的作用包括:

  • 描述项目信息:名称、版本、作者、许可证等。
  • 声明依赖:项目运行所需的包(dependencies)和开发所需的包(devDependencies)。
  • 定义脚本命令:通过 scripts 字段,让你可以用 npm run 执行自定义任务(如启动、测试、构建)。
  • 指定元数据:比如入口文件、浏览器兼容性等。

2. 基本结构示例

一个典型的 package.json 可能如下:

{
  "name": "my-project",
  "version": "1.0.0",
  "description": "A sample Node.js project",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "jest",
    "build": "webpack"
  },
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "jest": "^29.7.0",
    "webpack": "^5.89.0"
  },
  "author": "Your Name",
  "license": "MIT",
  "keywords": ["node", "express", "example"]
}

3. 核心字段说明

3.1 项目信息字段

  • name:项目名称(必须小写,无空格)。
  • version:项目版本,遵循 SemVer(语义化版本),格式为 x.y.z(主版本。次版本。补丁版本)。
  • description:项目的简短描述。
  • author:作者信息,可以是字符串或对象(如 {"name": "xxx", "email": "xxx"})。
  • license:开源许可证类型(如 MITISCGPL)。
  • keywords:项目关键字数组,方便在 npm 上搜索。

3.2 入口与配置字段

  • main:指定项目的入口文件(默认是 index.js)。

  • type:指定模块系统类型:

    • "commonjs"(默认):使用 require() 导入。
    • "module":使用 import/export 语法。
  • files:发布到 npm 时需要包含的文件或目录。

  • repository:项目代码仓库地址。


3.3 依赖字段

  • dependencies:生产环境依赖(项目运行时必需的包),例如:

    "dependencies": {
      "react": "^18.2.0"
    }
    

    版本号前的 ^ 表示兼容当前版本的次版本更新。

  • devDependencies:开发环境依赖(仅开发时使用,比如测试、构建工具),例如:

    "devDependencies": {
      "eslint": "^8.55.0"
    }
    
  • peerDependencies:声明项目运行时需要的外部依赖版本(常用于插件或库)。

  • optionalDependencies:可选依赖,即使安装失败也不会影响项目。


3.4 脚本字段

  • scripts:定义可执行的命令,例如:

    "scripts": {
      "start": "node index.js",
      "dev": "nodemon index.js"
    }
    

    执行方法:

    npm run start
    npm run dev
    

4. package.json 的生成方式

  • 手动创建:直接新建 package.json 文件并写入内容。

  • 使用命令:

    npm init
    

    会通过交互方式生成。

  • 使用默认配置:

    npm init -y
    

    直接生成一个默认的 package.json


5. 与 package-lock.json 的关系

  • package.json:声明依赖的版本范围
  • package-lock.json:锁定安装时的具体版本,确保每次安装的依赖版本一致。

✅ 总结package.json 是项目的 “身份证” 和 “说明书”,它定义了项目的基本信息、依赖关系、可执行脚本等。掌握它的结构和字段,是使用 npm 和 Node.js 开发的基础。

❌
❌