欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

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

一、Proviod / Inject - 官方推薦的跨層級方案

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

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

① 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>

核心特點:

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

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

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

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

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

為什么選擇 Pinia?

① 更簡潔的 API 設(shè)計                        ② 更好的 TypeScript 集成

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

步驟1:安裝Pinia

npm install pinia
# 或
yarn add pinia

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

// 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
  }),
  
  // 計算屬性
  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實例
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>

高級特性

模塊化 Store

可以根據(jù)功能模塊創(chuàng)建多個 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 - 輕量級事件總線

mitt 是一個輕量級的事件總線庫,適用于簡單的組件間通信場景

為什么選擇 mitt?

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

步驟1:安裝 mitt

npm install mitt
# 或
yarn add mitt

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

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

// 創(chuàng)建事件總線實例
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
}

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

// 組件卸載時移除事件監(jiān)聽
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>

高級用法:

事件命名規(guī)范

建議使用命名空間來組織事件

// 好的命名方式
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)聽一次事件
eventBus.once('message', (data) => {
  console.log('只執(zhí)行一次:', data)
})

清楚所有事件

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

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

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

步驟1:注冊全局屬性

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

const app = createApp(App)

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

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

// 注冊全局配置
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)前組件實例
const instance = getCurrentInstance()

// 通過proxy訪問全局屬性
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('請求失敗:', 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)前時間:{{ formattedDate }}</p>
    <p>應(yīng)用版本:{{ $appConfig.version }}</p>
    <button @click="fetchData">獲取數(shù)據(jù)</button>
  </div>
</template>

TypeScript 支持

為了在 TypeScript 中獲得類型提示,需要擴展 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
    }
  }
}

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

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

localStorage vs sessionStorage

特性

localStorage

sessionStorage

生命周期

永久存儲,除非手動清除

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

存儲大小

約 5MB

約 5MB

作用域

同源所有頁面共享

僅當(dāng)前標簽頁

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

會隨 HTTP 請求發(fā)送到服務(wù)器

不會隨 HTTP 請求發(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('讀取存儲數(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)聽數(shù)據(jù)變化,自動保存
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: ''
  }
  // 可選:清除特定存儲
  localStorage.removeItem('user')
}

// 清除所有存儲
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>瀏覽器存儲示例</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>語言:</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;">
      清除所有存儲
    </button>
  </div>
</template>

封裝存儲工具類

// 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)
    }
  }

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

export default StorageUtil

響應(yīng)式封裝:

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

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

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

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

// 會話級存儲
const tempData = createReactiveStorage('tempData', {}, true)
</script>

方案對比與選擇指南

方式

適用場景

響應(yīng)式

復(fù)雜度

推薦度

Provide/Inject

祖先→后代的跨層級傳遞

?

??

????

Pinia

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

?

???

?????

mitt 事件總線

簡單的組件間通信

?

?

???

全局屬性

工具類和配置的全局共享

?

?

??

瀏覽器存儲

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

?

?

???

總結(jié) 

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

相關(guān)文章

  • Vue如何使用ElementUI對表單元素進行自定義校驗及踩坑

    Vue如何使用ElementUI對表單元素進行自定義校驗及踩坑

    有一些驗證不是通過input select這樣的受控組件來觸發(fā)驗證條件的 ,可以通過自定義驗證的方法來觸發(fā),下面這篇文章主要給大家介紹了關(guān)于Vue如何使用ElementUI對表單元素進行自定義校驗及踩坑的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • vue3中vue.config.js配置Element-plus組件和Icon圖標實現(xiàn)按需自動引入實例代碼

    vue3中vue.config.js配置Element-plus組件和Icon圖標實現(xiàn)按需自動引入實例代碼

    這篇文章主要給大家介紹了關(guān)于vue3中vue.config.js配置Element-plus組件和Icon圖標實現(xiàn)按需自動引入的相關(guān)資料,在Vue 3中可以通過配置vue.config.js文件來進行按需自動引入,需要的朋友可以參考下
    2024-02-02
  • 一文教你vue3 watch如何停止監(jiān)視

    一文教你vue3 watch如何停止監(jiān)視

    這篇文章主要為大家詳細介紹了vue3中watch如何停止監(jiān)視,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-12-12
  • element中form組件prop嵌套屬性的問題解決

    element中form組件prop嵌套屬性的問題解決

    本文主要介紹了element中form組件prop嵌套屬性的問題解決,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • vue-router中query和params的區(qū)別解析

    vue-router中query和params的區(qū)別解析

    vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,適合用于構(gòu)建單頁面應(yīng)用,這篇文章主要介紹了vue-router中query和params的區(qū)別 ,需要的朋友可以參考下
    2022-10-10
  • vue3無法使用jsx的問題及解決

    vue3無法使用jsx的問題及解決

    這篇文章主要介紹了vue3無法使用jsx的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue+vuex+json-seiver實現(xiàn)數(shù)據(jù)展示+分頁功能

    vue+vuex+json-seiver實現(xiàn)數(shù)據(jù)展示+分頁功能

    這篇文章主要介紹了vue+vuex+json-seiver實現(xiàn)數(shù)據(jù)展示+分頁功能,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-04-04
  • Vuex中actions的使用教程詳解

    Vuex中actions的使用教程詳解

    actions作為Vuex的五大核心之一,它的屬性是用來處理異步方法的,通過提交mutations實現(xiàn)。本文將具體介紹一下actions的使用教程,需要的可以參考一下
    2022-01-01
  • vue3集成Element-Plus之全局導(dǎo)入和按需導(dǎo)入

    vue3集成Element-Plus之全局導(dǎo)入和按需導(dǎo)入

    這篇文章主要給大家介紹了關(guān)于vue3集成Element-Plus之全局導(dǎo)入和按需導(dǎo)入的相關(guān)資料,element-plus正是element-ui針對于vue3開發(fā)的一個UI組件庫,?它的使用方式和很多其他的組件庫是一樣的,需要的朋友可以參考下
    2023-07-07
  • Vue?Router?實現(xiàn)登錄后跳轉(zhuǎn)到之前想要訪問的頁面

    Vue?Router?實現(xiàn)登錄后跳轉(zhuǎn)到之前想要訪問的頁面

    這篇文章主要介紹了Vue?Router?實現(xiàn)登錄后跳轉(zhuǎn)到之前相要訪問的頁面,本文僅演示路由跳轉(zhuǎn)和導(dǎo)航守衛(wèi)相關(guān)代碼的實現(xiàn),不包含具體的權(quán)限驗證和登錄請求,需要的朋友可以參考下
    2022-12-12

最新評論