Vue笔记(最新)

Vue笔记(最新)

一、背景与问题

在现代前端开发中,Vue.js 已成为主流框架之一。其核心优势在于响应式数据绑定和组件化开发模式,但随着项目规模扩大,开发者常面临以下问题:

  1. 响应性失效:手动修改数组时无法触发视图更新
  2. 组件通信复杂:父子组件通信、兄弟组件通信、跨层级通信的解决方案选择
  3. 性能瓶颈:大型项目中频繁的 DOM 更新导致性能下降
  4. 状态管理混乱:多组件共享状态时容易产生数据耦合

本文将深入解析 Vue 的核心机制,结合真实开发场景,给出可复用的解决方案。

二、基本原理

1. 响应式系统原理

Vue 通过 Proxy 实现响应式系统(Vue 3)或 Object.defineProperty(Vue 2),核心原理如下:

// Vue 3 响应式系统核心代码
function createReactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      return Reflect.get(target, key);
    },
    set(target, key, value) {
      const oldValue = target[key];
      const newValue = value;
      // 触发更新逻辑
      return Reflect.set(target, key, value);
    }
  });
}

关键点

  • Proxy 可拦截所有属性访问和修改
  • Vue 3 使用 Reflect 保持兼容性
  • 通过 Dep 依赖收集和 Watcher 观察者模式实现响应式更新

2. 虚拟 DOM 工作机制

Vue 的虚拟 DOM 采用 diff 算法实现高效更新:

function diff(oldVNode, newVNode) {
  // 1. 全等匹配直接返回
  if (oldVNode === newVNode) return;
  
  // 2. 类型不同直接替换
  if (oldVNode.tagName !== newVNode.tagName) {
    return createNewVNode(newVNode);
  }
  
  // 3. 属性更新
  if (oldVNode.attrs !== newVNode.attrs) {
    updateAttrs(oldVNode, newVNode);
  }
  
  // 4. 子节点递归比较
  if (oldVNode.children !== newVNode.children) {
    diffChildren(oldVNode, newVNode);
  }
}

性能优化点

  • 只更新变化的部分
  • 使用 key 属性优化列表更新
  • 通过 v-once 防止重复渲染

3. 组件系统设计

Vue 的组件系统通过 defineProps/defineEmits 实现:

<script setup>
const props = defineProps({
  message: {
    type: String,
    required: true
  }
});

const emit = defineEmits(['update']);

function handleUpdate() {
  emit('update', 'New message');
}
</script>

<template>
  <div @click="handleUpdate">{{ props.message }}</div>
</template>

核心机制

  • props 通过 Proxy 实现响应式
  • events 通过 event bus 传递
  • 组件实例通过 vnode 管理生命周期

三、环境准备

# 安装 Vue CLI
npm install -g @vue/cli

# 创建新项目
vue create vue-project

# 进入项目目录
cd vue-project

# 安装依赖
npm install

建议项目结构:

src/
├── components/          # 组件目录
├── views/               # 页面目录
├── stores/              # 状态管理
├── services/            # 业务逻辑
├── utils/               # 工具函数
├── App.vue              # 根组件
└── main.js              # 入口文件

四、核心实现

1. 响应式数据绑定示例

<template>
  <div>
    <p>当前计数:{{ count }}</p>
    <button @click="increment">+1</button>
    <button @click="reset">重置</button>
  </div>
</template>

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

const count = ref(0);

function increment() {
  count.value++;
}

function reset() {
  count.value = 0;
}
</script>

关键代码解释

  • ref 创建响应式变量
  • count.value 是访问值的唯一方式
  • 修改 value 会触发视图更新

2. 组件通信示例

<!-- ParentComponent.vue -->
<template>
  <ChildComponent :message="parentMessage" @update="handleUpdate" />
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const parentMessage = ref('Hello from parent');
const handleUpdate = (newMessage) => {
  parentMessage.value = newMessage;
};
</script>
<!-- ChildComponent.vue -->
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="updateMessage">更新消息</button>
  </div>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue';

const props = defineProps({
  message: {
    type: String,
    required: true
  }
});

const emit = defineEmits(['update']);

function updateMessage() {
  const newMessage = 'Hello from child';
  emit('update', newMessage);
}
</script>

通信机制

  • 父组件通过 props 传递数据
  • 子组件通过 emit 触发事件
  • 事件处理函数在父组件中定义

3. 路由导航示例

// main.js
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import Home from './views/Home.vue';
import About from './views/About.vue';

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About }
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

createApp(App).use(router).mount('#app');
<!-- App.vue -->
<template>
  <router-view />
</template>

路由机制

  • 使用 createRouter 创建路由实例
  • createWebHistory 支持 HTML5 历史模式
  • router-view 动态渲染匹配的组件

五、完整案例

待办事项管理应用

项目结构

src/
├── components/
│   └── TodoList.vue
├── views/
│   ├── Home.vue
│   └── Settings.vue
├── stores/
│   └── todos.js
├── services/
│   └── api.js
├── App.vue
└── main.js

核心代码

<!-- src/views/Home.vue -->
<template>
  <div>
    <TodoList :todos="todos" @delete="deleteTodo" />
    <div>
      <input v-model="newTodo" placeholder="输入新任务" />
      <button @click="addTodo">添加</button>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import TodoList from '../components/TodoList.vue';
import { useTodosStore } from '../stores/todos';

const todos = useTodosStore();
const newTodo = ref('');

function addTodo() {
  if (newTodo.value.trim()) {
    todos.addTodo(newTodo.value);
    newTodo.value = '';
  }
}

function deleteTodo(id) {
  todos.deleteTodo(id);
}
</script>
// src/stores/todos.js
import { ref } from 'vue';

export const useTodosStore = () => {
  const todos = ref([
    { id: 1, text: '学习 Vue' },
    { id: 2, text: '完成项目' }
  ]);

  function addTodo(text) {
    todos.value.push({
      id: Date.now(),
      text
    });
  }

  function deleteTodo(id) {
    todos.value = todos.value.filter(todo => todo.id !== id);
  }

  return { todos, addTodo, deleteTodo };
};

关键点

  • 使用 ref 管理状态
  • 组件间通过 store 共享状态
  • 通过 @delete 事件触发删除操作

六、源码解析

1. Vue 3 响应式系统源码

// src/core/observer/index.js
export function createReactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      // 依赖收集
      track(target, key);
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      const oldValue = target[key];
      const newValue = value;
      
      // 触发更新
      trigger(target, key, oldValue, newValue);
      return Reflect.set(target, key, value, receiver);
    }
  });
}

关键函数

  • track:收集依赖(Dep)
  • trigger:触发更新(Watcher)
  • Dep 类管理依赖关系

2. 虚拟 DOM 构建过程

// src/core/vdom/createVNode.js
function createVNode(type, props, children) {
  const vnode = {
    type,
    props,
    children,
    key: props?.key,
    // 其他属性
  };
  
  // 创建组件实例
  if (typeof type === 'function') {
    const instance = new type();
    vnode.component = instance;
  }
  
  return vnode;
}

构建流程

  1. 通过 createVNode 创建虚拟节点
  2. 通过 render 函数生成真实 DOM
  3. 通过 diff 算法进行更新

七、进阶使用

1. 自定义指令

// directives.js
export default {
  'v-focus': {
    mounted(el) {
      el.focus();
    }
  }
}
<!-- 使用自定义指令 -->
<input v-focus />

2. 状态管理方案比较

方案适用场景优点缺点
Vuex大型应用状态集中管理配置复杂
Pinia中小型项目简单易用功能较 Vuex 简单
LocalStorage简单缓存无需额外依赖无法响应式更新

推荐方案

  • 新项目优先使用 Pinia
  • 复杂项目使用 Vuex + modules
  • 需要持久化存储时结合 LocalStorage

3. 路由高级用法

// 路由配置
const routes = [
  {
    path: '/user/:id',
    component: UserComponent,
    props: (route) => ({
      userId: route.params.id
    })
  }
];

动态路由

  • 使用 params 提取动态参数
  • 通过 props 将参数传递给组件
  • 支持嵌套路由和命名路由

八、性能与工程实践

1. 性能优化技巧

关键优化点

优化策略说明示例
v-once防止重复渲染
{{ data }}
v-memo按 key 缓存组件
keep-alive缓存组件状态
避免深层克隆减少不必要的计算使用 Object.freeze() 防止修改

实际应用

  • 大型列表使用 v-memo 缓存渲染
  • 搜索功能使用 v-once 防止重复渲染
  • 常用组件使用 keep-alive 缓存

2. 安全风险防范

常见风险

风险类型说明防范措施
XSS 攻击动态插入未过滤的 HTML使用 v-html 时过滤内容
跨站脚本非法数据注入使用 encodeURIComponent() 编码
跨站请求伪造未验证请求来源使用 CSRF token 验证

防范措施

  • 使用 v-html 时添加 sanitize 处理
  • 对用户输入进行严格校验
  • 使用 Content Security Policy (CSP) 防止注入攻击

九、常见问题与踩坑

1. 常见错误示例

错误代码

// 错误:直接修改 props
function updateMessage() {
  this.message = 'New message'; // ❌ 错误写法
}

正确写法

function updateMessage() {
  this.$emit('update', 'New message'); // ✅ 正确写法
}

错误原因:直接修改 props 会破坏响应性

2. 常见坑点分析

坑点说明解决方案
数组更新无效使用 push/pop 等方法无法触发更新使用 Vue.setthis.$set
组件未渲染忘记调用 this.$nextTick在 DOM 更新后使用 nextTick
路由参数丢失未正确传递 props使用 props 配置项或 params
状态未更新忘记使用 value 访问响应式数据确保所有访问都通过 value

十、最佳实践

1. 推荐开发规范

  • 使用 script setup 语法
  • 保持组件单一职责
  • 使用 key 优化列表渲染
  • 避免过度使用 v-if/v-show
  • 使用 v-model 实现双向绑定

2. 推荐工具链

工具作用推荐版本
ESLint代码规范8.x
Prettier代码格式化3.x
Vite构建工具3.x
Vitest单元测试1.x
Storybook组件文档1.x

3. 推荐开发模式

  • 使用 Vue 3 的 Composition API
  • 结合 TypeScript 提升类型安全
  • 使用 Vue Router 4 实现路由管理
  • 使用 VuexPinia 管理全局状态
  • 使用 Vite 加速开发流程

十一、总结

Vue 的核心价值在于其响应式系统和组件化开发模式,但实际开发中需要关注:

  1. 响应性失效:通过 ref/reactive 正确管理响应式数据
  2. 组件通信:合理使用 props 和 events 实现父子通信
  3. 性能优化:通过 v-once/v-memo 等指令优化渲染性能
  4. 状态管理:根据项目规模选择合适的状态管理方案
  5. 安全风险:防范 XSS 攻击和数据注入

在实际项目中,建议:

  • 中小型项目使用 Pinia 管理状态
  • 大型项目使用 Vuex + modules
  • 使用 TypeScript 提升类型安全
  • 避免直接修改 props 和 array 原生方法
  • 通过 Vite 加速开发流程

通过深入理解 Vue 的核心机制,结合实际场景选择合适的解决方案,可以显著提升开发效率和项目质量。

VUE
最后修改于:2026年09月15日 19:35

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日