阿里云主机折上折
  • 微信号
Current Site:Index > Sharing reactive state between components

Sharing reactive state between components

Author:Chuan Chen 阅读数:63582人阅读 分类: Vue.js

Sharing Reactive State Between Components

Vue3's reactivity system is based on Proxy. The core of sharing state between components lies in managing the reference relationships of reactive data. When reactive and ref create reactive objects that reference the same object across different components, state changes are automatically synchronized. This sharing mechanism is implemented through Vue's dependency tracking and update triggering system.

// store.js
import { reactive } from 'vue'
export const store = reactive({
  count: 0,
  user: { name: 'Alice' }
})

Implementation Principles of Shared State

Vue3's reactivity system intercepts object operations through Proxy. When a component accesses reactive properties, dependency relationships are established. The key to shared state lies in:

  1. All components reference the same Proxy object.
  2. Property access triggers track operations to record dependencies.
  3. Property modifications trigger trigger operations to notify updates.
// ComponentA.vue
import { store } from './store'
const increment = () => {
  store.count++ // Triggers updates in all components using store.count
}

Comparison of Different Sharing Methods

1. Props Drilling

The traditional method passes props layer by layer, suitable for shallow component trees:

// Parent.vue
<Child :count="count" />

// Child.vue
props: ['count']

2. Provide/Inject

The official solution for cross-level sharing:

// Ancestor component
import { provide, ref } from 'vue'
provide('count', ref(0))

// Descendant component
import { inject } from 'vue'
const count = inject('count')

3. Global State Management

Centralized state management provided by Pinia or Vuex:

// store/counter.js
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++
    }
  }
})

// Usage in components
const store = useCounterStore()
store.increment()

Considerations for Reactive Sharing

1. Destructuring Loses Reactivity

Direct destructuring breaks the Proxy's reactive connection:

const { count } = store // Wrong! count is no longer reactive
const count = computed(() => store.count) // Correct approach

2. Special Handling for Arrays and Collections

Vue overrides array methods to maintain reactivity:

const list = reactive([1, 2, 3])
list.push(4) // Maintains reactivity
list = [...list, 4] // Wrong! Reassignment loses reactivity

3. Performance Optimization Strategies

For large-scale state sharing, consider:

// Use shallowRef to reduce reactivity overhead
const largeList = shallowRef([]) 

// Use markRaw to skip reactivity conversion
const staticData = markRaw({...})

Underlying Implementation of the Reactivity System

The core of Vue3's reactivity system is in the @vue/reactivity package, with key implementations including:

  1. The targetMap structure for dependency tracking:
type TargetMap = WeakMap<object, DepsMap>
type DepsMap = Map<string | symbol, Dep>
type Dep = Set<ReactiveEffect>
  1. The execution flow for triggering updates:
// Simplified trigger implementation
function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  
  const effects = depsMap.get(key)
  effects && effects.forEach(effect => effect.run())
}

Best Practices for State Sharing

1. Encapsulation with Composable Functions

Encapsulate shared logic into reusable composable functions:

// useCounter.js
export function useCounter() {
  const count = ref(0)
  const double = computed(() => count.value * 2)
  
  function increment() {
    count.value++
  }
  
  return { count, double, increment }
}

2. Managing State with Custom Hooks

Create domain model hooks for complex scenarios:

// useUserStore.js
export function useUserStore() {
  const state = reactive({
    users: [],
    loading: false
  })
  
  async function fetchUsers() {
    state.loading = true
    state.users = await api.getUsers()
    state.loading = false
  }
  
  return { ...toRefs(state), fetchUsers }
}

3. State Partitioning Strategy

Divide state by feature modules in large applications:

// stores/
import { useAuthStore } from './auth'
import { useCartStore } from './cart'

export function useStore() {
  return {
    auth: useAuthStore(),
    cart: useCartStore()
  }
}

Edge Cases in Reactive Sharing

1. Cross-Window State Synchronization

Use BroadcastChannel for multi-tab synchronization:

const channel = new BroadcastChannel('app-state')
watch(store, (newValue) => {
  channel.postMessage(newValue)
})

channel.onmessage = (evt) => {
  Object.assign(store, evt.data)
}

2. Web Worker Communication

Serialization is required for sharing state with Workers:

// Main thread
const sharedState = useSharedState()
worker.postMessage({ type: 'STATE_UPDATE', payload: toRaw(sharedState) })

// Worker thread
self.onmessage = ({ data }) => {
  if (data.type === 'STATE_SYNC') {
    reactiveState = reactive(data.payload)
  }
}

3. SSR Environment Handling

Avoid state pollution in server-side rendering:

// Create state instances unique to each request
export function createAppState() {
  return reactive({ /*...*/ })
}

// In request handling
app.use((req, res) => {
  const state = createAppState()
  // ...rendering logic
})

Extension Mechanisms of the Reactivity System

Vue3 allows extending the behavior of the reactivity system:

1. Custom Refs

Implement refs with special logic:

function customRef(factory) {
  let value
  return {
    get() {
      track(this, 'value')
      return value
    },
    set(newVal) {
      value = factory(newVal)
      trigger(this, 'value')
    }
  }
}

2. Reactive Utility Functions

Create reactive utilities for specific scenarios:

function reactiveInterval(interval) {
  const counter = ref(0)
  const timer = setInterval(() => counter.value++, interval)
  
  onScopeDispose(() => clearInterval(timer))
  return counter
}

3. Deep Observation Control

Precisely control reactivity depth:

function shallowReactive(obj) {
  const observed = reactive({})
  Object.keys(obj).forEach(key => {
    observed[key] = obj[key]
  })
  return observed
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.