Vue.js 全栈知识点费曼学习法指南
🧠 费曼学习法核心原则
如果你不能简单地解释一个概念,说明你还没有真正理解它。
这份指南将用最简单易懂的语言,帮你彻底掌握Vue.js生态系统的所有核心知识点。
📚 目录结构
第一部分:Vue.js 核心基础
第二部分:Vue.js 进阶特性
第三部分:Vue.js 生态系统
第一部分:Vue.js 核心基础
1. Vue.js 是什么?
简单理解
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架,就像智能家居的控制中心。
生活例子:
- 想象你家里有很多电器:灯、空调、电视
- 传统方式:你需要逐个手动控制每个设备
- Vue方式:一个智能控制中心,你只需要说出需求,它自动协调所有设备
核心特点
1. 渐进式框架 Vue 的设计非常注重灵活性和"可以被逐步集成"这个特点
简单理解: 就像学游泳一样
- 🏊♀️ 浅水区:给现有网页加点交互(无需构建工具)
- 🏊♂️ 深水区:构建完整的单页应用
- 🏊♀️ 比赛级:全栈开发、服务器渲染
2. 声明式渲染 传统命令式: "先做这个,再做那个,然后..." Vue声明式: "我想要这个结果",Vue自动处理过程
scss
// 你只需要声明想要什么
<button @click="count++">点击次数: {{ count }}</button>
3. 响应式系统 简单理解: 数据和界面的"魔法连接"
- 数据变化 → 界面自动更新
- 就像Excel表格,改变A1,使用A1的公式会自动重新计算
2. 响应式系统
什么是响应式?
响应性是一种可以使我们声明式地处理变化的编程范式
生活例子:
- 温度计和空调:温度计检测到温度变化,空调自动调整
- 银行账户和ATM显示:账户余额变化,ATM屏幕自动更新显示
Vue的响应式原理
用厨房类比:
scss
数据(食材) → Vue响应式系统(智能厨房) → 界面(菜品)
核心概念:
1. ref() - 基本数据类型的响应式
js
import { ref } from 'vue'
const count = ref(0) // 包装成响应式
// 简单理解:给数据加个"监控器"
2. reactive() - 对象的响应式
js
import { reactive } from 'vue'
const state = reactive({
name: '张三',
age: 25
})
// 简单理解:给整个对象加"智能监控"
记忆技巧:
ref
= 单个值的"保险箱"reactive
= 整个对象的"智能监控系统"
响应式的"魔法"
原理简化版:
- 依赖收集:Vue记住"谁在用这个数据"
- 变化检测:数据变化时,Vue知道了
- 自动更新:通知所有使用这个数据的地方更新
实际例子:
js
// 1. 创建响应式数据
const message = ref('Hello')
// 2. 模板中使用
<p>{{ message }}</p> // Vue记住:这个p标签用了message
// 3. 数据变化
message.value = 'World' // Vue自动更新p标签内容
3. 模板语法
插值语法({{ }})
简单理解: 就像模板填空题
html
<!-- 文本插值 -->
<p>Hello {{ name }}!</p>
<!-- JavaScript表达式 -->
<p>{{ number + 1 }}</p>
<p>{{ ok ? 'YES' : 'NO' }}</p>
生活例子:
- 就像写信:"亲爱的{{ 收件人姓名 }},..."
- Vue会自动把{{ }}里的内容替换成实际值
指令基础
指令 = 给HTML元素的"特殊指令"
1. v-bind - 绑定属性
html
<!-- 传统HTML -->
<img src="固定路径.jpg">
<!-- Vue方式 -->
<img v-bind:src="动态路径">
<!-- 简写 -->
<img :src="动态路径">
2. v-on - 事件监听
html
<!-- 传统方式 -->
<button onclick="doSomething()">点击</button>
<!-- Vue方式 -->
<button v-on:click="doSomething">点击</button>
<!-- 简写 -->
<button @click="doSomething">点击</button>
3. v-if - 条件渲染
html
<p v-if="isVisible">只有条件为真才显示</p>
4. v-for - 列表渲染
html
<li v-for="item in list" :key="item.id">
{{ item.name }}
</li>
5. v-model - 双向绑定
html
<input v-model="message">
<p>{{ message }}</p> <!-- 输入框和文字会同步变化 -->
记忆技巧:
v-bind
= 单向传递(数据→界面)v-model
= 双向同步(数据↔界面)v-on
= 监听事件v-if
= 条件显示v-for
= 循环列表
4. 组件系统
什么是组件?
简单理解: 组件是可复用的 Vue 实例,就像乐高积木块
生活例子:
- 汽车 = 引擎组件 + 轮胎组件 + 座椅组件
- 网页 = 头部组件 + 导航组件 + 内容组件 + 底部组件
组件的基本结构
html
<template>
<!-- 组件的HTML结构(身体) -->
<div class="my-component">
<h2>{{ title }}</h2>
<button @click="handleClick">点击</button>
</div>
</template>
<script setup>
// 组件的逻辑(大脑)
import { ref } from 'vue'
const title = ref('我是一个组件')
function handleClick() {
title.value = '被点击了!'
}
</script>
<style scoped>
/* 组件的样式(衣服) */
.my-component {
padding: 20px;
border: 1px solid #ccc;
}
</style>
组件通信
1. Props - 父传子 简单理解: 就像父母给孩子零花钱
html
<!-- 父组件 -->
<ChildComponent :money="100" :message="'好好学习'" />
<!-- 子组件 -->
<script setup>
// 接收props
defineProps({
money: Number,
message: String
})
</script>
2. Emit - 子传父 简单理解: 就像孩子向父母报告
html
<!-- 子组件 -->
<script setup>
const emit = defineEmits(['report'])
function reportToParent() {
emit('report', '我考了100分!')
}
</script>
<!-- 父组件 -->
<ChildComponent @report="handleReport" />
3. 插槽 (Slots) - 内容分发 简单理解: 就像预留的空位
html
<!-- 组件定义 -->
<template>
<div class="card">
<h3>{{ title }}</h3>
<slot></slot> <!-- 这里是预留空位 -->
</div>
</template>
<!-- 使用组件 -->
<Card title="用户信息">
<p>这些内容会插入到slot位置</p>
</Card>
5. 生命周期
什么是生命周期?
简单理解: 组件从出生到死亡的整个过程,就像人的生命周期
生活例子:
js
婴儿出生 → 上学 → 工作 → 结婚 → 退休 → 去世
↓ ↓ ↓ ↓ ↓ ↓
created → mounted → updated → ... → unmounted
主要生命周期钩子
选项式 API:
js
export default {
// 1. 组件创建前
beforeCreate() {
console.log('组件还没出生')
},
// 2. 组件创建完成
created() {
console.log('组件出生了,可以发送请求获取数据')
},
// 3. 组件挂载前
beforeMount() {
console.log('准备上学(挂载到页面)')
},
// 4. 组件挂载完成
mounted() {
console.log('已经在学校了(页面上可见)')
},
// 5. 组件更新前
beforeUpdate() {
console.log('准备考试(数据要变化)')
},
// 6. 组件更新完成
updated() {
console.log('考试结束(页面已更新)')
},
// 7. 组件销毁前
beforeUnmount() {
console.log('准备毕业(组件要被销毁)')
},
// 8. 组件销毁完成
unmounted() {
console.log('已经毕业(组件被销毁)')
}
}
组合式 API:
js
import { onMounted, onUpdated, onUnmounted } from 'vue'
export default {
setup() {
onMounted(() => {
console.log('组件挂载完成')
})
onUpdated(() => {
console.log('组件更新完成')
})
onUnmounted(() => {
console.log('组件即将销毁')
})
}
}
实际应用场景:
- created/setup:发送HTTP请求获取数据
- mounted:操作DOM元素、初始化第三方库
- updated:数据变化后的额外处理
- unmounted:清理定时器、解绑事件
第二部分:Vue.js 进阶特性
6. 指令系统
内置指令详解
1. v-show vs v-if
v-if:真正的条件渲染
html
<p v-if="isVisible">我可能存在也可能不存在</p>
- 特点 :条件为假时,元素完全不存在
- 类比:房子要么盖起来,要么根本不盖
v-show:显示/隐藏
html
<p v-show="isVisible">我总是存在,只是可能被隐藏</p>
- 特点 :元素总是存在,只是CSS隐藏
- 类比:房子盖好了,只是拉上窗帘
选择建议:
- 频繁切换 → 用
v-show
- 很少切换 → 用
v-if
2. v-for 列表渲染
html
<!-- 遍历数组 -->
<li v-for="(item, index) in fruits" :key="item.id">
{{ index }} - {{ item.name }}
</li>
<!-- 遍历对象 -->
<li v-for="(value, key) in userInfo" :key="key">
{{ key }}: {{ value }}
</li>
<!-- 遍历数字 -->
<span v-for="n in 10" :key="n">{{ n }}</span>
重要:为什么需要 key?
- 类比:给每个学生一个学号,方便老师识别
- Vue用key来识别每个元素,提高更新效率
3. v-model 双向绑定
html
<!-- 文本输入 -->
<input v-model="message">
<!-- 复选框 -->
<input type="checkbox" v-model="checked">
<!-- 单选框 -->
<input type="radio" value="A" v-model="picked">
<input type="radio" value="B" v-model="picked">
<!-- 选择框 -->
<select v-model="selected">
<option value="A">选项A</option>
<option value="B">选项B</option>
</select>
自定义指令
简单理解: 给HTML元素添加自定义超能力
js
// 自动聚焦指令
app.directive('focus', {
mounted(el) {
el.focus()
}
})
// 使用
<input v-focus>
实际例子:
js
// 点击外部关闭指令
app.directive('click-outside', {
mounted(el, binding) {
el.clickOutsideEvent = function(event) {
if (!(el === event.target || el.contains(event.target))) {
binding.value()
}
}
document.addEventListener('click', el.clickOutsideEvent)
},
unmounted(el) {
document.removeEventListener('click', el.clickOutsideEvent)
}
})
// 使用
<div v-click-outside="closeModal">点击外部关闭</div>
7. API 风格对比
Options API vs Composition API
选项式 API(Options API) 选项式 API 以"组件实例"的概念为中心
特点: 像传统食谱,分类明确
js
export default {
data() {
return {
count: 0,
message: 'Hello'
}
},
methods: {
increment() {
this.count++
}
},
computed: {
doubleCount() {
return this.count * 2
}
},
mounted() {
console.log('组件挂载完成')
}
}
优点:
- 结构清晰,初学者友好
- 强制代码组织,不容易乱
- 类似面向对象编程思维
组合式 API(Composition API) 组合式 API 的核心思想是直接在函数作用域内定义响应式状态变量
特点: 像自由式烹饪,更灵活
js
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
// 状态
const count = ref(0)
const message = ref('Hello')
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
function increment() {
count.value++
}
// 生命周期
onMounted(() => {
console.log('组件挂载完成')
})
return {
count,
message,
doubleCount,
increment
}
}
}
优点:
- 更灵活的代码组织
- 更好的TypeScript支持
- 更容易抽取和复用逻辑
更简洁的写法:
js
<script setup>
import { ref, computed, onMounted } from 'vue'
// 直接声明,自动暴露给模板
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
onMounted(() => {
console.log('组件挂载完成')
})
</script>
如何选择?
学习阶段:
- 新手 → Options API(结构清晰)
- 有经验 → Composition API(更强大)
项目选择:
- 简单项目 → Options API
- 复杂项目、需要TypeScript → Composition API
8. 单文件组件(SFC)
什么是SFC?
Vue 的单文件组件会将一个组件的逻辑 (JavaScript),模板 (HTML) 和样式 (CSS) 封装在同一个文件里
简单理解: 把相关的东西放在一起,就像个人档案袋
- 成绩单(模板)
- 简历(逻辑)
- 照片(样式)
SFC的结构
js
<template>
<!-- 模板部分:组件的HTML结构 -->
<div class="my-component">
<h1>{{ title }}</h1>
<button @click="changeTitle">改变标题</button>
</div>
</template>
<script>
// 脚本部分:组件的逻辑
export default {
data() {
return {
title: '我是标题'
}
},
methods: {
changeTitle() {
this.title = '标题被改变了!'
}
}
}
</script>
<style scoped>
/* 样式部分:组件的CSS */
.my-component {
padding: 20px;
background-color: #f0f0f0;
}
h1 {
color: blue;
}
</style>
样式作用域
scoped 样式:
css
<style scoped>
/* 只影响当前组件 */
.title {
color: red;
}
</style>
全局样式:
css
<style>
/* 影响整个应用 */
.global-class {
font-size: 16px;
}
</style>
CSS Modules:
js
<template>
<div :class="$style.wrapper">
<h1 :class="$style.title">标题</h1>
</div>
</template>
<style module>
.wrapper {
padding: 20px;
}
.title {
color: blue;
}
</style>
预处理器支持
使用 Sass:
js
<style lang="scss" scoped>
$primary-color: #007bff;
.my-component {
background-color: $primary-color;
.title {
color: white;
&:hover {
opacity: 0.8;
}
}
}
</style>
使用 TypeScript:
js
<script setup lang="ts">
interface User {
id: number
name: string
}
const user: User = {
id: 1,
name: 'John'
}
</script>
9. 组合式 API 深入
ref 和 reactive 的选择
ref:基本类型和单一值
js
import { ref } from 'vue'
const count = ref(0)
const message = ref('Hello')
const isVisible = ref(true)
// 使用时需要 .value
count.value++
console.log(message.value)
reactive:对象和复杂数据
js
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: 'John',
age: 25
}
})
// 直接使用,无需 .value
state.count++
state.user.name = 'Jane'
选择原则:
- 基本类型(string, number, boolean)→
ref
- 对象、数组 →
reactive
或ref
计算属性 computed
简单理解: Excel中的公式单元格,依赖变化自动重新计算
js
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// 计算属性:自动拼接姓名
const fullName = computed(() => {
return firstName.value + lastName.value
})
// 可写计算属性
const fullNameWritable = computed({
get() {
return firstName.value + lastName.value
},
set(value) {
[firstName.value, lastName.value] = value.split('')
}
})
侦听器 watch
简单理解: 数据的保镖,数据一变化就执行动作
js
import { ref, watch } from 'vue'
const count = ref(0)
const message = ref('Hello')
// 侦听单个值
watch(count, (newValue, oldValue) => {
console.log(`count从 ${oldValue} 变为 ${newValue}`)
})
// 侦听多个值
watch([count, message], ([newCount, newMessage], [oldCount, oldMessage]) => {
console.log('count或message发生了变化')
})
// 立即执行
watch(count, (newValue) => {
console.log('立即执行,当前值:', newValue)
}, { immediate: true })
// 深度监听
const user = ref({ name: 'John', age: 25 })
watch(user, (newUser) => {
console.log('user对象发生了变化')
}, { deep: true })
watchEffect
简单理解: 自动侦听所有用到的响应式数据
js
import { ref, watchEffect } from 'vue'
const count = ref(0)
const message = ref('Hello')
// 自动侦听函数内用到的所有响应式数据
watchEffect(() => {
console.log(`count是${count.value},message是${message.value}`)
// 当count或message变化时,这个函数会自动重新执行
})
组合函数(Composables)
简单理解: 把相关逻辑打包成可重用的函数
js
// composables/useCounter.js
import { ref } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
function increment() {
count.value++
}
function decrement() {
count.value--
}
function reset() {
count.value = initialValue
}
return {
count,
increment,
decrement,
reset
}
}
// 在组件中使用
<script setup>
import { useCounter } from '@/composables/useCounter'
const { count, increment, decrement, reset } = useCounter(10)
</script>
常见的组合函数:
js
// useLocalStorage - 持久化存储
export function useLocalStorage(key, defaultValue) {
const storedValue = localStorage.getItem(key)
const value = ref(storedValue ? JSON.parse(storedValue) : defaultValue)
watch(value, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
return value
}
// useFetch - 数据获取
export function useFetch(url) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
async function fetchData() {
loading.value = true
try {
const response = await fetch(url)
data.value = await response.json()
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
fetchData()
return { data, loading, error, refetch: fetchData }
}
第三部分:Vue.js 生态系统
10. 路由管理 Vue Router
什么是路由?
简单理解: 网站的导航系统,就像商场的楼层指引
生活例子:
- 你想去商场的不同楼层(页面)
- 电梯/楼梯(路由器)带你到指定楼层
- 楼层指示牌(URL)告诉你当前位置
基本使用
1. 安装和配置
js
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
2. 在main.js中使用
js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
3. 在组件中使用
js
<template>
<div>
<!-- 导航链接 -->
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<!-- 页面内容显示区 -->
<router-view></router-view>
</div>
</template>
动态路由
路径参数:
js
// 路由配置
{
path: '/user/:id',
component: User
}
// 组件中获取参数
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // 获取用户ID
</script>
查询参数:
scss
// URL: /search?keyword=vue&page=1
const route = useRoute()
console.log(route.query.keyword) // 'vue'
console.log(route.query.page) // '1'
编程式导航
js
import { useRouter } from 'vue-router'
const router = useRouter()
// 跳转到指定路径
router.push('/about')
// 跳转并传参
router.push({ name: 'User', params: { id: 123 } })
// 后退
router.back()
// 前进
router.forward()
// 替换当前页面(不会产生历史记录)
router.replace('/login')
路由守卫
简单理解: 路口的保安,检查是否允许通过
js
// 全局前置守卫
router.beforeEach((to, from, next) => {
// 检查是否需要登录
if (to.meta.requiresAuth && !isLoggedIn) {
next('/login') // 重定向到登录页
} else {
next() // 允许通过
}
})
// 路由配置中的元信息
{
path: '/admin',
component: Admin,
meta: { requiresAuth: true }
}
嵌套路由
简单理解: 页面中的子页面
js
// 路由配置
{
path: '/user',
component: User,
children: [
{
path: 'profile', // /user/profile
component: UserProfile
},
{
path: 'settings', // /user/settings
component: UserSettings
}
]
}
js
<!-- User.vue -->
<template>
<div>
<h2>用户中心</h2>
<router-link to="/user/profile">个人资料</router-link>
<router-link to="/user/settings">账户设置</router-link>
<!-- 子页面显示区 -->
<router-view></router-view>
</div>
</template>
11. 状态管理 Vuex vs Pinia
为什么需要状态管理?
简单理解: 多个组件需要共享数据时的解决方案
生活例子:
- 没有状态管理:每个房间都有自己的温度计,互不相通
- 有状态管理:整栋楼有一个中央控制系统,各个房间共享信息
Vuex(传统方案)
核心概念:
State(状态) → 数据仓库
Getters(获取器) → 数据的计算属性
Mutations(变更) → 同步修改数据的方法
Actions(动作) → 异步操作和业务逻辑
基本使用:
js
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
count: 0,
user: null
},
getters: {
doubleCount: state => state.count * 2
},
mutations: {
INCREMENT(state) {
state.count++
},
SET_USER(state, user) {
state.user = user
}
},
actions: {
async login({ commit }, credentials) {
const user = await api.login(credentials)
commit('SET_USER', user)
}
}
})
在组件中使用:
js
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
// 获取状态
const count = computed(() => store.state.count)
const doubleCount = computed(() => store.getters.doubleCount)
// 修改状态
function increment() {
store.commit('INCREMENT')
}
// 异步操作
function login() {
store.dispatch('login', { username: 'admin', password: '123' })
}
return { count, doubleCount, increment, login }
}
}
Pinia(现代推荐)
与 Vuex 相比,Pinia 不仅提供了一个更简单的 API,也提供了符合组合式 API 风格的 API
特点:
- 更简洁的API
- 更好的TypeScript支持
- 没有mutations,只有actions
- 支持多个store
基本使用:
js
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
// 状态
state: () => ({
count: 0
}),
// 计算属性
getters: {
doubleCount: (state) => state.count * 2
},
// 方法(支持同步和异步)
actions: {
increment() {
this.count++
},
async fetchCount() {
const result = await api.getCount()
this.count = result
}
}
})
组合式API风格的Store:
js
// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
// 状态
const count = ref(0)
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
在组件中使用:
js
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
export default {
setup() {
const store = useCounterStore()
// 保持响应性的解构
const { count, doubleCount } = storeToRefs(store)
// 方法可以直接解构
const { increment } = store
return { count, doubleCount, increment }
}
}
Pinia 高级特性
1. 持久化存储
js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
userInfo: null
}),
actions: {
login(credentials) {
// 登录逻辑
}
},
// 持久化配置
persist: {
storage: localStorage,
paths: ['token', 'userInfo']
}
})
2. Store组合
js
// stores/auth.js
export const useAuthStore = defineStore('auth', () => {
const userStore = useUserStore() // 使用其他store
function logout() {
userStore.clearUser()
// 其他登出逻辑
}
return { logout }
})
Vuex vs Pinia 对比
特性 | Vuex | Pinia |
---|---|---|
API复杂度 | 复杂(4个概念) | 简单(3个概念) |
TypeScript支持 | 一般 | 优秀 |
代码分割 | 需要modules | 天然支持 |
开发体验 | 较复杂 | 更直观 |
文件大小 | 较大 | 更小 |
选择建议:
- 新项目 → Pinia
- Vue 3项目 → Pinia
- 需要强TypeScript支持 → Pinia
- 老项目维护 → 继续使用Vuex
12. 构建工具 Vite
什么是Vite?
简单理解: 超快的项目构建工具 ,就像高速列车
传统构建工具(Webpack):
- 像普通火车,停很多站,速度慢
- 需要打包所有文件才能启动
Vite:
- 像高铁,直达目标,速度快
- 按需加载,启动秒级
主要特性
1. 极速的开发服务器启动
perl
# 创建Vite项目
npm create vue@latest my-project
cd my-project
npm install
npm run dev # 秒级启动!
2. 闪电般的热更新(HMR)
- 修改代码后,页面几乎瞬间更新
- 保持应用状态,不会丢失数据
3. 原生ES模块支持
- 开发时直接使用ES模块
- 生产环境自动优化打包
项目结构
js
my-vue-project/
├── public/ # 静态资源
├── src/
│ ├── assets/ # 源码资源
│ ├── components/ # 组件
│ ├── views/ # 页面
│ ├── router/ # 路由
│ ├── stores/ # 状态管理
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── index.html # HTML模板
├── vite.config.js # Vite配置
└── package.json # 项目配置
Vite配置
js
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
// 路径别名
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
},
// 开发服务器配置
server: {
port: 3000,
open: true, // 自动打开浏览器
cors: true // 允许跨域
},
// 构建配置
build: {
outDir: 'dist',
sourcemap: true
}
})
环境变量
创建环境文件:
js
# .env.development(开发环境)
VITE_API_URL=http://localhost:3000/api
VITE_APP_TITLE=我的应用(开发)
# .env.production(生产环境)
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=我的应用
在代码中使用:
arduino
// 只有以VITE_开头的变量才会暴露给客户端
console.log(import.meta.env.VITE_API_URL)
console.log(import.meta.env.VITE_APP_TITLE)
第四部分:实战应用
13. 项目结构设计
标准项目结构
js
vue-project/
├── public/ # 静态资源(不会被构建处理)
│ ├── favicon.ico
│ └── index.html
├── src/
│ ├── assets/ # 静态资源(会被构建处理)
│ │ ├── images/
│ │ ├── styles/
│ │ └── fonts/
│ ├── components/ # 公共组件
│ │ ├── common/ # 通用组件
│ │ └── ui/ # UI组件
│ ├── views/ # 页面组件
│ │ ├── Home/
│ │ ├── About/
│ │ └── User/
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── stores/ # 状态管理
│ │ ├── modules/
│ │ └── index.js
│ ├── composables/ # 组合函数
│ │ ├── useAuth.js
│ │ └── useApi.js
│ ├── utils/ # 工具函数
│ │ ├── request.js
│ │ └── helpers.js
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── tests/ # 测试文件
├── vite.config.js # Vite配置
├── package.json
└── README.md
组件命名规范
文件命名:
- 组件文件:PascalCase(大驼峰)
- 例如:
MyComponent.vue
、UserProfile.vue
组件使用:
xml
<!-- 在模板中使用kebab-case -->
<user-profile />
<my-component />
<!-- 或者使用PascalCase -->
<UserProfile />
<MyComponent />
代码组织原则
1. 单一职责原则
js
<!-- ❌ 不好的例子:一个组件做太多事 -->
<template>
<div>
<!-- 用户信息 -->
<!-- 订单列表 -->
<!-- 支付模块 -->
<!-- 评论系统 -->
</div>
</template>
<!-- ✅ 好的例子:拆分成多个组件 -->
<template>
<div>
<UserInfo />
<OrderList />
<PaymentModule />
<CommentSystem />
</div>
</template>
2. 组件层级
js
页面组件(Views)
└── 业务组件(Business Components)
└── 基础组件(Base Components)
└── UI组件(UI Components)
API接口管理
js
// utils/request.js - 请求封装
import axios from 'axios'
const request = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000
})
// 请求拦截器
request.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 响应拦截器
request.interceptors.response.use(
response => response.data,
error => {
console.error('请求错误:', error)
return Promise.reject(error)
}
)
export default request
js
// api/user.js - API接口
import request from '@/utils/request'
export const userApi = {
// 获取用户信息
getUserInfo(id) {
return request.get(`/users/${id}`)
},
// 更新用户信息
updateUser(id, data) {
return request.put(`/users/${id}`, data)
},
// 用户登录
login(credentials) {
return request.post('/auth/login', credentials)
}
}
14. 最佳实践
性能优化
1. 组件懒加载
js
// 路由懒加载
const Home = () => import('@/views/Home.vue')
const About = () => import('@/views/About.vue')
// 组件懒加载
<script setup>
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() =>
import('@/components/HeavyComponent.vue')
)
</script>
2. v-memo 优化列表渲染
js
<template>
<div v-for="item in list" :key="item.id" v-memo="[item.id, item.selected]">
<!-- 只有id或selected变化时才重新渲染 -->
{{ item.name }}
</div>
</template>
3. computed 代替复杂表达式
js
<!-- ❌ 不好的做法 -->
<template>
<div>{{ users.filter(u => u.active).map(u => u.name).join(', ') }}</div>
</template>
<!-- ✅ 好的做法 -->
<template>
<div>{{ activeUserNames }}</div>
</template>
<script setup>
const activeUserNames = computed(() =>
users.filter(u => u.active).map(u => u.name).join(', ')
)
</script>
代码规范
1. 组件Props定义
js
// ✅ 好的Props定义
defineProps({
title: {
type: String,
required: true
},
count: {
type: Number,
default: 0
},
options: {
type: Array,
default: () => []
}
})
2. 事件命名
js
<!-- ✅ 动词-名词格式 -->
<MyComponent
@submit-form="handleSubmit"
@update-user="handleUserUpdate"
@delete-item="handleDelete"
/>
3. 样式组织
css
<style scoped>
/* 组件根元素 */
.my-component {
padding: 20px;
}
/* 子元素 */
.my-component__header {
margin-bottom: 16px;
}
.my-component__content {
line-height: 1.6;
}
/* 修饰符 */
.my-component--large {
padding: 40px;
}
.my-component--dark {
background-color: #333;
color: white;
}
</style>
错误处理
1. 全局错误处理
js
// main.js
const app = createApp(App)
app.config.errorHandler = (error, instance, info) => {
console.error('全局错误:', error)
console.error('错误信息:', info)
// 发送错误报告到监控系统
}
2. 异步错误处理
js
// composables/useAsyncData.js
export function useAsyncData(asyncFunction) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
async function execute() {
loading.value = true
error.value = null
try {
data.value = await asyncFunction()
} catch (err) {
error.value = err
console.error('异步操作失败:', err)
} finally {
loading.value = false
}
}
return { data, loading, error, execute }
}
测试策略
1. 单元测试
javascript
// tests/components/MyButton.test.js
import { mount } from '@vue/test-utils'
import MyButton from '@/components/MyButton.vue'
describe('MyButton', () => {
test('renders correctly', () => {
const wrapper = mount(MyButton, {
props: { text: 'Click me' }
})
expect(wrapper.text()).toBe('Click me')
})
test('emits click event', async () => {
const wrapper = mount(MyButton)
await wrapper.trigger('click')
expect(wrapper.emitted('click')).toBeTruthy()
})
})
🎯 费曼测试题
第一层:概念理解
- 用一句话解释什么是Vue.js?
- 响应式系统解决了什么问题?
- 组件和HTML元素有什么区别?
第二层:原理解释
- 为什么Vue能自动更新界面?
- Props和Emit是如何工作的?
- Computed和Methods有什么区别?
第三层:实际应用
- 什么时候用Options API,什么时候用Composition API?
- 如何设计一个组件的API?
- 什么情况下需要状态管理?
第四层:问题解决
- 如果列表渲染性能很慢,怎么优化?
- 组件之间如何通信最合适?
- 如何组织大型项目的代码结构?
📝 总结:Vue.js的核心理念
记住这个公式:
声明式 + 响应式 + 组件化 = Vue.js
核心思想:
- 关注状态,而非DOM操作
- 声明结果,而非描述过程
- 组合功能,而非继承复杂性
学习心态:
- 先理解概念,再学习语法
- 多写代码,少背文档
- 遇到问题先思考原理
- 从简单项目开始实践
记住: 如果你能用简单的话向别人解释Vue.js的概念,说明你真的理解了!
🎉 恭喜你完成了Vue.js全栈知识点的费曼学习法指南!现在开始动手实践吧!