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

Vue中跨頁(yè)面通信的8種主流方式詳解

 更新時(shí)間:2026年04月26日 09:19:45   作者:前端那點(diǎn)事  
在Vue項(xiàng)目開(kāi)發(fā)中,跨頁(yè)面通信是高頻需求,本文整理8種主流通信方式,每種方式附完整可運(yùn)行Demo、適配場(chǎng)景和注意事項(xiàng),覆蓋Vue2、Vue3所有項(xiàng)目場(chǎng)景,新手也能直接復(fù)制落地,快跟隨小編一起學(xué)習(xí)一下吧

在Vue項(xiàng)目開(kāi)發(fā)中,跨頁(yè)面通信(非父子組件、不同路由頁(yè)面間)是高頻需求,比如“列表頁(yè)跳轉(zhuǎn)詳情頁(yè)傳遞數(shù)據(jù)”“頁(yè)面A操作后同步更新頁(yè)面B內(nèi)容”。本文整理8種主流通信方式,按“常用度+實(shí)用性”排序,每種方式附完整可運(yùn)行Demo、適配場(chǎng)景和注意事項(xiàng),覆蓋Vue2、Vue3所有項(xiàng)目場(chǎng)景,新手也能直接復(fù)制落地。

一、路由參數(shù)傳遞(最常用,簡(jiǎn)單場(chǎng)景首選)

核心思路:通過(guò)路由跳轉(zhuǎn)時(shí)攜帶參數(shù),目標(biāo)頁(yè)面接收參數(shù),適合“頁(yè)面跳轉(zhuǎn)傳值”場(chǎng)景(如列表頁(yè)→詳情頁(yè)),分為「query參數(shù)」和「params參數(shù)」兩種,按需選擇。

query參數(shù)(路徑可見(jiàn),適合簡(jiǎn)單數(shù)據(jù))- 完整可運(yùn)行Demo

適配場(chǎng)景:列表頁(yè)跳轉(zhuǎn)詳情頁(yè),傳遞簡(jiǎn)單ID、名稱等數(shù)據(jù),刷新頁(yè)面不丟失。

前置準(zhǔn)備:已配置Vue Router(Vue2/Vue3均可,以下Demo分別提供兩種版本)。

Vue3 Demo(組合式API)

// 1. 路由配置(router/index.js)
import { createRouter, createWebHistory } from 'vue-router';
import PageA from '@/views/PageA.vue';
import PageB from '@/views/PageB.vue';
const routes = [
  { path: '/pageA', name: 'PageA', component: PageA },
  { path: '/pageB', name: 'PageB', component: PageB }
];
const router = createRouter({
  history: createWebHistory(),
  routes
});
export default router;
// 2. 頁(yè)面A(跳轉(zhuǎn)方,@/views/PageA.vue)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(query參數(shù)跳轉(zhuǎn))</h3>
    <!-- 方式1:router-link跳轉(zhuǎn) -->
    <router-link 
      :to="{ path: '/pageB', query: { id: 123, name: 'Vue跨頁(yè)面通信Demo' } }"
      class="btn"
    >
      點(diǎn)擊跳轉(zhuǎn)頁(yè)面B(router-link)
    </router-link>
    <!-- 方式2:編程式導(dǎo)航跳轉(zhuǎn) -->
    <button @click="goToPageB" class="btn">點(diǎn)擊跳轉(zhuǎn)頁(yè)面B(編程式)</button>
  </div>
</template>
<script setup>
import { useRouter } from 'vue-router';
// 編程式導(dǎo)航
const router = useRouter();
const goToPageB = () => {
  router.push({
    path: '/pageB',
    query: { id: 123, name: 'Vue跨頁(yè)面通信Demo' }
  });
};
</script>
<style scoped>
.btn { margin: 0 10px; padding: 6px 12px; cursor: pointer; }
</style>
// 3. 頁(yè)面B(接收方,@/views/PageB.vue)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(接收query參數(shù))</h3>
    <div>接收的ID:{{ id }}</div>
    <div>接收的名稱:{{ name }}</div>
    <!-- 返回頁(yè)面A -->
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>
<script setup>
import { useRoute } from 'vue-router';
import { ref } from 'vue';
const route = useRoute();
// 接收參數(shù)(query參數(shù)默認(rèn)是字符串,需手動(dòng)轉(zhuǎn)類型)
const id = ref(Number(route.query.id));
const name = ref(route.query.name);
// 監(jiān)聽(tīng)路由變化(若頁(yè)面不刷新,參數(shù)變化時(shí)同步更新)
watch(
  () => route.query,
  (newQuery) => {
    id.value = Number(newQuery.id);
    name.value = newQuery.name;
  },
  { immediate: true }
);
</script>
// 4. main.js(入口文件)
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');

Vue2 Demo(選項(xiàng)式API)

// 1. 路由配置(router/index.js)
import Vue from 'vue';
import Router from 'vue-router';
import PageA from '@/views/PageA';
import PageB from '@/views/PageB';

Vue.use(Router);

export default new Router({
  routes: [
    { path: '/pageA', name: 'PageA', component: PageA },
    { path: '/pageB', name: 'PageB', component: PageB }
  ]
});

// 2. 頁(yè)面A(跳轉(zhuǎn)方,@/views/PageA.vue)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(query參數(shù)跳轉(zhuǎn))</h3>
    <router-link 
      :to="{ path: '/pageB', query: { id: 123, name: 'Vue跨頁(yè)面通信Demo' } }"
      class="btn"
    >
      點(diǎn)擊跳轉(zhuǎn)頁(yè)面B(router-link)
    </router-link>
    <button @click="goToPageB" class="btn">點(diǎn)擊跳轉(zhuǎn)頁(yè)面B(編程式)</button>
  </div>
</template>

<script>
export default {
  methods: {
    goToPageB() {
      this.$router.push({
        path: '/pageB',
        query: { id: 123, name: 'Vue跨頁(yè)面通信Demo' }
      });
    }
  }
};
</script>

// 3. 頁(yè)面B(接收方,@/views/PageB.vue)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(接收query參數(shù))</h3>
    <div>接收的ID:{{ id }}</div>
    <div>接收的名稱:{{ name }}</div>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      id: '',
      name: ''
    };
  },
  mounted() {
    // 初始接收參數(shù)
    this.id = Number(this.$route.query.id);
    this.name = this.$route.query.name;
  },
  watch: {
    // 監(jiān)聽(tīng)路由變化,同步更新參數(shù)
    '$route.query': {
      handler(newQuery) {
        this.id = Number(newQuery.id);
        this.name = newQuery.name;
      },
      immediate: true
    }
  }
};
</script>

// 4. main.js(入口文件)
import Vue from 'vue';
import App from './App';
import router from './router';

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
});

params參數(shù)(路徑不可見(jiàn),適合敏感/復(fù)雜數(shù)據(jù))- 完整可運(yùn)行Demo

適配場(chǎng)景:傳遞敏感數(shù)據(jù)(如用戶隱私信息),路徑不可見(jiàn),需路由配置占位符避免刷新丟失。

Vue3 Demo(組合式API)

// 1. 路由配置(router/index.js,必須添加params占位符)
import { createRouter, createWebHistory } from 'vue-router';
import PageA from '@/views/PageA.vue';
import PageB from '@/views/PageB.vue';

const routes = [
  { path: '/pageA', name: 'PageA', component: PageA },
  // 配置params占位符::id、:name(與傳遞的參數(shù)名一致)
  { path: '/pageB/:id/:name', name: 'PageB', component: PageB }
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

export default router;

// 2. 頁(yè)面A(跳轉(zhuǎn)方,@/views/PageA.vue)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(params參數(shù)跳轉(zhuǎn))</h3>
    <!-- 注意:params參數(shù)必須用name跳轉(zhuǎn),不能用path -->
    <button @click="goToPageB" class="btn">點(diǎn)擊跳轉(zhuǎn)頁(yè)面B</button>
  </div>
</template>

<script setup>
import { useRouter } from 'vue-router';

const router = useRouter();
const goToPageB = () => {
  router.push({
    name: 'PageB', // 必須用name
    params: { id: 456, name: '敏感數(shù)據(jù)Demo' } // 傳遞params參數(shù)
  });
};
</script>

// 3. 頁(yè)面B(接收方,@/views/PageB.vue)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(接收params參數(shù))</h3>
    <div>接收的ID:{{ id }}</div>
    <div>接收的敏感名稱:{{ name }}</div>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script setup>
import { useRoute } from 'vue-router';
import { ref, watch } from 'vue';

const route = useRoute();
const id = ref(Number(route.params.id));
const name = ref(route.params.name);

// 監(jiān)聽(tīng)路由變化,同步更新參數(shù)
watch(
  () => route.params,
  (newParams) => {
    id.value = Number(newParams.id);
    name.value = newParams.name;
  },
  { immediate: true }
);
</script>

Vue2 Demo(選項(xiàng)式API)

// 1. 路由配置(router/index.js)
import Vue from 'vue';
import Router from 'vue-router';
import PageA from '@/views/PageA';
import PageB from '@/views/PageB';

Vue.use(Router);

export default new Router({
  routes: [
    { path: '/pageA', name: 'PageA', component: PageA },
    { path: '/pageB/:id/:name', name: 'PageB', component: PageB }
  ]
});

// 2. 頁(yè)面A(跳轉(zhuǎn)方,@/views/PageA.vue)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(params參數(shù)跳轉(zhuǎn))</h3>
    <button @click="goToPageB" class="btn">點(diǎn)擊跳轉(zhuǎn)頁(yè)面B</button>
  </div>
</template>

<script>
export default {
  methods: {
    goToPageB() {
      this.$router.push({
        name: 'PageB',
        params: { id: 456, name: '敏感數(shù)據(jù)Demo' }
      });
    }
  }
};
</script>

// 3. 頁(yè)面B(接收方,@/views/PageB.vue)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(接收params參數(shù))</h3>
    <div>接收的ID:{{ id }}</div>
    <div>接收的敏感名稱:{{ name }}</div>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      id: '',
      name: ''
    };
  },
  mounted() {
    this.id = Number(this.$route.params.id);
    this.name = this.$route.params.name;
  },
  watch: {
    '$route.params': {
      handler(newParams) {
        this.id = Number(newParams.id);
        this.name = newParams.name;
      },
      immediate: true
    }
  }
};
</script>

注意事項(xiàng)

  • query參數(shù):路徑可見(jiàn)(如/pageB?id=123),刷新頁(yè)面不丟失,適合傳遞簡(jiǎn)單數(shù)據(jù)(字符串、數(shù)字);
  • params參數(shù):路徑不可見(jiàn),刷新頁(yè)面會(huì)丟失(除非路由配置中寫(xiě)占位符),適合傳遞敏感、臨時(shí)數(shù)據(jù);
  • 傳遞復(fù)雜數(shù)據(jù)(如對(duì)象)時(shí),需先JSON.stringify轉(zhuǎn)字符串,接收時(shí)再JSON.parse解析(避免數(shù)據(jù)錯(cuò)亂)。

二、Vuex/Pinia(全局狀態(tài)管理,復(fù)雜場(chǎng)景首選)

核心思路:通過(guò)全局狀態(tài)倉(cāng)庫(kù)存儲(chǔ)數(shù)據(jù),所有頁(yè)面均可讀寫(xiě),適合“多頁(yè)面共享數(shù)據(jù)”場(chǎng)景(如用戶信息、全局配置、多頁(yè)面聯(lián)動(dòng)),Vue2常用Vuex,Vue3首選Pinia(更輕量、簡(jiǎn)潔)。

Pinia(Vue3首選)- 完整可運(yùn)行Demo

適配場(chǎng)景:多頁(yè)面共享用戶信息、全局計(jì)數(shù)器等,支持跨頁(yè)面實(shí)時(shí)聯(lián)動(dòng),無(wú)需手動(dòng)傳遞參數(shù)。

前置準(zhǔn)備:安裝Pinia依賴(npm install pinia)。

// 1. 創(chuàng)建Pinia實(shí)例并注冊(cè)(main.js)
import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia'; // 引入Pinia
import router from './router';

const app = createApp(App);
app.use(createPinia()); // 注冊(cè)Pinia
app.use(router);
app.mount('#app');

// 2. 創(chuàng)建全局倉(cāng)庫(kù)(store/modules/user.js)
import { defineStore } from 'pinia';

// 定義倉(cāng)庫(kù)(user為倉(cāng)庫(kù)唯一標(biāo)識(shí))
export const useUserStore = defineStore('user', {
  state: () => ({
    userInfo: { id: 1, name: '測(cè)試用戶', age: 20 }, // 全局共享數(shù)據(jù)
    count: 0 // 全局計(jì)數(shù)器(用于跨頁(yè)面聯(lián)動(dòng))
  }),
  actions: {
    // 修改用戶信息
    setUserInfo(info) {
      this.userInfo = info;
    },
    // 增加計(jì)數(shù)器
    addCount() {
      this.count++;
    },
    // 重置計(jì)數(shù)器
    resetCount() {
      this.count = 0;
    }
  }
});

// 3. 頁(yè)面A(修改全局?jǐn)?shù)據(jù),@/views/PageA.vue)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(修改Pinia全局?jǐn)?shù)據(jù))</h3>
    <div>當(dāng)前全局計(jì)數(shù)器:{{ userStore.count }}</div>
    <button @click="userStore.addCount" class="btn">計(jì)數(shù)器+1</button>
    <button @click="updateUserInfo" class="btn">修改用戶信息</button>
    <router-link to="/pageB" class="btn">跳轉(zhuǎn)到頁(yè)面B查看數(shù)據(jù)</router-link>
  </div>
</template>

<script setup>
import { useUserStore } from '@/store/modules/user';

// 引入并使用倉(cāng)庫(kù)
const userStore = useUserStore();

// 修改用戶信息
const updateUserInfo = () => {
  userStore.setUserInfo({
    id: 2,
    name: '新用戶',
    age: 22
  });
};
</script>

// 4. 頁(yè)面B(讀取/修改全局?jǐn)?shù)據(jù),@/views/PageB.vue)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(讀取Pinia全局?jǐn)?shù)據(jù))</h3>
    <div>用戶ID:{{ userStore.userInfo.id }}</div>
    <div>用戶名:{{ userStore.userInfo.name }}</div>
    <div>當(dāng)前全局計(jì)數(shù)器:{{ userStore.count }}</div>
    <button @click="userStore.addCount" class="btn">計(jì)數(shù)器+1</button>
    <button @click="userStore.resetCount" class="btn">重置計(jì)數(shù)器</button>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script setup>
import { useUserStore } from '@/store/modules/user';

const userStore = useUserStore();
</script>

Vuex(Vue2常用)- 完整可運(yùn)行Demo

前置準(zhǔn)備:安裝Vuex依賴(npm install vuex@3,Vue2對(duì)應(yīng)Vuex3版本)。

// 1. 創(chuàng)建Vuex倉(cāng)庫(kù)并注冊(cè)(main.js)
import Vue from 'vue';
import App from './App';
import router from './router';
import Vuex from 'vuex'; // 引入Vuex
import store from './store'; // 引入倉(cāng)庫(kù)

Vue.use(Vuex); // 注冊(cè)Vuex

new Vue({
  el: '#app',
  router,
  store, // 注入倉(cāng)庫(kù)
  components: { App },
  template: '<App/>'
});

// 2. 創(chuàng)建全局倉(cāng)庫(kù)(store/index.js)
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    userInfo: { id: 1, name: '測(cè)試用戶', age: 20 },
    count: 0
  },
  mutations: {
    // 同步修改狀態(tài)(必須通過(guò)mutation修改state)
    setUserInfo(state, info) {
      state.userInfo = info;
    },
    addCount(state) {
      state.count++;
    },
    resetCount(state) {
      state.count = 0;
    }
  },
  actions: {
    // 異步修改狀態(tài)(如接口請(qǐng)求后修改)
    addCountAsync({ commit }) {
      setTimeout(() => {
        commit('addCount'); // 調(diào)用mutation修改state
      }, 1000);
    }
  },
  getters: {
    // 計(jì)算屬性(簡(jiǎn)化數(shù)據(jù)讀?。?
    userName: state => state.userInfo.name
  }
});

// 3. 頁(yè)面A(修改全局?jǐn)?shù)據(jù),@/views/PageA.vue)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(修改Vuex全局?jǐn)?shù)據(jù))</h3>
    <div>當(dāng)前全局計(jì)數(shù)器:{{ $store.state.count }}</div>
    <div>用戶名:{{ $store.getters.userName }}</div>
    <button @click="$store.commit('addCount')" class="btn">計(jì)數(shù)器+1(同步)</button>
    <button @click="$store.dispatch('addCountAsync')" class="btn">計(jì)數(shù)器+1(異步)</button>
    <button @click="updateUserInfo" class="btn">修改用戶信息</button>
    <router-link to="/pageB" class="btn">跳轉(zhuǎn)到頁(yè)面B查看數(shù)據(jù)</router-link>
  </div>
</template>

<script>
export default {
  methods: {
    updateUserInfo() {
      // 調(diào)用mutation修改用戶信息
      this.$store.commit('setUserInfo', {
        id: 2,
        name: '新用戶',
        age: 22
      });
    }
  }
};
</script>

// 4. 頁(yè)面B(讀取/修改全局?jǐn)?shù)據(jù),@/views/PageB.vue)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(讀取Vuex全局?jǐn)?shù)據(jù))</h3>
    <div>用戶ID:{{ $store.state.userInfo.id }}</div>
    <div>用戶名:{{ $store.getters.userName }}</div>
    <div>當(dāng)前全局計(jì)數(shù)器:{{ $store.state.count }}</div>
    <button @click="$store.commit('addCount')" class="btn">計(jì)數(shù)器+1</button>
    <button @click="$store.commit('resetCount')" class="btn">重置計(jì)數(shù)器</button>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script>
export default {};
</script>

注意事項(xiàng)

  • Pinia無(wú)需手動(dòng)注冊(cè),Vuex需在main.js中注冊(cè)(Vue2:Vue.use(Vuex);Vue3:app.use(store));
  • 全局狀態(tài)會(huì)在頁(yè)面刷新后丟失,需配合localStorage/sessionStorage緩存(下文會(huì)講);
  • 適合頻繁共享、多頁(yè)面聯(lián)動(dòng)的數(shù)據(jù),不適合臨時(shí)跳轉(zhuǎn)傳值(沒(méi)必要)。

三、localStorage/sessionStorage(本地存儲(chǔ),持久化傳值)

核心思路:利用瀏覽器本地存儲(chǔ)API,將數(shù)據(jù)存入本地,所有頁(yè)面均可讀取,適合“持久化數(shù)據(jù)”場(chǎng)景(如用戶登錄狀態(tài)、記住密碼、長(zhǎng)期保存的配置),分為兩種存儲(chǔ)方式,按需選擇。

完整可運(yùn)行Demo(Vue2/Vue3通用)

適配場(chǎng)景:持久化存儲(chǔ)用戶登錄狀態(tài)、記住密碼,刷新頁(yè)面不丟失,跨頁(yè)面共享。

// 1. 封裝本地存儲(chǔ)工具(utils/storage.js,簡(jiǎn)化版,通用)
// 存儲(chǔ)localStorage(永久存儲(chǔ))
export const setLocal = (key, value) => {
  // 處理對(duì)象/數(shù)組,轉(zhuǎn)為字符串
  const val = typeof value === 'object' ? JSON.stringify(value) : value;
  localStorage.setItem(key, val);
};

// 獲取localStorage
export const getLocal = (key) => {
  const val = localStorage.getItem(key);
  try {
    // 嘗試解析為對(duì)象/數(shù)組
    return JSON.parse(val);
  } catch (e) {
    // 解析失敗,返回原始字符串
    return val;
  }
};

// 刪除localStorage
export const removeLocal = (key) => {
  localStorage.removeItem(key);
};

// 存儲(chǔ)sessionStorage(會(huì)話存儲(chǔ))
export const setSession = (key, value) => {
  const val = typeof value === 'object' ? JSON.stringify(value) : value;
  sessionStorage.setItem(key, val);
};

// 獲取sessionStorage
export const getSession = (key) => {
  const val = sessionStorage.getItem(key);
  try {
    return JSON.parse(val);
  } catch (e) {
    return val;
  }
};

// 2. 頁(yè)面A(存儲(chǔ)數(shù)據(jù),@/views/PageA.vue,Vue3示例,Vue2用法類似)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(存儲(chǔ)本地?cái)?shù)據(jù))</h3>
    <button @click="saveLocalData" class="btn">存儲(chǔ)永久數(shù)據(jù)(localStorage)</button>
    <button @click="saveSessionData" class="btn">存儲(chǔ)臨時(shí)數(shù)據(jù)(sessionStorage)</button>
    <button @click="removeLocal('userInfo')" class="btn">刪除永久數(shù)據(jù)</button>
    <router-link to="/pageB" class="btn">跳轉(zhuǎn)到頁(yè)面B讀取數(shù)據(jù)</router-link>
  </div>
</template>

<script setup>
import { setLocal, setSession, removeLocal } from '@/utils/storage';

// 存儲(chǔ)永久數(shù)據(jù)(刷新頁(yè)面不丟失,除非手動(dòng)刪除)
const saveLocalData = () => {
  setLocal('userInfo', { id: 1, name: '測(cè)試用戶', remember: true });
  alert('永久數(shù)據(jù)存儲(chǔ)成功!');
};

// 存儲(chǔ)臨時(shí)數(shù)據(jù)(關(guān)閉標(biāo)簽頁(yè)/瀏覽器后丟失)
const saveSessionData = () => {
  setSession('tempData', '臨時(shí)測(cè)試數(shù)據(jù)123');
  alert('臨時(shí)數(shù)據(jù)存儲(chǔ)成功!');
};
</script>

// 3. 頁(yè)面B(讀取數(shù)據(jù),@/views/PageB.vue,Vue3示例)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(讀取本地?cái)?shù)據(jù))</h3>
    <div>永久數(shù)據(jù)(localStorage):{{ userInfo }}</div>
    <div>臨時(shí)數(shù)據(jù)(sessionStorage):{{ tempData }}</div>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { getLocal, getSession } from '@/utils/storage';

const userInfo = ref({});
const tempData = ref('');

// 頁(yè)面掛載時(shí)讀取數(shù)據(jù)
onMounted(() => {
  userInfo.value = getLocal('userInfo') || {};
  tempData.value = getSession('tempData') || '暫無(wú)臨時(shí)數(shù)據(jù)';
});
</script>

// Vue2 頁(yè)面B示例(@/views/PageB.vue)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(讀取本地?cái)?shù)據(jù))</h3>
    <div>永久數(shù)據(jù)(localStorage):{{ userInfo }}</div>
    <div>臨時(shí)數(shù)據(jù)(sessionStorage):{{ tempData }}</div>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script>
import { getLocal, getSession } from '@/utils/storage';

export default {
  data() {
    return {
      userInfo: {},
      tempData: ''
    };
  },
  mounted() {
    this.userInfo = getLocal('userInfo') || {};
    this.tempData = getSession('tempData') || '暫無(wú)臨時(shí)數(shù)據(jù)';
  }
};
</script>

注意事項(xiàng)

  • 本地存儲(chǔ)只能存儲(chǔ)字符串,傳遞對(duì)象、數(shù)組時(shí),需用JSON.stringify轉(zhuǎn)字符串,接收時(shí)用JSON.parse解析;
  • localStorage容量約5MB,不可存儲(chǔ)過(guò)大數(shù)據(jù),且數(shù)據(jù)暴露在本地,不適合存儲(chǔ)敏感數(shù)據(jù)(如token,建議加密后存儲(chǔ));
  • 數(shù)據(jù)變化時(shí),頁(yè)面不會(huì)自動(dòng)響應(yīng),需手動(dòng)監(jiān)聽(tīng)(下文“監(jiān)聽(tīng)本地存儲(chǔ)”會(huì)補(bǔ)充)。

四、EventBus(事件總線,簡(jiǎn)單跨頁(yè)面通信)

核心思路:創(chuàng)建一個(gè)全局事件總線,頁(yè)面A觸發(fā)事件并傳遞數(shù)據(jù),頁(yè)面B監(jiān)聽(tīng)事件并接收數(shù)據(jù),適合“簡(jiǎn)單跨頁(yè)面聯(lián)動(dòng)”場(chǎng)景(如頁(yè)面A操作后,頁(yè)面B同步更新),Vue2和Vue3實(shí)現(xiàn)方式略有差異。

Vue3 實(shí)現(xiàn) - 完整可運(yùn)行Demo

適配場(chǎng)景:頁(yè)面A修改數(shù)據(jù)后,頁(yè)面B實(shí)時(shí)更新,無(wú)需跳轉(zhuǎn)路由(如兩個(gè)標(biāo)簽頁(yè)聯(lián)動(dòng))。

// 1. 創(chuàng)建事件總線(utils/eventBus.js)
import { ref } from 'vue';

// 定義總線容器
const bus = ref({});

// 觸發(fā)事件(發(fā)送數(shù)據(jù))
export const emitEvent = (eventName, data) => {
  // 若有監(jiān)聽(tīng)該事件的回調(diào),執(zhí)行并傳遞數(shù)據(jù)
  bus.value[eventName]?.(data);
};

// 監(jiān)聽(tīng)事件(接收數(shù)據(jù))
export const onEvent = (eventName, callback) => {
  // 注冊(cè)回調(diào)函數(shù)
  bus.value[eventName] = callback;
};

// 取消監(jiān)聽(tīng)(避免內(nèi)存泄漏)
export const offEvent = (eventName) => {
  // 刪除事件回調(diào)
  delete bus.value[eventName];
};

// 2. 頁(yè)面A(觸發(fā)事件,發(fā)送數(shù)據(jù),@/views/PageA.vue)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(EventBus發(fā)送數(shù)據(jù))</h3>
    <input v-model="message" placeholder="請(qǐng)輸入要傳遞的內(nèi)容" />
    <button @click="sendData" class="btn">發(fā)送數(shù)據(jù)到頁(yè)面B</button>
    <router-link to="/pageB" class="btn">跳轉(zhuǎn)到頁(yè)面B</router-link>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { emitEvent } from '@/utils/eventBus';

const message = ref('');

// 觸發(fā)事件,傳遞數(shù)據(jù)
const sendData = () => {
  if (!message.value) {
    alert('請(qǐng)輸入內(nèi)容');
    return;
  }
  emitEvent('sendData', { message: message.value, time: new Date().toLocaleString() });
  message.value = '';
  alert('數(shù)據(jù)發(fā)送成功!');
};
</script>

// 3. 頁(yè)面B(監(jiān)聽(tīng)事件,接收數(shù)據(jù),@/views/PageB.vue)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(EventBus接收數(shù)據(jù))</h3>
    <div class="data-list">
      <div v-for="(item, index) in dataList" :key="index">
        {{ item.time }}:{{ item.message }}
      </div>
      <div v-if="dataList.length === 0">暫無(wú)數(shù)據(jù)</div>
    </div>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { onEvent, offEvent } from '@/utils/eventBus';

const dataList = ref([]);

onMounted(() => {
  // 監(jiān)聽(tīng)事件,接收數(shù)據(jù)
  onEvent('sendData', (data) => {
    dataList.value.push(data);
  });
});

onUnmounted(() => {
  // 組件銷毀時(shí)取消監(jiān)聽(tīng),避免內(nèi)存泄漏
  offEvent('sendData');
});
</script>

<style scoped>
.data-list { margin: 20px 0; padding: 10px; border: 1px solid #eee; }
.data-list div { margin: 5px 0; }
</style>

Vue2 實(shí)現(xiàn) - 完整可運(yùn)行Demo

// 1. 注冊(cè)全局EventBus(main.js)
import Vue from 'vue';
import App from './App';
import router from './router';

// 注冊(cè)全局總線(掛載到Vue原型上)
Vue.prototype.$bus = new Vue();

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
});

// 2. 頁(yè)面A(觸發(fā)事件,發(fā)送數(shù)據(jù),@/views/PageA.vue)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(EventBus發(fā)送數(shù)據(jù))</h3>
    <input v-model="message" placeholder="請(qǐng)輸入要傳遞的內(nèi)容" />
    <button @click="sendData" class="btn">發(fā)送數(shù)據(jù)到頁(yè)面B</button>
    <router-link to="/pageB" class="btn">跳轉(zhuǎn)到頁(yè)面B</router-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: ''
    };
  },
  methods: {
    sendData() {
      if (!this.message) {
        alert('請(qǐng)輸入內(nèi)容');
        return;
      }
      // 觸發(fā)全局事件,傳遞數(shù)據(jù)
      this.$bus.$emit('sendData', {
        message: this.message,
        time: new Date().toLocaleString()
      });
      this.message = '';
      alert('數(shù)據(jù)發(fā)送成功!');
    }
  }
};
</script>

// 3. 頁(yè)面B(監(jiān)聽(tīng)事件,接收數(shù)據(jù),@/views/PageB.vue)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(EventBus接收數(shù)據(jù))</h3>
    <div class="data-list">
      <div v-for="(item, index) in dataList" :key="index">
        {{ item.time }}:{{ item.message }}
      </div>
      <div v-if="dataList.length === 0">暫無(wú)數(shù)據(jù)</div>
    </div>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      dataList: []
    };
  },
  mounted() {
    // 監(jiān)聽(tīng)全局事件
    this.$bus.$on('sendData', (data) => {
      this.dataList.push(data);
    });
  },
  beforeDestroy() {
    // 組件銷毀時(shí)取消監(jiān)聽(tīng),避免內(nèi)存泄漏
    this.$bus.$off('sendData');
  }
};
</script>

注意事項(xiàng)

  • EventBus適合簡(jiǎn)單通信,復(fù)雜場(chǎng)景(多頁(yè)面、多事件)易造成事件混亂,建議用Pinia/Vuex替代;
  • 組件銷毀時(shí)必須取消監(jiān)聽(tīng),否則會(huì)導(dǎo)致內(nèi)存泄漏;
  • 頁(yè)面未渲染時(shí),監(jiān)聽(tīng)事件可能接收不到數(shù)據(jù)(需確保頁(yè)面B已渲染再觸發(fā)事件)。

五、監(jiān)聽(tīng)localStorage/sessionStorage(數(shù)據(jù)變化響應(yīng))

核心思路:基于本地存儲(chǔ),監(jiān)聽(tīng)存儲(chǔ)數(shù)據(jù)的變化,當(dāng)頁(yè)面A修改本地存儲(chǔ)時(shí),頁(yè)面B自動(dòng)感知并更新,解決“本地存儲(chǔ)數(shù)據(jù)變化不響應(yīng)”的問(wèn)題,適合“持久化數(shù)據(jù)聯(lián)動(dòng)”場(chǎng)景。

完整可運(yùn)行Demo(Vue2/Vue3通用)

適配場(chǎng)景:頁(yè)面A修改本地存儲(chǔ)的用戶配置,頁(yè)面B自動(dòng)同步更新,無(wú)需手動(dòng)刷新。

// 1. 封裝本地存儲(chǔ)工具(utils/storage.js,同上文,可直接復(fù)用)
export const setLocal = (key, value) => {
  const val = typeof value === 'object' ? JSON.stringify(value) : value;
  localStorage.setItem(key, val);
};
export const getLocal = (key) => {
  const val = localStorage.getItem(key);
  try { return JSON.parse(val); } catch (e) { return val; }
};

// 2. 頁(yè)面A(修改本地存儲(chǔ),觸發(fā)變化,@/views/PageA.vue,Vue3示例)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(修改本地存儲(chǔ))</h3>
    <button @click="updateCount" class="btn">修改全局計(jì)數(shù)(localStorage)</button>
    <div>當(dāng)前計(jì)數(shù):{{ count }}</div>
    <router-link to="/pageB" class="btn">跳轉(zhuǎn)到頁(yè)面B(自動(dòng)同步)</router-link>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { setLocal, getLocal } from '@/utils/storage';

const count = ref(0);

// 頁(yè)面掛載時(shí)讀取當(dāng)前計(jì)數(shù)
onMounted(() => {
  count.value = getLocal('count') || 0;
});

// 修改本地存儲(chǔ)的計(jì)數(shù)(觸發(fā)storage事件)
const updateCount = () => {
  count.value++;
  setLocal('count', count.value);
};
</script>

// 3. 頁(yè)面B(監(jiān)聽(tīng)本地存儲(chǔ)變化,自動(dòng)更新,@/views/PageB.vue,Vue3示例)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(監(jiān)聽(tīng)本地存儲(chǔ)變化)</h3>
    <div>同步的計(jì)數(shù):{{ count }}</div>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { getLocal } from '@/utils/storage';

const count = ref(0);

// 監(jiān)聽(tīng)storage事件
const handleStorageChange = (e) => {
  // 判斷是否是我們關(guān)注的key
  if (e.key === 'count') {
    count.value = Number(e.newValue);
  }
};

onMounted(() => {
  // 初始讀取計(jì)數(shù)
  count.value = getLocal('count') || 0;
  // 注冊(cè)storage監(jiān)聽(tīng)
  window.addEventListener('storage', handleStorageChange);
});

onUnmounted(() => {
  // 取消監(jiān)聽(tīng),避免內(nèi)存泄漏
  window.removeEventListener('storage', handleStorageChange);
});
</script>

// Vue2 頁(yè)面B示例
<script>
import { getLocal } from '@/utils/storage';

export default {
  data() {
    return { count: 0 };
  },
  mounted() {
    this.count = getLocal('count') || 0;
    window.addEventListener('storage', this.handleStorageChange);
  },
  beforeDestroy() {
    window.removeEventListener('storage', this.handleStorageChange);
  },
  methods: {
    handleStorageChange(e) {
      if (e.key === 'count') {
        this.count = Number(e.newValue);
      }
    }
  }
};
</script>

注意事項(xiàng)

  • storage事件僅在“不同頁(yè)面”間觸發(fā),同一頁(yè)面修改本地存儲(chǔ),不會(huì)觸發(fā)自身的storage事件;
  • 僅能監(jiān)聽(tīng)localStorage、sessionStorage的變化,無(wú)法監(jiān)聽(tīng)Cookie的變化;
  • 傳遞復(fù)雜數(shù)據(jù)時(shí),需配合JSON.stringify/JSON.parse解析。

六、Cookie(小型數(shù)據(jù),跨域/持久化適配)

核心思路:利用瀏覽器Cookie存儲(chǔ)小型數(shù)據(jù),可設(shè)置過(guò)期時(shí)間,支持跨域(配置domain),適合“小型持久化數(shù)據(jù)”場(chǎng)景(如用戶token、記住登錄狀態(tài)),容量較小(約4KB)。

完整可運(yùn)行Demo(Vue2/Vue3通用)

適配場(chǎng)景:存儲(chǔ)用戶登錄token、記住密碼狀態(tài),跨頁(yè)面共享,支持過(guò)期時(shí)間設(shè)置。

// 1. 封裝Cookie工具(utils/cookie.js,通用)
// 設(shè)置Cookie(支持過(guò)期時(shí)間、路徑、域名)
export const setCookie = (key, value, days = 7, path = '/', domain = '') => {
  let cookieStr = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
  // 設(shè)置過(guò)期時(shí)間
  if (days) {
    const date = new Date();
    date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
    cookieStr += `;expires=${date.toUTCString()}`;
  }
  // 設(shè)置路徑
  if (path) cookieStr += `;path=${path}`;
  // 設(shè)置域名(跨域時(shí)使用)
  if (domain) cookieStr += `;domain=${domain}`;
  // 寫(xiě)入Cookie
  document.cookie = cookieStr;
};

// 獲取Cookie
export const getCookie = (key) => {
  const cookies = document.cookie.split('; ');
  for (const cookie of cookies) {
    const [k, v] = cookie.split('=');
    if (k === encodeURIComponent(key)) {
      return decodeURIComponent(v);
    }
  }
  return '';
};

// 刪除Cookie
export const removeCookie = (key, path = '/', domain = '') => {
  // 設(shè)置過(guò)期時(shí)間為過(guò)去,實(shí)現(xiàn)刪除
  setCookie(key, '', -1, path, domain);
};

// 2. 頁(yè)面A(設(shè)置Cookie,@/views/PageA.vue,Vue3示例)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(設(shè)置Cookie)</h3>
    <div>
      <label>記住密碼:</label>
      <input type="checkbox" v-model="remember" />
    </div>
    <button @click="login" class="btn">模擬登錄(設(shè)置Cookie)</button>
    <button @click="removeCookie('token')" class="btn">刪除token</button>
    <router-link to="/pageB" class="btn">跳轉(zhuǎn)到頁(yè)面B讀取Cookie</router-link>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { setCookie, removeCookie } from '@/utils/cookie';

const remember = ref(false);

// 模擬登錄,設(shè)置Cookie
const login = () => {
  const token = 'abc123456789'; // 模擬后端返回的token
  // 記住密碼:保存7天;不記?。翰辉O(shè)置過(guò)期時(shí)間(會(huì)話結(jié)束后失效)
  setCookie('token', token, remember.value ? 7 : 0);
  alert('登錄成功,Cookie已設(shè)置!');
};
</script>

// 3. 頁(yè)面B(讀取Cookie,@/views/PageB.vue,Vue3示例)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(讀取Cookie)</h3>
    <div>當(dāng)前登錄token:{{ token }}</div>
    <div v-if="!token">未獲取到token(Cookie已過(guò)期或未設(shè)置)</div>
    <router-link to="/pageA" class="btn">返回頁(yè)面A</router-link>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { getCookie } from '@/utils/cookie';

const token = ref('');

// 頁(yè)面掛載時(shí)讀取Cookie
onMounted(() => {
  token.value = getCookie('token');
});
</script>

// Vue2 頁(yè)面B示例
<script>
import { getCookie } from '@/utils/cookie';

export default {
  data() {
    return { token: '' };
  },
  mounted() {
    this.token = getCookie('token');
  }
};
</script>

注意事項(xiàng)

  • Cookie容量約4KB,僅適合存儲(chǔ)小型數(shù)據(jù)(如token、用戶ID);
  • 默認(rèn)隨每次請(qǐng)求發(fā)送到后端,可用于前后端共享數(shù)據(jù)(如身份驗(yàn)證);
  • 敏感數(shù)據(jù)(如token)建議加密后存儲(chǔ),避免泄露。

七、窗口通信(window.postMessage,跨域/多窗口適配)

核心思路:通過(guò)window.postMessage方法,實(shí)現(xiàn)不同頁(yè)面(甚至跨域頁(yè)面、多窗口)間的通信,適合“跨域頁(yè)面、多窗口聯(lián)動(dòng)”場(chǎng)景(如Vue頁(yè)面與iframe頁(yè)面通信、打開(kāi)新窗口傳值)。

完整可運(yùn)行Demo(分2種場(chǎng)景,Vue2/Vue3通用)

場(chǎng)景1:頁(yè)面A打開(kāi)新窗口(頁(yè)面B),傳遞數(shù)據(jù)

// 頁(yè)面A(打開(kāi)新窗口,發(fā)送數(shù)據(jù),@/views/PageA.vue,Vue3示例)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(打開(kāi)新窗口傳值)</h3>
    <button @click="openPageB" class="btn">打開(kāi)頁(yè)面B并傳遞數(shù)據(jù)</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';

// 存儲(chǔ)新窗口實(shí)例
const pageB = ref(null);

// 打開(kāi)頁(yè)面B并發(fā)送數(shù)據(jù)
const openPageB = () => {
  // 打開(kāi)新窗口(同域場(chǎng)景,路徑為頁(yè)面B的路由)
  pageB.value = window.open('/pageB', '_blank');
  
  // 確保頁(yè)面B加載完成后發(fā)送數(shù)據(jù)(避免接收不到)
  setTimeout(() => {
    // 發(fā)送數(shù)據(jù):第一個(gè)參數(shù)是數(shù)據(jù),第二個(gè)參數(shù)是目標(biāo)域名(*表示允許所有,生產(chǎn)環(huán)境需指定具體域名)
    pageB.value.postMessage(
      { type: 'init', data: { id: 123, name: '窗口通信Demo' } },
      'http://localhost:8080' // 生產(chǎn)環(huán)境替換為實(shí)際域名
    );
  }, 1000);
};
</script>

// 頁(yè)面B(新窗口,接收數(shù)據(jù),@/views/PageB.vue,Vue3示例)
<template>
  <div class="page-b">
    <h3>頁(yè)面B(新窗口接收數(shù)據(jù))</h3>
    <div v-if="receivedData">
      <div>接收的數(shù)據(jù)類型:{{ receivedData.type }}</div>
      <div>接收的ID:{{ receivedData.data.id }}</div>
      <div>接收的名稱:{{ receivedData.data.name }}</div>
    </div>
    <div v-else>暫無(wú)數(shù)據(jù)接收</div>
    <button @click="sendBackData" class="btn">向頁(yè)面A返回?cái)?shù)據(jù)</button>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

const receivedData = ref(null);

// 接收頁(yè)面A發(fā)送的數(shù)據(jù)
const handleMessage = (e) => {
  // 安全校驗(yàn):只接收指定域名的消息(生產(chǎn)環(huán)境必加,防止惡意消息)
  if (e.origin !== 'http://localhost:8080') return;
  // 接收數(shù)據(jù)并賦值
  receivedData.value = e.data;
};

// 向頁(yè)面A返回?cái)?shù)據(jù)
const sendBackData = () => {
  if (!receivedData.value) {
    alert('未接收任何數(shù)據(jù),無(wú)法返回');
    return;
  }
  // 通過(guò)opener獲取父窗口(頁(yè)面A),發(fā)送返回?cái)?shù)據(jù)
  window.opener.postMessage(
    { type: 'reply', data: '已成功接收數(shù)據(jù),感謝發(fā)送!' },
    'http://localhost:8080'
  );
  alert('返回?cái)?shù)據(jù)已發(fā)送給頁(yè)面A');
};

onMounted(() => {
  // 注冊(cè)message監(jiān)聽(tīng)
  window.addEventListener('message', handleMessage);
});

onUnmounted(() => {
  // 取消監(jiān)聽(tīng),避免內(nèi)存泄漏
  window.removeEventListener('message', handleMessage);
});
</script>

// Vue2 頁(yè)面B示例(@/views/PageB.vue)
<script>
export default {
  data() {
    return {
      receivedData: null
    };
  },
  mounted() {
    window.addEventListener('message', this.handleMessage);
  },
  beforeDestroy() {
    window.removeEventListener('message', this.handleMessage);
  },
  methods: {
    handleMessage(e) {
      if (e.origin !== 'http://localhost:8080') return;
      this.receivedData = e.data;
    },
    sendBackData() {
      if (!this.receivedData) {
        alert('未接收任何數(shù)據(jù),無(wú)法返回');
        return;
      }
      window.opener.postMessage(
        { type: 'reply', data: '已成功接收數(shù)據(jù),感謝發(fā)送!' },
        'http://localhost:8080'
      );
      alert('返回?cái)?shù)據(jù)已發(fā)送給頁(yè)面A');
    }
  }
};
</script>

// 補(bǔ)充:頁(yè)面A接收頁(yè)面B返回?cái)?shù)據(jù)(Vue3示例,添加到PageA的script中)
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

const pageB = ref(null);
const replyData = ref('');

// 接收頁(yè)面B返回的數(shù)據(jù)
const handleReply = (e) => {
  if (e.origin !== 'http://localhost:8080') return;
  replyData.value = e.data.data;
  alert(`收到頁(yè)面B返回:${replyData.value}`);
};

const openPageB = () => {
  pageB.value = window.open('/pageB', '_blank');
  setTimeout(() => {
    pageB.value.postMessage(
      { type: 'init', data: { id: 123, name: '窗口通信Demo' } },
      'http://localhost:8080'
    );
  }, 1000);
};

onMounted(() => {
  window.addEventListener('message', handleReply);
});

onUnmounted(() => {
  window.removeEventListener('message', handleReply);
});
</script>

// 場(chǎng)景2:Vue頁(yè)面與iframe頁(yè)面通信(跨域/同域通用)
// 1. 頁(yè)面A(包含iframe,發(fā)送數(shù)據(jù),@/views/PageA.vue,Vue3示例)
<template>
  <div class="page-a">
    <h3>頁(yè)面A(iframe通信)</h3>
    <!-- iframe嵌入頁(yè)面B(可跨域,如https://xxx.com/pageB) -->
    <iframe
      ref="iframeRef"
      src="http://localhost:8080/pageB" // 替換為實(shí)際iframe地址
      width="800"
      height="400"
    ></iframe>
    <button @click="sendToIframe" class="btn" style="margin-top: 20px;">向iframe發(fā)送數(shù)據(jù)</button>
    <div>iframe返回?cái)?shù)據(jù):{{ iframeReply }}</div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

const iframeRef = ref(null);
const iframeReply = ref('');

// 向iframe發(fā)送數(shù)據(jù)
const sendToIframe = () => {
  if (!iframeRef.value) return;
  // 獲取iframe的window對(duì)象,發(fā)送數(shù)據(jù)
  iframeRef.value.contentWindow.postMessage(
    { type: 'iframeData', data: '來(lái)自Vue頁(yè)面的iframe通信數(shù)據(jù)' },
    'http://localhost:8080' // 跨域時(shí)填寫(xiě)iframe的實(shí)際域名
  );
};

// 接收iframe返回的數(shù)據(jù)
const handleIframeReply = (e) => {
  if (e.origin !== 'http://localhost:8080') return;
  iframeReply.value = e.data.data;
};

onMounted(() => {
  window.addEventListener('message', handleIframeReply);
  // 等待iframe加載完成(可選,確保通信穩(wěn)定)
  iframeRef.value.onload = () => {
    console.log('iframe加載完成,可開(kāi)始通信');
  };
});

onUnmounted(() => {
  window.removeEventListener('message', handleIframeReply);
});
</script>

// 2. 頁(yè)面B(iframe內(nèi)嵌頁(yè)面,接收/返回?cái)?shù)據(jù),@/views/PageB.vue,Vue3示例)
<template>
  <div class="page-b">
    <h3>iframe內(nèi)嵌頁(yè)面(接收數(shù)據(jù))</h3>
    <div>接收的iframe數(shù)據(jù):{{ iframeData }}</div>
    <button @click="replyToParent" class="btn">向父頁(yè)面返回?cái)?shù)據(jù)</button>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

const iframeData = ref('暫無(wú)數(shù)據(jù)');

// 接收父頁(yè)面(頁(yè)面A)發(fā)送的數(shù)據(jù)
const handleParentMessage = (e) => {
  // 安全校驗(yàn),僅接收指定域名的消息
  if (e.origin !== 'http://localhost:8080') return;
  iframeData.value = e.data.data;
};

// 向父頁(yè)面返回?cái)?shù)據(jù)
const replyToParent = () => {
  // 通過(guò)parent獲取父窗口,發(fā)送返回?cái)?shù)據(jù)
  window.parent.postMessage(
    { type: 'iframeReply', data: '已接收iframe數(shù)據(jù),返回確認(rèn)!' },
    'http://localhost:8080'
  );
};

onMounted(() => {
  window.addEventListener('message', handleParentMessage);
});

onUnmounted(() => {
  window.removeEventListener('message', handleParentMessage);
});
</script>

// Vue2 iframe通信示例(頁(yè)面B)
<script>
export default {
  data() {
    return {
      iframeData: '暫無(wú)數(shù)據(jù)'
    };
  },
  mounted() {
    window.addEventListener('message', this.handleParentMessage);
  },
  beforeDestroy() {
    window.removeEventListener('message', this.handleParentMessage);
  },
  methods: {
    handleParentMessage(e) {
      if (e.origin !== 'http://localhost:8080') return;
      this.iframeData = e.data.data;
    },
    replyToParent() {
      window.parent.postMessage(
        { type: 'iframeReply', data: '已接收iframe數(shù)據(jù),返回確認(rèn)!' },
        'http://localhost:8080'
      );
    }
  }
};
</script>

以上就是Vue中跨頁(yè)面通信的8種主流方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue跨頁(yè)面通信的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue中使用rem布局的兩種方法小結(jié)

    vue中使用rem布局的兩種方法小結(jié)

    這篇文章主要介紹了vue中使用rem布局的兩種方法小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • vuex的簡(jiǎn)單使用教程

    vuex的簡(jiǎn)單使用教程

    vuex是一個(gè)專門(mén)為vue.js設(shè)計(jì)的集中式狀態(tài)管理架構(gòu)。這篇文章主要介紹了vuex的簡(jiǎn)單使用,需要的朋友可以參考下
    2018-02-02
  • 淺談一下Vue技術(shù)棧之生命周期

    淺談一下Vue技術(shù)棧之生命周期

    這篇文章主要介紹了淺談一下Vue技術(shù)棧之生命周期,每一個(gè)vue實(shí)例從創(chuàng)建到銷毀的過(guò)程,就是這個(gè)vue實(shí)例的生命周期,這些過(guò)程中會(huì)伴隨著一些函數(shù)的自調(diào)用,需要的朋友可以參考下
    2023-05-05
  • 基于ElementUI實(shí)現(xiàn)v-tooltip指令的示例代碼

    基于ElementUI實(shí)現(xiàn)v-tooltip指令的示例代碼

    文本溢出隱藏并使用tooltip 提示的需求,相信在平時(shí)的開(kāi)發(fā)中會(huì)經(jīng)常遇到,常規(guī)做法一般是使用 elementui 的 el-tooltip 組件,在每次組件更新的時(shí)候去獲取元素的寬度/高度判斷是否被隱藏,本文給大家介紹了基于ElementUI實(shí)現(xiàn)v-tooltip指令,需要的朋友可以參考下
    2024-09-09
  • Vue3的10種組件通信方式總結(jié)

    Vue3的10種組件通信方式總結(jié)

    組件是vue.js最強(qiáng)大的功能之一,而組件實(shí)例的作用域是相互獨(dú)立的,這就意味著不同組件之間的數(shù)據(jù)無(wú)法相互引用,這篇文章主要給大家介紹了關(guān)于Vue3的10種組件通信方式的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • vue3+vite+ts父子組件之間的傳值

    vue3+vite+ts父子組件之間的傳值

    隨著vue2的落幕,vue3越來(lái)成熟,有必要更新一下vue3的父子組件之間的傳值方式,這里介紹下vue3+vite+ts父子組件之間的傳值方式實(shí)例詳解,感興趣的朋友一起看看吧
    2023-12-12
  • vue3中<script?setup>?和?setup函數(shù)的區(qū)別對(duì)比

    vue3中<script?setup>?和?setup函數(shù)的區(qū)別對(duì)比

    這篇文章主要介紹了vue3中<script?setup>?和?setup函數(shù)的區(qū)別,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • 如何查看vue項(xiàng)目的node版本

    如何查看vue項(xiàng)目的node版本

    文章總結(jié):查看Vue項(xiàng)目中使用的Node版本,特別是當(dāng)項(xiàng)目使用Yarn和TypeScript時(shí),可以通過(guò)查看yarn.lock文件中的@types/node@version來(lái)確定版本
    2025-01-01
  • vue3中Hook使用以及Hook結(jié)合自定義指令

    vue3中Hook使用以及Hook結(jié)合自定義指令

    這篇文章主要介紹了vue3中Hook使用以及Hook結(jié)合自定義指令方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 解決vue-cli webpack打包開(kāi)啟Gzip 報(bào)錯(cuò)問(wèn)題

    解決vue-cli webpack打包開(kāi)啟Gzip 報(bào)錯(cuò)問(wèn)題

    這篇文章主要介紹了vue-cli webpack打包開(kāi)啟Gzip 報(bào)錯(cuò)問(wèn)題的解決方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-07-07

最新評(píng)論

独山县| 青川县| 灵宝市| 岑巩县| 交口县| 乌鲁木齐市| 祁东县| 泰安市| 龙江县| 磐石市| 社旗县| 辉县市| 商水县| 双柏县| 福州市| 临清市| 达州市| 洞头县| 南城县| 晋江市| 白城市| 新乐市| 无棣县| 确山县| 博白县| 沽源县| 建昌县| 清河县| 东平县| 白河县| 芮城县| 衡山县| 大方县| 贡嘎县| 文成县| 富平县| 镇原县| 襄垣县| 盐城市| 玉门市| 大姚县|