Vue.js 核心知识点

好的,我们深入探讨 Vue.js 的这些核心知识点。


1. 响应式原理:Vue 2 vs Vue 3

Vue 2: Object.defineProperty

Vue 2 通过遍历一个对象的所有属性,并使用 Object.defineProperty 将它们转换为 getter 和 setter 来实现响应式。

工作原理:

  1. 初始化:遍历 data 函数返回对象的所有属性。
  2. 递归转化:对每个属性使用 Object.defineProperty 设置 getter 和 setter。
  3. 依赖收集:在 getter 中,将正在计算的组件(Watcher)收集为这个属性的依赖。
  4. 触发更新:在 setter 中,当属性值变化时,通知所有收集到的依赖(Watcher)进行更新(重新渲染)。

缺点:

  1. 无法检测对象属性的添加或删除:因为 Object.defineProperty 是在初始化时针对已有属性设置的。需要使用 Vue.setVue.delete 来解决。
  2. 数组监听受限:无法直接监听数组索引的设置(arr[index] = newValue)和 length 的变化。Vue 2 通过重写数组的 7 个变异方法(push, pop, shift, unshift, splice, sort, reverse)来 hack 实现响应式。
  3. 性能瓶颈:初始化时需要递归遍历整个对象,对于大型对象,性能开销较大。

Vue 3: Proxy

Vue 3 使用 ES6 的 Proxy 来创建响应式对象。Proxy 可以拦截对象上的基本操作,功能远比 Object.defineProperty 强大。

工作原理:

  1. 创建代理:使用 reactive() 函数包裹一个对象,返回一个 Proxy 代理。
  2. 拦截操作:Proxy 可以拦截多达 13 种操作,包括属性的 get、set、delete、has 等。
  3. 依赖收集:在 get 拦截中收集依赖(副作用函数,如组件的渲染函数)。
  4. 触发更新:在 setdeleteProperty 拦截中,触发所有收集的依赖。

优点:

  1. 完美检测新增和删除属性:因为 Proxy 拦截的是对整个对象的操作,而不是特定属性。obj.newProperty = valuedelete obj.oldProperty 都能被捕获。
  2. 原生支持数组arr[index] = valuearr.length = newLength 都能被 set 拦截器捕获,无需 hack 数组方法。
  3. 更好的性能
    • Proxy 是“懒”处理的,只有在访问到某个深层属性时才会递归将其转化为 Proxy,减少了初始化开销。
    • 提供了更细粒度的拦截,为后续优化提供了空间(如编译时优化)。
  4. 支持更多数据结构Proxy 可以代理 Map, Set, WeakMap, WeakSet 等集合类型,而 Object.defineProperty 只能代理普通对象。

对比总结:

特性 Vue 2 (Object.defineProperty) Vue 3 (Proxy)
数据监听 对象已有属性 整个对象,动态属性(增删)也无压力
数组支持 需要重写方法 hack 原生完美支持
性能 初始化全量递归,性能较差 惰性递归,性能更优
数据结构 仅支持 Object、Array 支持所有 ES6+ 集合类型

2. Composition API vs Options API

Options API

Vue 2 的传统方式,通过一系列选项(data, methods, computed, watch, lifecycle hooks)来组织代码。

  • 优点:结构清晰,强制将代码按选项分类,对初学者友好。
  • 缺点
    • 逻辑关注点分离:一个功能的代码(如从API获取用户信息)可能被分散到 data, methods, mounted 等多个选项中,理解和维护复杂功能时,需要在代码中不断“跳跃”。
    • 逻辑复用困难:主要通过 mixins 实现,容易发生命名冲突,且来源不清晰。

Composition API

Vue 3 引入的新范式。它允许你使用导入的函数(如 ref, reactive, onMounted)来定义组件的逻辑,像一个普通的 JavaScript 函数一样自由组织代码。

  • 核心思想:将逻辑组合到组合式函数中,而不是按选项组织。
  • 优点
    1. 更好的逻辑组织和复用
      • 组织:可以将一个功能的所有相关代码(状态、计算属性、方法、生命周期)放在一起,代码内聚性极高。
      • 复用:逻辑可以轻松提取到“组合式函数”中,在多个组件间复用,且没有 mixin 的缺点。
    2. 更好的 TypeScript 支持:使用普通的变量和函数,类型推断非常自然和清晰。
    3. 更灵活的代码组织:代码不再被选项限制,可以像写普通函数一样自由组织。

示例对比:一个跟踪鼠标位置的功能

  • Options API (分散)

    // Vue 2 组件
    export default {
      data() {
        return {
          x: 0,
          y: 0
        }
      },
      methods: {
        updatePosition(e) {
          this.x = e.pageX;
          this.y = e.pageY;
        }
      },
      mounted() {
        window.addEventListener('mousemove', this.updatePosition);
      },
      beforeUnmount() { // Vue 3 是 unmounted
        window.removeEventListener('mousemove', this.updatePosition);
      }
    }
    
  • Composition API (内聚)

    // Vue 3 组件
    import { ref, onMounted, onUnmounted } from 'vue';
    
    export default {
      setup() {
        // 鼠标逻辑 - 所有相关代码都在一起!
        const x = ref(0);
        const y = ref(0);
        const updatePosition = (e) => {
          x.value = e.pageX;
          y.value = e.pageY;
        };
        onMounted(() => window.addEventListener('mousemove', updatePosition));
        onUnmounted(() => window.removeEventListener('mousemove', updatePosition));
    
        // 可以继续写其他逻辑...
        return { x, y }; // 模板中需要使用的数据
      }
    };
    

    甚至可以轻松地将其提取成一个可复用的组合式函数:

    // useMouse.js
    import { ref, onMounted, onUnmounted } from 'vue';
    export function useMouse() {
      const x = ref(0);
      const y = ref(0);
      const updatePosition = (e) => {
        x.value = e.pageX;
        y.value = e.pageY;
      };
      onMounted(() => window.addEventListener('mousemove', updatePosition));
      onUnmounted(() => window.removeEventListener('mousemove', updatePosition));
      return { x, y };
    }
    
    // 在组件中使用
    import { useMouse } from './useMouse';
    export default {
      setup() {
        const { x, y } = useMouse(); // 逻辑清晰且可复用!
        return { x, y };
      }
    };
    

3. 虚拟DOM与Diff算法

为什么要用VDOM?

直接操作真实DOM(如 document.createElement)的代价非常昂贵。频繁的DOM操作是网页性能瓶颈的主要原因。

VDOM是一个用来描述真实DOM的JavaScript对象。它的核心价值在于:

  1. 抽象:为开发者提供了一个声明式的API(如Vue的模板),无需直接操作DOM。
  2. 性能:通过Diff算法比较新旧VDOM的差异,然后批量、高效地将最小变化更新到真实DOM上,减少不必要的DOM操作。

Vue的Diff算法大致流程

Vue的Diff算法是同级比较、深度优先的。

  1. 同层比较:不会跨层级比较节点。如果发现同一层的节点类型不同(如从 div 变为 p),Vue会直接销毁旧节点及其子节点,创建新节点并插入。这是为了降低算法复杂度。
  2. Key的作用key 是节点的唯一标识。Diff算法通过 key 来精确判断两个节点是否是同一个。没有 key 时,Vue会使用“就地复用”策略,如果节点顺序发生变化,可能会错误地复用元素而不是移动它们,导致状态错乱。使用 key 可以帮助Vue高效地识别和重新排序元素,避免不必要的渲染和状态问题。
  3. 双端比较:Vue 2 的核心 Diff 算法借鉴了 snabbdom双端比较策略。它会同时比较新旧子节点数组的头头、尾尾、头尾、尾头四个位置,试图找到可复用的节点,最大程度减少移动操作。
  4. 静态提升 & Patch Flags (Vue 3优化):Vue 3 的编译器会做更多优化。
    • 静态提升:将静态节点提升到渲染函数之外,每次渲染时复用,避免重复创建VDOM。
    • Patch Flags:在编译时分析动态绑定的类型(如 class, style, text),在VDOM节点上打上标记。Diff时可以直接根据标记定位到需要对比的动态内容,跳过静态内容的对比,极大提升了Diff效率。

4. 生命周期

Vue 3 的生命周期与 Vue 2 大部分相似,但为了配合 Composition API,名称有细微变化,并提供了对应的组合式API函数。

Vue 2 Option Vue 3 Option (Options API) Vue 3 Composition API Hook 触发时机
beforeCreate beforeCreate Not Needed (Use setup()) 实例初始化后,数据观测和事件配置之前
created created Not Needed (Use setup()) 实例创建完成,数据观测已完成
beforeMount beforeMount onBeforeMount 挂载开始之前被调用
mounted mounted onMounted 实例被挂载后调用
beforeUpdate beforeUpdate onBeforeUpdate 数据更新时,虚拟DOM打补丁之前
updated updated onUpdated 数据更改导致虚拟DOM重新渲染后
beforeDestroy beforeUnmount onBeforeUnmount 实例销毁之前
destroyed unmounted onUnmounted 实例销毁后
errorCaptured errorCaptured onErrorCaptured 捕获到来自后代组件的错误时

setup() 函数是 Composition API 的入口,它运行在 beforeCreatecreated 之前。 在这些钩子中编写的任何代码都应该直接在 setup() 中编写。


5. 生态库

Vue Router

Vue.js 的官方路由管理器。

  • 原理:基于前端路由,监听 popstate (history模式) 或 hashchange (hash模式) 事件,动态匹配预先定义的路由配置,并渲染对应的组件。
  • 核心概念<router-view> (路由出口), <router-link> (导航), 路由配置 (routes), 编程式导航 (router.push), 导航守卫 (用于权限控制)。
  • 使用场景:构建单页面应用 (SPA),实现页面切换和导航。

Pinia / Vuex

状态管理库,用于集中管理跨组件的共享状态。

  • Vuex (Vue 2/3)

    • 核心概念
      • State:单一状态树,存储应用级状态。
      • Getters:基于 state 的计算属性。
      • Mutations同步修改 state 的唯一方式。
      • Actions:提交 mutations,可以包含异步操作。
    • 流程Component -> Dispatch(Action) -> Commit(Mutation) -> Mutate(State) -> Render(Component)
  • Pinia (Vue 3 推荐)

    • 可以看作是 Vuex 5,是下一代状态管理工具。
    • 优势
      1. 更简化的API:没有 mutations,只有 state, getters, actionsactions 既可以处理同步也可以处理异步。
      2. 完美的TS支持:API设计非常类型友好。
      3. Composition API 风格:定义 Store 的方式很像 setup()
      4. 模块化设计:默认就是多个 store,不再需要嵌套模块。
  • 使用场景:中大型应用,需要管理复杂的、多个组件共享的状态(如用户登录信息、购物车、全局配置等)。

posted @ 2025-10-10 13:37  阿木隆1237  阅读(13)  评论(0)    收藏  举报