目录
-
- [一、ES6+ 核心语法](#一、ES6+ 核心语法)
-
- [1.1 解构赋值](#1.1 解构赋值)
- [1.2 箭头函数](#1.2 箭头函数)
- [1.3 模板字符串](#1.3 模板字符串)
- [1.4 展开运算符 `...`](#1.4 展开运算符
...) - [1.5 async / await](#1.5 async / await)
- [1.6 可选链 `?.`](#1.6 可选链
?.) - [1.7 空值合并 `??`](#1.7 空值合并
??) - [1.8 Map 和 Set](#1.8 Map 和 Set)
- [二、模块化 ESM](#二、模块化 ESM)
-
- [2.1 为什么需要模块化](#2.1 为什么需要模块化)
- [2.2 ESM 是什么](#2.2 ESM 是什么)
- [2.3 导出语法](#2.3 导出语法)
- [2.4 导入语法](#2.4 导入语法)
- [2.5 动态 import](#2.5 动态 import)
- [2.6 ESM vs CommonJS](#2.6 ESM vs CommonJS)
- [2.7 `import { ref } from 'vue'` 完整解读](#2.7
import { ref } from 'vue'完整解读)
- [三、Node.js / npm 基础](#三、Node.js / npm 基础)
-
- [3.1 Node.js 是什么](#3.1 Node.js 是什么)
- [3.2 npm 和 pnpm](#3.2 npm 和 pnpm)
- [3.3 常用命令](#3.3 常用命令)
- [3.4 package.json 结构](#3.4 package.json 结构)
- [3.5 版本号规则](#3.5 版本号规则)
- [3.6 node_modules 是什么](#3.6 node_modules 是什么)
- [3.7 一个 Vue 项目的完整启动流程](#3.7 一个 Vue 项目的完整启动流程)
一、ES6+ 核心语法
ES6 = JavaScript 的一次革命性版本更新 + 一套影响至今的现代语法规范。本质上就是js。在Vue3里有广泛使用,所以总结以下常用的写法。
1.1 解构赋值
语法:
// 对象解构
const { 键名1, 键名2, 键名3 } = 对象
// 数组解构
const [ 变量1, 变量2, 变量3 ] = 数组
对象解构:等号右边的对象里,取出与左边同名的键,赋值给左边的变量。左边写什么键名,就去右边取什么值。
数组解构:按位置对应------第一个变量取数组第一个元素,第二个取第二个,依此类推。
详细讲解:
传统写法中,要从对象里拿几个字段出来,需要重复写 对象.字段,既啰嗦又容易写错:
javascript
const name = user.name;
const age = user.age;
const email = user.email; // 3 个字段就要写 3 遍 user.
解构赋值一行搞定:
javascript
const { name, age, email } = user;
这行的意思是:从 user 对象中找到 name 这个键,把它的值赋给变量 name,age 和 email 同理。左边就是一个"模板",右边就是数据源,模板和数据源的结构对上就提取出来了。
更高级的用法------改名(当键名不合适时):
javascript
const { name: userName, age: userAge } = user;
// 把 user.name 赋给变量 userName,user.age 赋给 userAge
Vue3 中无处不在:
javascript
const { ref, computed, onMounted } = from 'vue';
// 从 vue 包里取 ref、computed、onMounted 这三个东西
const { name, age } = defineProps(['name', 'age']);
// 从 defineProps 返回的对象里取 name 和 age
const { query, push } = useRouter();
// 从 useRouter 返回的对象里取 query 和 push
1.2 箭头函数
语法:
// 一个参数,一个表达式(省略括号和 return)
参数 => 返回值表达式
// 多个参数,一个表达式
(参数1, 参数2) => 返回值表达式
// 多个参数,多行函数体(需要写 return)
(参数1, 参数2) => {
语句1
语句2
return 返回值
}
// 无参数
() => 返回值表达式
箭头函数由三部分组成:参数列表 + => + 函数体。函数体如果只有一个表达式,可以省略 {} 和 return,这个表达式的结果自动作为返回值。
详细讲解:
箭头函数是传统函数的简写形式,但有一个重要区别------它不绑定自己的 this,this 继承自外层作用域。
javascript
// 传统函数
const add = function(a, b) {
return a + b;
};
// 箭头函数 ------ 完全等价
const add = (a, b) => a + b;
// 一个参数的极简形式
const double = n => n * 2;
// 无参数
const getRandom = () => Math.random();
对于 Vue3 来说,为什么箭头函数是主流?
Vue2 时代大量用 this:this.name、this.count、this.method()。Vue3 的组合式 API(<script setup>)抛弃了 this,所有数据和函数都在顶层作用域直接访问。箭头函数不绑定 this 在这里正好------它继承外层作用域,行为自然。
javascript
// Vue3 写法 ------ 没有 this
const count = ref(0);
const double = computed(() => count.value * 2);
function increment() {
count.value++;
}
不用的后果:Vue3 的所有官方示例、教程、组件库源码,回调全是箭头函数。你不用这个写法,连官方文档都看不懂。
1.3 模板字符串
语法:
`字符内容 ${JavaScript表达式} 更多字符内容`
`第一行
第二行
第三行`
用反引号 `````(键盘上数字 1 左边那个键)包裹。${} 里面可以写任何 JS 表达式(变量、函数调用、三元运算等),它的结果会被拼接到字符串中。支持直接换行,不需要写 \n。
详细讲解:
传统字符串拼接的问题:
javascript
// 传统 ------ + 号把字符串断成好几截,容易漏空格
const msg = 'Hello, ' + name + '! You are ' + age + '.';
// 模板字符串 ------ 插值符 ${} 一目了然
const msg = `Hello, ${name}! You are ${age}.`;
// 多行 HTML ------ 传统写法要用 + 和 \n
const html = '<div>\n' +
' <h1>' + title + '</h1>\n' +
' <p>' + content + '</p>\n' +
'</div>';
// 模板字符串 ------ 直接换行,自然
const html = `
<div>
<h1>${title}</h1>
<p>${content}</p>
</div>
`;
Vue3 中动态拼接 class 名、URL、样式值:
javascript
const btnClass = `btn btn-${variant} ${isActive ? 'btn-active' : ''}`;
const apiUrl = `https://api.example.com/v1/users/${userId}/posts`;
不用的后果:你要写大量 + 号拼接,代码又碎又容易出错(比如忘了加空格或逗号)。
1.4 展开运算符 ...
语法:
// 展开 ------ 把数组/对象拆开放到新容器中
[...原数组, 新元素]
{...原对象, 新键: 新值}
// 剩余 ------ 把剩余元素收集成一个数组
function 函数名(固定参数, ...剩余参数数组) {}
const [第一项, 第二项, ...剩余数组] = 数组
三个点 ... 在不同位置含义不同。展开 是把已有的东西"抖开"放进去;剩余是把剩下的东西"收拢"成一个数组。
详细讲解:
展开(Spread)------ 用在赋值等号右侧,或数组/对象的字面量中:
javascript
// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
// arr2 结果是 [1, 2, 3, 4, 5]
// 对象展开
const user = { name: 'Alice', age: 25 };
const updated = { ...user, role: 'admin' };
// updated 结果是 { name: 'Alice', age: 25, role: 'admin' }
// 注意:这是建立了一个新对象,原对象 user 没有被修改
剩余(Rest)------ 用在赋值等号左侧,或函数参数位置:
javascript
// 数组解构中的剩余
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first=1, second=2, rest=[3, 4, 5]
// 函数参数中的剩余 ------ 把多个参数收成一个数组
function logAll(...args) {
args.forEach(arg => console.log(arg));
}
logAll('a', 'b', 'c'); // args = ['a', 'b', 'c']
Vue3 中最重要的用法------不可变更新。在 Vue 的响应式系统中,直接修改原数据可能会导致不可预期的副作用。标准做法是创建一个新对象,覆盖旧对象:
javascript
const user = ref({ name: 'Alice', age: 25, role: 'user' });
function promoteToAdmin() {
user.value = { ...user.value, role: 'admin' };
// 新对象 = 把 user 原来的所有字段展开 + 覆盖 role
}
不用的后果:你写不了符合 Vue3 规范的"不可变数据更新",要么直接改出 bug,要么代码很笨重地逐字段复制。
1.5 async / await
语法:
async function 函数名(参数) {
try {
const 结果1 = await Promise对象1;
const 结果2 = await Promise对象2;
return 最终结果;
} catch (错误对象) {
// 处理错误
}
}
async 加在 function 前面,声明这个函数内部有异步操作。await 加在 Promise 对象前面,会暂停当前函数的执行,等 Promise 完成后拿到结果再继续往下走。用 try/catch 包裹 await 来捕获错误。
详细讲解:
JavaScript 中很多操作是异步的------发网络请求、读取文件、定时器。传统上用回调函数或 .then() 来处理:
javascript
// 回调地狱 ------ 嵌套越来越多
fetchUser(id, function(user) {
fetchPosts(user.id, function(posts) {
fetchComments(posts[0].id, function(comments) {
render(comments);
});
});
});
// Promise 链式 ------ 比回调好,但每多一步就多一个 .then()
fetchUser(id)
.then(user => fetchPosts(user.id))
.then(posts => fetchComments(posts[0].id))
.then(comments => render(comments))
.catch(err => console.error(err));
async/await 让异步代码看起来像同步一样自然:
javascript
async function loadFullData(userId) {
try {
const user = await fetchUser(userId);
const posts = await fetchPosts(user.id);
const comments = await fetchComments(posts[0].id);
render(comments);
} catch (err) {
console.error('加载失败', err);
}
}
注意:await 会暂停当前函数,但不会阻塞整个程序------浏览器界面照常响应其他事件。这是 JS 事件循环的机制。
Vue3 中几乎所有组件初始化都需要发请求拿数据:
javascript
onMounted(async () => {
loading.value = true;
try {
const res = await api.getUserList();
users.value = res.data;
} catch (e) {
error.value = e.message;
} finally {
loading.value = false;
}
});
不用的后果:你在 Vue 组件中发 API 请求时,要么用 .then() 链(多一步嵌套就乱一层),要么写不出来"先拿用户 ID → 再拿用户文章 → 最后才渲染"这种有依赖关系的异步流程。
1.6 可选链 ?.
语法:
对象?.属性
对象?.属性?.更深属性
对象?.方法?.()
数组?.[索引]
?. 前面的值是 null 或 undefined 时,整个表达式直接返回 undefined,不再继续访问后面的内容,也不报错。如果前面的值存在,就正常访问后面的属性。
详细讲解:
深层嵌套的对象,中间某一层可能是 null 或 undefined。传统写法需要每一层都做判断:
javascript
// 传统 ------ 少写一个 && 就崩
const city = user && user.address && user.address.city;
// 可选链 ------ 一行搞定,安全
const city = user?.address?.city;
// 如果 user 是 undefined → 返回 undefined,不往后走
// 如果 user.address 是 null → 返回 undefined,不往后走
// 都正常 → 返回 user.address.city 的值
不用的后果:null.city 这种表达式会直接抛出 TypeError: Cannot read properties of null,导致整个页面白屏崩溃。特别是 API 返回的数据结构不可控时,某个字段可能不存在。
javascript
// Vue3 模板中
<p>{{ user?.profile?.avatar?.url }}</p>
<!-- 即使 profile 或 avatar 是 null,页面照常渲染,只是不显示这段内容 -->
1.7 空值合并 ??
语法:
可能为null或undefined的值 ?? 兜底的默认值
如果左边不是 null 也不是 undefined,返回左边;否则返回右边。
详细讲解:
?? 和 || 看起来像,但有本质区别:
javascript
const score = 0;
// || 会把所有"假值"都当作不存在:0、''、false、null、undefined
const a = score || 100; // a = 100 ❌ 0 被吞了
// ?? 只认 null 和 undefined,0 是有效值
const b = score ?? 100; // b = 0 ✅
// 同理
const name = '';
const c = name || '匿名'; // c = '匿名' ❌ 空字符串被吞了
const d = name ?? '匿名'; // d = '' ✅
什么时候用 ??:当 0、''、false 本身是有效值,你只希望在真正"没传"(null/undefined)时才给默认值。
javascript
// Vue3 中 ------ 组件 props 设置默认值
const pageSize = props.pageSize ?? 20; // 没传 pageSize 时用 20
const count = data?.total ?? 0; // 从 API 取数据,没有就给 0
1.8 Map 和 Set
语法:
// Set ------ 不重复值的集合
const 变量名 = new Set([初始值1, 初始值2, ...])
变量名.add(值)
变量名.has(值)
变量名.delete(值)
变量名.size
// Map ------ 键值对集合(键可以是任何类型)
const 变量名 = new Map()
变量名.set(键, 值)
变量名.get(键)
变量名.has(键)
变量名.delete(键)
变量名.size
Set 类似数组,但每个值只能出现一次(自动去重)。Map 类似对象,但键可以是对象、函数等任意类型,而不限于字符串。
详细讲解:
javascript
// Set ------ 自动去重
const arr = [1, 2, 2, 3, 3, 3];
const unique = new Set(arr);
// unique 里面是 {1, 2, 3}
unique.size; // 3
unique.has(2); // true
unique.add(4); // 添加
[...unique]; // 转回数组 [1, 2, 3, 4]
// Map ------ 键可以是对象
const userScores = new Map();
const user1 = { id: 1 };
const user2 = { id: 2 };
userScores.set(user1, 95);
userScores.set(user2, 87);
userScores.get(user1); // 95
userScores.size; // 2
Vue3 源码中,Map 是响应式系统的核心数据结构------用来存储"哪个数据被哪些组件/副作用依赖了"。作为初学者,知道有这两种结构即可,等以后研究源码时再深挖。
二、模块化 ESM
2.1 为什么需要模块化
假设你写了一个 formatDate 函数,在页面 A、页面 B、页面 C 中都要用:
❌ 不模块化:三个页面各复制粘贴一遍 formatDate
改日期格式时,三个地方都要改,漏一个就 bug
✅ 模块化:把 formatDate 写在 utils/format.js 里
三个页面各自 import
只改一个地方,全部生效
模块化就是一个文件可以导出自己的一部分,其他文件按需导入使用。每个文件各司其职,互不污染全局作用域。
2.2 ESM 是什么
ES Modules(ESM)是 ES6 正式纳入 JavaScript 标准的模块系统。Vue3 + Vite 项目全部使用 ESM。
写代码时:一个 .vue 文件 或 .js 文件 = 一个模块
模块可以 export(导出)自己的代码
其他模块可以 import(导入)这些代码
构建时:Vite 把所有 import/export 关系理清
打包成一个或多个浏览器能识别的文件
2.3 导出语法
语法:
// 方式一:在声明时直接导出
export const 常量名 = 值;
export function 函数名(参数) { ... }
// 方式二:在文件底部统一导出
const 常量名 = 值;
function 函数名(参数) { ... }
export { 常量名, 函数名 };
// 默认导出 ------ 一个文件最多一个
export default 值;
一个文件同时只能有一个 export default,但可以有多个 export。
详细讲解:
javascript
// ===== file: utils/math.js =====
// 命名导出 ------ 导出的东西有精确的名字
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// ===== file: utils/validator.js =====
// 默认导出 ------ 不定义名字,由导入方取名
export default function validateEmail(email) {
return email.includes('@');
}
命名导出 vs 默认导出怎么选:
| 命名导出 | 默认导出 | |
|---|---|---|
| 一个文件能导几个 | 多个 | 最多一个 |
| 导入写法 | import { 精确名字 } |
import 任意名字 |
| IDE 自动补全 | ✅ 输入名字就提示 | ❌ 导入方自己取名,无法自动补全 |
| 重构时 | 改名自动同步到所有导入方 | 容易断 |
| 适用场景 | 工具函数、常量、多个 API | 一个文件主要只导出一个东西(组件、类) |
| Vue 组件惯例 | 不常用(除非一个文件有多个组件) | ✅ 最常用(一个 .vue 文件 = 一个组件) |
如果一个文件主要用于提供一个东西(比如一个 Vue 组件、一个配置对象),用 export default。如果一个文件提供多个工具函数或常量,用命名导出。
2.4 导入语法
语法:
// 导入命名导出 ------ 大括号内写要导入的具体名字,必须和导出时一致
import { 名字1, 名字2 } from '模块路径'
// 导入默认导出 ------ 不用大括号,名字自己起
import 任意名字 from '模块路径'
// 同时导入两种
import 默认导出名字, { 命名导出1, 命名导出2 } from '模块路径'
// 改名导入 ------ 避免名字冲突
import { 原名 as 新名 } from '模块路径'
// 整体导入 ------ 把模块的所有命名导出收成一个对象
import * as 对象名 from '模块路径'
详细讲解:
javascript
// ===== file: app.js =====
// 按名字取
import { PI, add, multiply } from './utils/math.js';
console.log(add(1, 2)); // 3
// 默认导入 ------ 名字可以自己定
import validate from './utils/validator.js';
validate('test@test.com'); // true
// 改名 ------ 项目中可能有两个不同模块都导出了 formatDate
import { formatDate as fd } from './utils/a.js';
import { formatDate as displayDate } from './utils/b.js';
// 整体导入 ------ 把所有导出装进一个对象
import * as MathUtils from './utils/math.js';
MathUtils.PI; // 3.14159
MathUtils.add(1, 2); // 3
模块路径规则:
import xxx from 'vue' → 去 node_modules 找第三方包
import xxx from './xxx.js' → 去当前文件同级的 ./xxx.js 找(本地文件)
import xxx from '@/xxx.js' → @ 是 Vite 配置的路径别名,通常指 src/ 目录
2.5 动态 import
语法:
const 模块对象 = await import('模块路径')
import() 是一个函数,返回 Promise。做到运行时按需加载------不需要在文件顶部提前加载,而是等到"需要的时候"才去加载。
详细讲解:
javascript
// 静态导入 ------ 文件一执行就加载(不管后面用不用)
import { Chart } from 'heavy-chart-lib';
// 动态导入 ------ 用户点击时才加载
async function loadChart() {
const { Chart } = await import('heavy-chart-lib');
const chart = new Chart(container);
}
document.getElementById('btn').onclick = loadChart;
Vue3 路由懒加载的应用:
javascript
// 常规导入 ------ 用户访问首页时就把所有页面都加载了
import Home from './views/Home.vue';
import Dashboard from './views/Dashboard.vue';
import Settings from './views/Settings.vue';
// 动态导入 ------ 只有用户访问 /dashboard 时才加载 Dashboard.vue
const Dashboard = () => import('./views/Dashboard.vue');
const Settings = () => import('./views/Settings.vue');
这样首屏加载只加载必要的代码,其他页面等用户点到时再加载,显著提升首次打开速度。
2.6 ESM vs CommonJS
CommonJS 是 Node.js 传统的模块系统(用 require / module.exports)。Vue 项目不用它,但有时候会见到(比如一些老的 Node.js 工具、配置文件),了解区别有助于不混淆。
语法对比:
javascript
// ===== CommonJS(Node.js 传统) =====
const fs = require('fs');
const path = require('path');
module.exports = { myFunction };
exports.helper = () => {};
// ===== ESM(Vue 项目用) =====
import fs from 'fs';
import path from 'path';
export const myFunction = () => {};
export function helper() {}
本质区别:
| ESM | CommonJS | |
|---|---|---|
| 加载时机 | 编译时静态解析(代码运行前分析依赖) | 运行时动态加载(执行到 require 才加载) |
| 浏览器支持 | ✅ 原生支持(现代浏览器 <script type="module">) |
❌ 不支持 |
| Tree-shaking | ✅ 打包时可"摇掉"没用的代码 | ❌ 不能 |
| 静态分析 | ✅ 工具可以分析出导出了什么 | ❌ 因为是动态的,分析不了 |
| Vue 生态 | ✅ 全部用 ESM | ❌ 不用 |
一个文件同时看到两种语法怎么办?
javascript
// 有时在 .config 文件中看到
module.exports = {
// CommonJS 写法
};
// 在 .vue 和 src/ 下的 .js 中看到
import xxx from 'xxx'
export const xxx = ...
规则是:.vue 文件和 src/ 目录下的 JS 文件用 ESM。vite.config.js 等构建配置文件有时用 CommonJS(因为 Vite 内部处理了兼容)。写 Vue 组件时只用 ESM 即可。
2.7 import { ref } from 'vue' 完整解读
这句是你进入 Vue3 世界后看到的第一行代码,逐段拆开:
import { ref, onMounted } from 'vue'
↑ ↑ ↑ ↑
ESM 关键字 解构赋值,从 "来自哪里" npm 包名,
(声明导入) vue包中取出 ref 去 node_modules
和 onMounted 这两个 找 vue 这个包
命名导出
本质上就是:去 node_modules 里找到 vue 这个包,
从它的导出中取出 ref 和 onMounted 这两个东西,
赋值给当前文件的同名变量使用。
如果你没写这行,直接写 ref(0),浏览器会报错:ref is not defined。
三、Node.js / npm 基础
3.1 Node.js 是什么
Node.js 是服务端 JavaScript 运行环境。Vue 项目最终在浏览器运行,但开发和构建过程依赖 Node.js。
在终端输入 pnpm run dev
│
▼
Node.js 启动 Vite(构建工具)
│
▼
Vite 把 .vue 文件编译成浏览器能识别的
.html + .js + .css
│
▼
打开浏览器访问 http://localhost:5173
看到你的项目
所以 Vue 开发 = 用 Node.js 跑一个开发服务器,然后浏览器连上去。你改代码 → 服务器自动重新编译 → 浏览器自动刷新。
3.2 npm 和 pnpm
npm(Node Package Manager)是 Node.js 自带的包管理器。pnpm 是它的升级替代品,更快、更省磁盘空间。Vue 官方推荐用 pnpm,命令语法基本一样。
3.3 常用命令
语法:
pnpm create 项目模板名 ← 用模板创建项目(如 vue@latest)
pnpm install ← 安装 package.json 中所有依赖
pnpm add 包名 ← 安装某个包到 dependencies
pnpm add -D 包名 ← 安装某个包到 devDependencies
pnpm remove 包名 ← 卸载某个包
pnpm run 脚本名 ← 执行 package.json 中 scripts 里定义的命令
详细讲解:
bash
# 创建一个 Vue 项目(交互式问答:项目名、是否用 TS / 路由 / Pinia 等)
pnpm create vue@latest
# 进入项目目录
cd my-vue-app
# 安装所有依赖(根据 package.json 中列出的包全部下载到 node_modules)
pnpm install
# 安装运行时依赖(项目运行必须的,打包时会包含)
pnpm add vue-router
pnpm add pinia
# 安装开发依赖(只有开发时用,打包后不包含)
pnpm add -D eslint prettier
pnpm add -D @types/node
# 运行脚本
pnpm run dev # 启动开发服务器(热更新,改代码自动刷新)
pnpm run build # 构建生产版本(生成 dist/ 目录,放服务器上线)
pnpm run preview # 本地预览构建后的结果
3.4 package.json 结构
语法:
json
{
"name": "项目名",
"version": "版本号",
"private": true,
"scripts": {
"命令名1": "要执行的命令",
"命令名2": "要执行的命令"
},
"dependencies": {
"包名": "版本范围"
},
"devDependencies": {
"包名": "版本范围"
}
}
name:项目名称,也是 npm 包名(发布到 npm 时用)。如果不发布,加 "private": true 防止误发布。
scripts:自定义的快捷命令。pnpm run dev 就是执行这里 "dev": "vite"。
dependencies:项目运行时需要的包。比如 Vue 本身、路由库、状态管理库。这些会被打包进最终文件。
devDependencies:开发阶段需要的工具。比如构建工具 Vite、代码检查 ESLint、格式化 Prettier。它们不会打包进最终文件。
json
{
"name": "my-vue-app",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.4.0",
"vue-router": "^4.3.0",
"pinia": "^2.1.0"
},
"devDependencies": {
"vite": "^5.0.0",
"eslint": "^8.0.0",
"prettier": "^3.0.0"
}
}
3.5 版本号规则
语法:
主版本 . 次版本 . 补丁版本
3 . 4 . 0
^3.4.0 → 接受 >=3.4.0 且 <4.0.0(自动升级次版本和补丁版本)
~3.4.0 → 接受 >=3.4.0 且 <3.5.0(只自动升级补丁版本)
3.4.0 → 锁死,绝不自动升级
* → 任何版本(不推荐,太不可控)
- 主版本:大改版,API 不向下兼容(3→4 可能有破坏性变化)
- 次版本:加新功能,向下兼容(3.3→3.4 加了东西但旧代码不受影响)
- 补丁:修 Bug,向下兼容(3.4.0→3.4.1 只是修补)
^ 的意思是"接受兼容范围内的最新版本"------安装时如果已有 3.4.5,就装 3.4.5 而不是 3.4.0。需要锁死时必须明确写 "vue": "3.4.0"。
3.6 node_modules 是什么
运行 pnpm install 后,项目根目录会出现 node_modules/ 文件夹。里面装着 Vue 及 Vue 所依赖的所有代码包。一个中等项目可能有几百 MB。
这个文件夹永远不会提交到 Git ------在 .gitignore 中声明忽略。因为:
- 文件太大,git 仓库根本存不下
- 它是可生成的------任何人在任何机器上运行
pnpm install都能重建
3.7 一个 Vue 项目的完整启动流程
你在终端输入: 背后发生:
pnpm create vue@latest → 下载 Vue 项目模板
└─ 交互式问答 生成项目文件结构
项目名? 创建 my-vue-app/
用 TS? 创建 src/、public/ 等目录
用 Vue Router? 写入 package.json、vite.config.ts
用 Pinia? 等等
用 ESLint?
cd my-vue-app → 进入项目目录
pnpm install → 读取 package.json
下载 vue、vite 等所有依赖
放到 node_modules/
生成 pnpm-lock.yaml(锁版本号)
pnpm run dev → 执行 "vite"
Vite 启动开发服务器
默认打开 http://localhost:5173
.vue 文件被编译成浏览器能识别的代码
改代码 → 自动热更新
此时打开浏览器,输入 http://localhost:5173,看到页面就说明一切正常。