vue笔记

72 阅读9分钟

vue2与vue3的区别

  1. 响应式系统

(1)Vue2基于 Object.defineProperty实现,会在初始化时,把 data 中的每个属性用 Object.defineProperty 转换成 getter / setter,当属性被访问时触发 getter,依赖收集;当属性被修改时触发 setter,派发更新。

缺点:对象新增/删除属性时,要用vue.setvue.set、vue.delete;通过索引直接修改数组元素,无法触发更新;Vue 包装过的方法(pushsplice)才能响应式;初始化时会递归遍历整个对象,数据量大时性能很差。

(2)Vue3使用 ES6 Proxy 来拦截对象的操作,响应式转换在“访问时”才进行,而不是 Vue2 的“初始化时”。

  1. Options API 与 Composition API

Options API(选项式 API)使用 datamethodscomputedwatchmounted 等不同的选项组织代码。 Composition API 在 setup() 函数里,使用 refreactivecomputedwatch 等组合功能。

  1. Diff 算法

(1) 静态节点提升(Static Hoisting)

- 原理:编译时识别出完全静态的节点(如纯文本、无动态绑定的标签),将其标记为静态节点,并在编译阶段就从渲染函数中“提升”出去,只创建一次。
- 效果:后续更新时,diff 算法会直接跳过这些静态节点,无需对比,减少比对次数。
例: <div >静态文本</div >  这类节点不会参与每次更新的 diff 过程。

(2) 补丁标记(Patch Flags)

- 原理:编译时为动态节点添加“补丁标记”,标记该节点的动态内容类型(如动态文本、动态 class、动态 props 等)。
- 效果:diff 时,只需要根据标记检查节点的特定动态部分,无需对比整个节点的所有属性。
例: <div :class="activeClass" >内容</div >  会被标记为“动态 class”,更新时仅检查 class 是否变化,忽略其他静态内容。

(3) 列表 diff 优化(稳定序列复用)

- 针对场景: v-for  渲染的列表更新时,Vue2 的 diff 算法可能会对整个列表进行全量对比,效率较低。
- Vue3 优化:
- 当列表项的顺序变化但部分节点结构未变时,通过最长递增子序列算法,找出可以复用的稳定节点序列,减少节点的移动和重新创建。
- 配合  key ,更精准地定位节点的增删改位置,避免不必要的节点操作。

(4) 减少比对层级(Tree Flattening)

- 原理:Vue2 的 diff 是“全树递归比对”,即使父节点无变化,也会递归比对所有子节点。
- Vue3 优化:编译时将模板结构“扁平化”,将嵌套的子节点转换为一个线性数组,diff 时通过数组索引直接访问节点,减少递归层级,提升遍历效率。

  1. Fragments
  • Vue2:一个组件只能有一个根节点。

  • Vue3:支持返回多个根节点(Fragments),减少无意义的包裹 div

  1. 生命周期

setup 替代了 beforeCreate / created

  • 创建阶段beforeCreatecreated

  • 挂载阶段beforeMountmounted

  • 更新阶段beforeUpdateupdated

  • 销毁阶段beforeDestroydestroyed

vuex

核心概念

State(状态) Vuex 使用单一状态树,用一个对象包含全部的应用层级状态。每个应用仅包含一个 store 实例。

Getters(获取器) Vuex 允许在 store 中定义 getters,可以认为是 store 的计算属性。getters 的返回值会根据它的依赖被缓存起来。

Mutations(变更) 更改 Vuex store 中状态的唯一方法是提交 mutation。mutation 必须是同步函数,每个 mutation 都有一个字符串的事件类型和一个回调函数。

this.$store.commit('DECREMENT')

Actions(动作) Action 类似于 mutation,不同之处在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。

this.$store.dispatch('increment')

Modules(模块) Vuex 允许将 store 分割成模块,每个模块拥有自己的 state、mutation、action、getter。

computed: {
    ...mapState(['count']),
    ...mapGetters(['doubleCount'])
  },
  
  methods: {
    ...mapActions(['increment', 'decrement']),
    ...mapMutations(['INCREMENT']),
}

数据流

Vuex 的数据流是单向的:组件触发 Action → Action 提交 Mutation → Mutation 改变 State → State 变化触发视图更新。

主要特点

集中式管理:所有状态存储在一个地方,便于管理和调试。

可预测性:状态变化遵循严格的规则,使得状态变化可预测。

响应式:基于 Vue 的响应式系统,状态变化自动触发视图更新。

开发工具支持:配合 Vue DevTools 提供强大的调试功能,支持时间旅行调试。

适用场景

Vuex 适合中大型单页应用,特别是需要多个组件共享状态或状态变化逻辑复杂的场景。对于简单应用,可能会显得过于复杂,此时可以考虑使用简单的状态管理方案或 Vue 3 的 Composition API。

pinia

核心特性

简洁的 API Pinia 提供了更直观的 API,去除了 Vuex 中的 mutations 概念,actions 可以直接修改状态,使代码更简洁。

完整的 TypeScript 支持 从设计之初就考虑了 TypeScript,提供了完整的类型推导和类型安全,无需额外的类型声明。

模块化设计 每个 store 都是独立的,不需要嵌套结构,支持代码分割和懒加载。

Composition API 友好 与 Vue 3 的 Composition API 完美集成,支持在 setup 函数中直接使用。

轻量级 体积更小,运行时开销更低,性能更好。

主要概念

Store(存储) Pinia 中的 store 是一个保存状态和业务逻辑的实体,类似于组件。每个 store 都有 state、getters 和 actions。

State(状态) 存储数据的地方,是响应式的。可以通过 store 实例直接访问和修改。

Getters(获取器) 类似于计算属性,用于从 state 中派生出一些状态。getters 会被缓存,只有当依赖发生变化时才会重新计算。

Actions(动作) 用于定义业务逻辑,可以是同步或异步的。actions 可以直接修改状态,也可以调用其他 actions。

使用场景

定义一个 Store(store/counter.js)

// store/counter.js 或 counter.ts
import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'My Counter',
  }),
  getters: {
    doubleCount: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
    reset() {
      this.count = 0
    },
  },
})

在组件中使用 Store(如:App.vue)

<template>
  <div>
    <h1>{{ counter.name }}</h1>
    <p>Count: {{ counter.count }}</p>
    <p>Double: {{ counter.doubleCount }}</p>
    <button @click="counter.increment">+</button>
    <button @click="counter.reset">Reset</button>
  </div>
</template><script setup>
import { useCounterStore } from './store/counter'const counter = useCounterStore()
</script>

vuex与pinia对比

架构设计

Vuex 采用单一状态树模式,所有状态集中在一个 store 中,通过 modules 来组织代码,有严格的 mutations/actions 分离。

Pinia 采用多 store 模式,每个 store 都是独立的,更接近组合式 API 的设计理念。它没有 mutations 概念,actions 可以直接修改状态。

语法复杂度

Vuex 的语法相对复杂:

  • 需要定义 state、mutations、actions、getters
  • 修改状态必须通过 mutations
  • 异步操作需要在 actions 中处理
  • 大量的样板代码

Pinia 的语法更简洁:

  • 只需要定义 state、actions、getters
  • 可以直接修改状态或通过 actions
  • 支持组合式 API 风格的写法
  • 更少的样板代码

TypeScript 支持

Vuex 的 TypeScript 支持比较复杂,需要额外的类型定义和配置。

Pinia 原生支持 TypeScript,类型推断更好,开发体验更佳。

开发工具

Vuex 有成熟的 Vue DevTools 支持。

Pinia 也有很好的 DevTools 支持,并且提供了更直观的调试体验。

性能

Pinia 在性能上有优势,支持代码分割,只有使用到的 store 才会被加载。而 Vuex 需要在应用启动时加载整个状态树。

Vuex原理

1. 核心实现原理

1.1 响应式系统

Vuex利用Vue的响应式系统来实现状态的响应式更新:

// 核心响应式实现
class Store {
  constructor(options = {}) {
    // 使用Vue实例来实现响应式,利用Vue的响应式系统
    // 将state包装在Vue实例的data中,使其具备响应式能力
    this._vm = new Vue({
      data: {
        // 使用$开头表示这是内部属性,避免与用户数据冲突
        $state: options.state
      }
    })
  }
​
  // 通过getter暴露state,确保获取的是响应式的状态
  get state() {
    return this._vm._data.$state
  }
​
  // 禁止直接设置state,强制通过mutation来修改状态
  set state(v) {
    console.error('不能直接设置store的state')
  }
}

1.2 安装机制

通过Vue.mixin在所有组件中注入$store:

/**
 * Vuex安装函数 - 通过Vue.mixin在所有组件中注入$store
 * @param {Vue} Vue - Vue构造函数
 */
function install(Vue) {
  // 通过mixin的方式,在每个组件实例化前注入$store
  Vue.mixin({
    beforeCreate() {
      const options = this.$options
      // 根组件:直接从options中获取store实例
      if (options.store) {
        this.$store = options.store
      } 
      // 子组件:从父组件继承$store
      else if (options.parent && options.parent.$store) {
        this.$store = options.parent.$store
      }
    }
  })
}

1.3 Mutation实现

class Store {
  constructor(options) {
    // 创建空的mutations对象来存储所有mutation函数
    this._mutations = Object.create(null)
    
    // 注册mutations - 将用户定义的mutations包装成可执行的函数
    const mutations = options.mutations || {}
    Object.keys(mutations).forEach(key => {
      // 包装mutation函数,确保传入正确的参数(state和payload)
      this._mutations[key] = (payload) => {
        // 调用用户定义的mutation,传入当前状态和载荷
        mutations[key].call(this, this.state, payload)
      }
    })
  }
​
  /**
   * 提交mutation - 同步修改状态的唯一方式
   * @param {string} type - mutation的类型
   * @param {any} payload - 传递给mutation的数据
   */
  commit(type, payload) {
    // 根据类型获取对应的mutation函数
    const mutation = this._mutations[type]
    if (!mutation) {
      console.error(`未知的mutation类型: ${type}`)
      return
    }
    // 执行mutation,修改状态
    mutation(payload)
  }
}

1.4 Action实现

class Store {
  constructor(options) {
    // 创建空的actions对象来存储所有action函数
    this._actions = Object.create(null)
    
    // 注册actions - 处理异步操作的地方
    const actions = options.actions || {}
    Object.keys(actions).forEach(key => {
      // 包装action函数,提供context对象和payload参数
      this._actions[key] = (payload) => {
        // action函数接收context对象,包含commit、dispatch、state、getters
        return actions[key].call(this, {
          commit: this.commit.bind(this),     // 用于提交mutation
          dispatch: this.dispatch.bind(this), // 用于分发其他action
          state: this.state,                  // 当前状态
          getters: this.getters              // 所有getters
        }, payload)
      }
    })
  }
​
  /**
   * 分发action - 处理异步操作
   * @param {string} type - action的类型
   * @param {any} payload - 传递给action的数据
   * @return {Promise} 返回Promise以支持异步操作
   */
  dispatch(type, payload) {
    // 根据类型获取对应的action函数
    const action = this._actions[type]
    if (!action) {
      console.error(`未知的action类型: ${type}`)
      return
    }
    // 执行action,可能包含异步操作
    return action(payload)
  }
}

1.5 Getter实现

class Store {
  constructor(options) {
    // 初始化getters对象
    this.getters = {}
    const getters = options.getters || {}
    
    // 为每个getter创建属性描述符,实现计算属性的效果
    Object.keys(getters).forEach(key => {
      // 使用Object.defineProperty定义getter属性
      Object.defineProperty(this.getters, key, {
        // 每次访问时都会调用这个get函数
        get: () => {
          // 调用用户定义的getter函数,传入state和getters作为参数
          return getters[key](this.state, this.getters)
        }
      })
    })
  }
}

2. 完整的简化版实现

/**
 * 简化版Vuex Store实现
 * 包含完整的state管理、mutations、actions、getters功能
 */
class VuexStore {
  constructor(options = {}) {
    // 使用Vue实例实现响应式状态管理
    // Vue的响应式系统会自动追踪状态变化并更新相关视图
    this._vm = new Vue({
      data: {
        // 使用$前缀避免与用户数据冲突,这是Vue的内部约定
        $state: options.state || {}
      }
    })
​
    // 初始化存储对象
    this._mutations = Object.create(null)  // 存储所有mutation函数
    this._actions = Object.create(null)    // 存储所有action函数
    this.getters = {}                      // 存储所有getter
​
    // 注册mutations - 同步修改状态的唯一方式
    const mutations = options.mutations || {}
    Object.keys(mutations).forEach(key => {
      this._mutations[key] = (payload) => {
        // 确保mutation函数的this指向store实例
        // 传入当前状态和载荷数据
        mutations[key].call(this, this.state, payload)
      }
    })
​
    // 注册actions - 处理异步逻辑和复杂业务逻辑
    const actions = options.actions || {}
    Object.keys(actions).forEach(key => {
      this._actions[key] = (payload) => {
        // 构建context对象,提供action所需的所有API
        const context = {
          commit: this.commit.bind(this),     // 提交mutation
          dispatch: this.dispatch.bind(this), // 分发其他action
          state: this.state,                  // 当前状态(只读)
          getters: this.getters              // 所有getter(只读)
        }
        // 调用用户定义的action函数
        return actions[key].call(this, context, payload)
      }
    })
​
    // 注册getters - 从state派生出一些状态(类似计算属性)
    const getters = options.getters || {}
    Object.keys(getters).forEach(key => {
      // 使用属性描述符定义getter,实现懒计算和缓存
      Object.defineProperty(this.getters, key, {
        get: () => getters[key](this.state, this.getters),
        enumerable: true  // 可枚举,方便调试和遍历
      })
    })
​
    // 绑定方法的this上下文,防止在组件中解构使用时丢失this
    this.commit = this.commit.bind(this)
    this.dispatch = this.dispatch.bind(this)
  }
​
  // 状态的getter,提供对响应式状态的访问
  get state() {
    return this._vm._data.$state
  }
​
  /**
   * 提交mutation - 修改状态的唯一合法方式
   * @param {string} type - mutation类型
   * @param {any} payload - 传递的数据
   */
  commit(type, payload) {
    const mutation = this._mutations[type]
    if (!mutation) {
      console.error(`[vuex] unknown mutation type: ${type}`)
      return
    }
    // 执行mutation,同步修改状态
    mutation(payload)
  }
​
  /**
   * 分发action - 处理异步操作和业务逻辑
   * @param {string} type - action类型
   * @param {any} payload - 传递的数据
   * @return {Promise} 始终返回Promise以保证一致的异步接口
   */
  dispatch(type, payload) {
    const action = this._actions[type]
    if (!action) {
      console.error(`[vuex] unknown action type: ${type}`)
      return Promise.resolve()
    }
    // 将结果包装为Promise,确保返回值的一致性
    return Promise.resolve(action(payload))
  }
}
​
/**
 * Vuex安装函数 - 将Vuex集成到Vue应用中
 * @param {Vue} Vue - Vue构造函数
 */
function install(Vue) {
  // 防止重复安装
  if (install.installed) return
  install.installed = true
​
  // 通过全局mixin在所有组件中注入$store属性
  Vue.mixin({
    beforeCreate() {
      const options = this.$options
      // 根组件:从选项中获取store
      if (options.store) {
        this.$store = typeof options.store === 'function' 
          ? options.store()  // 支持工厂函数
          : options.store
      } 
      // 子组件:从父组件继承store
      else if (options.parent && options.parent.$store) {
        this.$store = options.parent.$store
      }
    }
  })
}
​
// 导出Vuex对象,包含Store类和install方法
const Vuex = {
  Store: VuexStore,
  install
}

3. 关键设计思想

3.1 单向数据流

  • View -> Action -> Mutation -> State -> View
  • 确保状态变更的可追踪性和可预测性

3.2 响应式更新

  • 利用Vue的响应式系统
  • 状态变更自动触发视图更新

3.3 严格的状态管理

  • 只能通过mutation改变状态
  • Action处理异步操作
  • 开发工具可追踪所有状态变更