最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue?3跨組件傳參之非父子層級(jí)通信解決方案

 更新時(shí)間:2025年11月03日 11:44:55   作者:Cherry?Zack  
在Vue3組件開(kāi)發(fā)中,組件之間的數(shù)據(jù)傳遞是一個(gè)非常重要的功能,這篇文章主要介紹了Vue?3跨組件傳參之非父子層級(jí)通信解決方案,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

在現(xiàn)代前端開(kāi)發(fā)中,組件化架構(gòu)已經(jīng)成為標(biāo)準(zhǔn)實(shí)踐。Vue 3 作為當(dāng)前最流行的前端框架之一,提供了多種優(yōu)雅的跨組件通信方案。特別是當(dāng)組件之間不存在直接的父子關(guān)系時(shí),如何高效、可靠地傳遞數(shù)據(jù)就成為了每個(gè) Vue 開(kāi)發(fā)者必須掌握的技能

本文詳細(xì)介紹 Vue 3中 5種常用的非父子組件傳參方式,幫助你在不同場(chǎng)景下選擇最合適的解決方案

一、Proviod / Inject - 官方推薦的跨層級(jí)方案

Proviod / Inject 是Vue 3 官方推薦的跨層級(jí)傳參方式,特別適用于祖先組件向任意后代組件(不限層級(jí)深度)傳遞數(shù)據(jù)的場(chǎng)景

基本原理:這種方式基于依賴注入的設(shè)計(jì)模式

① Provide:祖先組件提供數(shù)據(jù)

② Inject:后代組件注入并使用數(shù)據(jù)

步驟1:祖先組件提供數(shù)據(jù)

<!-- ParentComponent.vue -->
<script setup>
import { provide, ref, reactive } from 'vue'

// 提供響應(yīng)式數(shù)據(jù)
const user = ref({ 
  name: 'Alice', 
  age: 25,
  email: 'alice@example.com'
})

const theme = reactive({
  color: 'blue',
  fontSize: '16px'
})

// 使用provide提供數(shù)據(jù)
provide('user', user)
provide('theme', theme)
provide('appTitle', '我的應(yīng)用') // 也可以提供非響應(yīng)式數(shù)據(jù)
</script>

<template>
  <div>
    <h2>祖先組件</h2>
    <p>用戶名:{{ user.name }}</p>
    <ChildComponent />
  </div>
</template>

步驟2:后代組件注入數(shù)據(jù)

<!-- GrandChildComponent.vue -->
<script setup>
import { inject } from 'vue'

// 注入數(shù)據(jù)
const user = inject('user')
const theme = inject('theme')
const appTitle = inject('appTitle')

// 可以直接修改響應(yīng)式數(shù)據(jù)
const updateUserName = (newName) => {
  user.value.name = newName
}

const changeTheme = () => {
  theme.color = theme.color === 'blue' ? 'red' : 'blue'
}
</script>

<template>
  <div :style="{ color: theme.color, fontSize: theme.fontSize }">
    <h3>{{ appTitle }}</h3>
    <p>用戶信息:{{ user.name }} ({{ user.age }}歲)</p>
    <button @click="updateUserName('Bob')">修改用戶名</button>
    <button @click="changeTheme">切換主題</button>
  </div>
</template>

核心特點(diǎn):

① 響應(yīng)式傳遞:直接傳遞 ref 或 reactive 對(duì)象即可保持響應(yīng)性

② 無(wú)層級(jí)限制:無(wú)論組件嵌套多深,都可以輕松獲取數(shù)據(jù)

③ 類型安全:配合 TypeScript 可以實(shí)現(xiàn)完整的類型檢查

二、Pinia - 現(xiàn)代化的全局狀態(tài)管理

Pinia 是 Vue 3 官方推薦的狀態(tài)管理庫(kù),它替代了 Vuex,提供了更簡(jiǎn)潔的 API 和更好的 TypeScript 支持

為什么選擇 Pinia?

① 更簡(jiǎn)潔的 API 設(shè)計(jì)                        ② 更好的 TypeScript 集成

③ 完善的開(kāi)發(fā)工具支持                     ④ 模塊化的 store 設(shè)計(jì)

步驟1:安裝Pinia

npm install pinia
# 或
yarn add pinia

步驟2:創(chuàng)建 Pinia 實(shí)例并注冊(cè)

// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
const pinia = createPinia()

app.use(pinia)
app.mount('#app')

步驟3:定義 Store

// stores/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  // 狀態(tài)
  state: () => ({
    name: 'Alice',
    age: 25,
    email: 'alice@example.com',
    isLoggedIn: false
  }),
  
  // 計(jì)算屬性
  getters: {
    fullInfo: (state) => `${state.name} (${state.age}歲)`,
    isAdult: (state) => state.age >= 18
  },
  
  // 方法
  actions: {
    updateName(newName) {
      this.name = newName
    },
    
    updateAge(newAge) {
      if (newAge >= 0) {
        this.age = newAge
      }
    },
    
    login(email, password) {
      // 模擬API調(diào)用
      return new Promise((resolve) => {
        setTimeout(() => {
          this.isLoggedIn = true
          this.email = email
          resolve(true)
        }, 1000)
      })
    },
    
    logout() {
      this.isLoggedIn = false
      this.email = ''
    }
  }
})

步驟4:在組件中使用 Store

<!-- UserProfile.vue -->
<script setup>
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'

// 獲取store實(shí)例
const userStore = useUserStore()

// 使用storeToRefs保持響應(yīng)性
const { name, age, fullInfo, isLoggedIn } = storeToRefs(userStore)

// 直接解構(gòu)actions
const { updateName, updateAge, login, logout } = userStore

// 組件方法
const handleLogin = async () => {
  const success = await login('alice@example.com', 'password123')
  if (success) {
    console.log('登錄成功')
  }
}
</script>

<template>
  <div class="user-profile">
    <h2>用戶資料</h2>
    
    <div v-if="isLoggedIn">
      <p>歡迎,{{ fullInfo }}</p>
      <p>郵箱:{{ userStore.email }}</p>
      
      <div>
        <label>修改用戶名:</label>
        <input v-model="name" />
        <button @click="updateName(name)">保存</button>
      </div>
      
      <div>
        <label>修改年齡:</label>
        <input v-model.number="age" type="number" />
        <button @click="updateAge(age)">保存</button>
      </div>
      
      <button @click="logout">退出登錄</button>
    </div>
    
    <div v-else>
      <button @click="handleLogin">登錄</button>
    </div>
  </div>
</template>

高級(jí)特性

模塊化 Store

可以根據(jù)功能模塊創(chuàng)建多個(gè) store:

// stores/cart.js
export const useCartStore = defineStore('cart', {
  state: () => ({
    items: [],
    total: 0
  }),
  actions: {
    addItem(product) {
      this.items.push(product)
      this.calculateTotal()
    },
    calculateTotal() {
      this.total = this.items.reduce((sum, item) => sum + item.price, 0)
    }
  }
})

Store 組合使用

<script setup>
import { useUserStore } from '@/stores/user'
import { useCartStore } from '@/stores/cart'

const userStore = useUserStore()
const cartStore = useCartStore()

const checkout = async () => {
  if (!userStore.isLoggedIn) {
    await userStore.login()
  }
  // 執(zhí)行結(jié)賬邏輯
  console.log('結(jié)賬商品:', cartStore.items)
}
</script>

三、mitt - 輕量級(jí)事件總線

mitt 是一個(gè)輕量級(jí)的事件總線庫(kù),適用于簡(jiǎn)單的組件間通信場(chǎng)景

為什么選擇 mitt?

① 體積小巧        ② API 簡(jiǎn)潔易用        ③ 良好的 TypeScript        ④ 高性能

步驟1:安裝 mitt

npm install mitt
# 或
yarn add mitt

步驟2:創(chuàng)建事件總線實(shí)例

// utils/eventBus.js
import mitt from 'mitt'

// 創(chuàng)建事件總線實(shí)例
const eventBus = mitt()

export default eventBus

步驟3:發(fā)送事件

<!-- ComponentA.vue -->
<script setup>
import eventBus from '@/utils/eventBus'

const sendMessage = () => {
  // 發(fā)送事件
  eventBus.emit('message', {
    text: 'Hello from Component A',
    timestamp: new Date()
  })
}

const updateUser = () => {
  // 發(fā)送帶參數(shù)的事件
  eventBus.emit('user:update', {
    name: 'New Name',
    age: 30
  })
}
</script>

<template>
  <div>
    <h3>組件A</h3>
    <button @click="sendMessage">發(fā)送消息</button>
    <button @click="updateUser">更新用戶</button>
  </div>
</template>

步驟4:接收事件

<!-- ComponentB.vue -->
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import eventBus from '@/utils/eventBus'

const messages = ref([])
const userName = ref('')

// 事件處理函數(shù)
const handleMessage = (data) => {
  messages.value.push(data)
}

const handleUserUpdate = (userData) => {
  userName.value = userData.name
}

// 組件掛載時(shí)注冊(cè)事件監(jiān)聽(tīng)
onMounted(() => {
  eventBus.on('message', handleMessage)
  eventBus.on('user:update', handleUserUpdate)
})

// 組件卸載時(shí)移除事件監(jiān)聽(tīng)
onUnmounted(() => {
  eventBus.off('message', handleMessage)
  eventBus.off('user:update', handleUserUpdate)
})
</script>

<template>
  <div>
    <h3>組件B</h3>
    <p>當(dāng)前用戶:{{ userName }}</p>
    <div v-for="msg in messages" :key="msg.timestamp">
      <p>{{ msg.text }} - {{ msg.timestamp.toLocaleTimeString() }}</p>
    </div>
  </div>
</template>

高級(jí)用法:

事件命名規(guī)范

建議使用命名空間來(lái)組織事件

// 好的命名方式
eventBus.emit('user:created', userData)
eventBus.emit('user:updated', userData)
eventBus.emit('user:deleted', userId)

eventBus.emit('order:placed', orderData)
eventBus.emit('order:cancelled', orderId)

一次性事件

// 只監(jiān)聽(tīng)一次事件
eventBus.once('message', (data) => {
  console.log('只執(zhí)行一次:', data)
})

清楚所有事件

// 清除所有事件監(jiān)聽(tīng)
eventBus.all.clear()

四、全局屬性 - 工具類的全局共享

Vue 3 提供了app.config.globalProperties 來(lái)注冊(cè)全局屬性,適用于工具類和配置的全局共享

步驟1:注冊(cè)全局屬性

// main.js
import { createApp } from 'vue'
import axios from 'axios'
import App from './App.vue'

const app = createApp(App)

// 注冊(cè)全局HTTP客戶端
app.config.globalProperties.$http = axios.create({
  baseURL: 'https://api.example.com'
})

// 注冊(cè)全局工具函數(shù)
app.config.globalProperties.$formatDate = (date) => {
  return new Intl.DateTimeFormat('zh-CN').format(date)
}

// 注冊(cè)全局配置
app.config.globalProperties.$appConfig = {
  apiUrl: 'https://api.example.com',
  version: '1.0.0',
  theme: 'dark'
}

app.mount('#app')

步驟2:在組件中使用全局屬性

<!-- AnyComponent.vue -->
<script setup>
import { getCurrentInstance } from 'vue'

// 獲取當(dāng)前組件實(shí)例
const instance = getCurrentInstance()

// 通過(guò)proxy訪問(wèn)全局屬性
const $http = instance?.proxy?.$http
const $formatDate = instance?.proxy?.$formatDate
const $appConfig = instance?.proxy?.$appConfig

// 使用全局HTTP客戶端
const fetchData = async () => {
  try {
    const response = await $http.get('/users')
    console.log('用戶數(shù)據(jù):', response.data)
  } catch (error) {
    console.error('請(qǐng)求失?。?, error)
  }
}

// 使用全局工具函數(shù)
const formattedDate = $formatDate(new Date())

// 使用全局配置
console.log('API地址:', $appConfig.apiUrl)
console.log('應(yīng)用版本:', $appConfig.version)
</script>

<template>
  <div>
    <h3>全局屬性示例</h3>
    <p>當(dāng)前時(shí)間:{{ formattedDate }}</p>
    <p>應(yīng)用版本:{{ $appConfig.version }}</p>
    <button @click="fetchData">獲取數(shù)據(jù)</button>
  </div>
</template>

TypeScript 支持

為了在 TypeScript 中獲得類型提示,需要擴(kuò)展 Vue 的類型定義:

// shims-vue.d.ts
import { AxiosInstance } from 'axios'

declare module 'vue' {
  interface ComponentCustomProperties {
    $http: AxiosInstance
    $formatDate: (date: Date) => string
    $appConfig: {
      apiUrl: string
      version: string
      theme: string
    }
  }
}

五、瀏覽器存儲(chǔ) - 持久化數(shù)據(jù)方案

瀏覽器提供的localStorage和sessionStorage是實(shí)現(xiàn)數(shù)據(jù)持久化的簡(jiǎn)單方案

localStorage vs sessionStorage

特性

localStorage

sessionStorage

生命周期

永久存儲(chǔ),除非手動(dòng)清除

會(huì)話期間,頁(yè)面關(guān)閉后清除

存儲(chǔ)大小

約 5MB

約 5MB

作用域

同源所有頁(yè)面共享

僅當(dāng)前標(biāo)簽頁(yè)

網(wǎng)絡(luò)請(qǐng)求

會(huì)隨 HTTP 請(qǐng)求發(fā)送到服務(wù)器

不會(huì)隨 HTTP 請(qǐng)求發(fā)送

基礎(chǔ)使用示例:

<!-- StorageComponent.vue -->
<script setup>
import { ref, watch } from 'vue'

// 用戶設(shè)置
const userSettings = ref({
  theme: 'light',
  language: 'zh-CN',
  fontSize: '16px'
})

// 用戶登錄狀態(tài)
const user = ref({
  isLoggedIn: false,
  name: '',
  token: ''
})

// 初始化:從localStorage讀取數(shù)據(jù)
const initData = () => {
  try {
    // 讀取用戶設(shè)置
    const savedSettings = localStorage.getItem('userSettings')
    if (savedSettings) {
      userSettings.value = JSON.parse(savedSettings)
    }
    
    // 讀取用戶登錄狀態(tài)
    const savedUser = localStorage.getItem('user')
    if (savedUser) {
      user.value = JSON.parse(savedUser)
    }
  } catch (error) {
    console.error('讀取存儲(chǔ)數(shù)據(jù)失?。?, error)
  }
}

// 保存到localStorage
const saveToLocalStorage = () => {
  try {
    localStorage.setItem('userSettings', JSON.stringify(userSettings.value))
    localStorage.setItem('user', JSON.stringify(user.value))
  } catch (error) {
    console.error('保存數(shù)據(jù)失?。?, error)
  }
}

// 監(jiān)聽(tīng)數(shù)據(jù)變化,自動(dòng)保存
watch(userSettings, saveToLocalStorage, { deep: true })
watch(user, saveToLocalStorage, { deep: true })

// 登錄方法
const login = (username, password) => {
  // 模擬登錄API調(diào)用
  user.value = {
    isLoggedIn: true,
    name: username,
    token: 'fake-jwt-token'
  }
}

// 退出登錄
const logout = () => {
  user.value = {
    isLoggedIn: false,
    name: '',
    token: ''
  }
  // 可選:清除特定存儲(chǔ)
  localStorage.removeItem('user')
}

// 清除所有存儲(chǔ)
const clearAllStorage = () => {
  localStorage.clear()
  sessionStorage.clear()
  // 重置狀態(tài)
  userSettings.value = {
    theme: 'light',
    language: 'zh-CN',
    fontSize: '16px'
  }
  user.value = {
    isLoggedIn: false,
    name: '',
    token: ''
  }
}

// 初始化數(shù)據(jù)
initData()
</script>

<template>
  <div>
    <h3>瀏覽器存儲(chǔ)示例</h3>
    
    <div v-if="user.isLoggedIn">
      <p>歡迎,{{ user.name }}!</p>
      <button @click="logout">退出登錄</button>
    </div>
    
    <div v-else>
      <button @click="login('Alice', 'password')">登錄</button>
    </div>
    
    <div>
      <h4>用戶設(shè)置</h4>
      <div>
        <label>主題:</label>
        <select v-model="userSettings.theme">
          <option value="light">淺色</option>
          <option value="dark">深色</option>
        </select>
      </div>
      
      <div>
        <label>語(yǔ)言:</label>
        <select v-model="userSettings.language">
          <option value="zh-CN">中文</option>
          <option value="en-US">English</option>
        </select>
      </div>
    </div>
    
    <button @click="clearAllStorage" style="margin-top: 20px;">
      清除所有存儲(chǔ)
    </button>
  </div>
</template>

封裝存儲(chǔ)工具類

// utils/storage.js
class StorageUtil {
  // localStorage操作
  static setLocal(key, value) {
    try {
      localStorage.setItem(key, JSON.stringify(value))
    } catch (error) {
      console.error(`設(shè)置localStorage失敗 (${key}):`, error)
    }
  }

  static getLocal(key, defaultValue = null) {
    try {
      const value = localStorage.getItem(key)
      return value ? JSON.parse(value) : defaultValue
    } catch (error) {
      console.error(`獲取localStorage失敗 (${key}):`, error)
      return defaultValue
    }
  }

  static removeLocal(key) {
    try {
      localStorage.removeItem(key)
    } catch (error) {
      console.error(`刪除localStorage失敗 (${key}):`, error)
    }
  }

  // sessionStorage操作
  static setSession(key, value) {
    try {
      sessionStorage.setItem(key, JSON.stringify(value))
    } catch (error) {
      console.error(`設(shè)置sessionStorage失敗 (${key}):`, error)
    }
  }

  static getSession(key, defaultValue = null) {
    try {
      const value = sessionStorage.getItem(key)
      return value ? JSON.parse(value) : defaultValue
    } catch (error) {
      console.error(`獲取sessionStorage失敗 (${key}):`, error)
      return defaultValue
    }
  }

  static removeSession(key) {
    try {
      sessionStorage.removeItem(key)
    } catch (error) {
      console.error(`刪除sessionStorage失敗 (${key}):`, error)
    }
  }

  // 清除所有存儲(chǔ)
  static clearAll() {
    try {
      localStorage.clear()
      sessionStorage.clear()
    } catch (error) {
      console.error('清除存儲(chǔ)失敗:', error)
    }
  }
}

export default StorageUtil

響應(yīng)式封裝:

<!-- ReactiveStorage.vue -->
<script setup>
import { ref, watch } from 'vue'
import StorageUtil from '@/utils/storage'

// 創(chuàng)建響應(yīng)式存儲(chǔ)
const createReactiveStorage = (key, defaultValue, useSession = false) => {
  const data = ref(StorageUtil[useSession ? 'getSession' : 'getLocal'](key, defaultValue))
  
  // 監(jiān)聽(tīng)數(shù)據(jù)變化,自動(dòng)保存
  watch(data, (newValue) => {
    StorageUtil[useSession ? 'setSession' : 'setLocal'](key, newValue)
  }, { deep: true })
  
  return data
}

// 使用響應(yīng)式存儲(chǔ)
const user = createReactiveStorage('user', {
  isLoggedIn: false,
  name: ''
})

const settings = createReactiveStorage('settings', {
  theme: 'light'
})

// 會(huì)話級(jí)存儲(chǔ)
const tempData = createReactiveStorage('tempData', {}, true)
</script>

方案對(duì)比與選擇指南

方式

適用場(chǎng)景

響應(yīng)式

復(fù)雜度

推薦度

Provide/Inject

祖先→后代的跨層級(jí)傳遞

?

??

????

Pinia

任意組件的復(fù)雜狀態(tài)共享

?

???

?????

mitt 事件總線

簡(jiǎn)單的組件間通信

?

?

???

全局屬性

工具類和配置的全局共享

?

?

??

瀏覽器存儲(chǔ)

數(shù)據(jù)持久化

?

?

???

總結(jié) 

到此這篇關(guān)于Vue 3跨組件傳參之非父子層級(jí)通信解決方案的文章就介紹到這了,更多相關(guān)Vue3跨組件傳參非父子層級(jí)通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

车险| 绥芬河市| 青田县| 芮城县| 邻水| 桓仁| 普兰县| 女性| 宁都县| 永济市| 宁阳县| 绵竹市| 保亭| 惠东县| 天柱县| 双峰县| 连州市| 凤城市| 石河子市| 吉林市| 沧州市| 锡林郭勒盟| 东安县| 亳州市| 托里县| 郓城县| 灵川县| 乌鲁木齐县| 南宫市| 永和县| 涪陵区| 牟定县| 化隆| 邯郸市| 曲阳县| 永登县| 金山区| 尚志市| 四川省| 波密县| 雷波县|