Skip to content

组件之间的通信

uni-app 中,原生的 uni.navigateTo 导航方法提供了一个名为 events 的参数,用于实现页面间的通信。通过这个参数,可以在导航目标页和跳转起始页之间进行数据通讯。然而,在 uni-simple-router 中,所有的导航方法都不支持 events 参数用于通讯。

相应地,uni-simple-router 提供了一种更高效的方式来进行页面间的通信。它内部引入了一个名为上帝函数(God Function)的概念,该函数在所有页面之上,统一管理着所有页面的运行空间。通过告知上帝函数你要访问的页面,你可以获取该页面的 Vue 实例,并且可以在该实例中执行任何你想要的操作。你可以传递参数、调用页面的方法、修改页面的数据等等。

使用上帝函数可以更方便地实现页面间的通信和操作,避免了传统的事件派发和监听的方式,提供了更高效和便捷的页面管理机制。

上帝函数 parserInstance

例如,假设你的路由配置如下:

ts
// router.js
import {
    __dynamicImportComponent__
} from '@/uni-simple-router'

const routes = [
  {
    path: '/admin',
    name:`adminHome`,
    component: AdminLayout,
    children: [
      {
        path: 'dashboard',
        name:`dashboard`,
        component: DashboardPage
      },
      {
        path: 'users',
        name:`users`,
        component: UsersPage
      },
      {
        path: 'settings',
        name:`settings`,
        component: SettingsPage
      }
    ]
  }
];

DashboardPage.vue 组件的内容

html
<!-- DashboardPage.vue -->
<template>
  <view class="content">
    <h1>{{ title }}</h1>
  </view>
</template>

<script lang="ts">
import { ref } from 'vue'
export default {
  setup(){
    const title = ref('Hello World')

    function changeTitle(t:string){
      title.value = t
      uni.showToast({
        title:`修改成功`,
      })
    }

    return {
      changeTitle,
      title
    }
  }
}

</script>

在路由名为 users 的组件中调用 dashboard 页面的函数。

html
<!-- UsersPage.vue -->
<template>
    <view class="content">
        <button @click="callMethod">触发上帝函数</button>
    </view>
</template>

<script setup lang="ts">
import {ref} from 'vue'
import {parserInstance} from '@/uni-simple-router'

function callMethod(){
  const result = parserInstance(`dashboard`,[
      (instance)=>instance.changeTitle(`你好 uni-simple-router`)
  ])
  console.log(`执行结果:${result}`)
}
</script>

不难看出,使用 parserInstance 函数非常简单,只需要提供相应的页面名称和需要执行的任务列表即可在任务中获取对应实例。这在使用多命名视图的情况下尤为重要,因为在一个页面下可能存在多个命名视图组件,需要精确获取对应的组件实例。下面是一个示例:

例如,假设你的路由配置如下:

ts
// router.js
import {
    __dynamicImportComponent__
} from '@/uni-simple-router'

const routes = [
  {
    path: '/admin',
    name:`adminHome`,
    component: AdminLayout,
    children: [
      {
        path: 'dashboard',
        name:`dashboard`,
        components:{
          default:DashboardPage,
          users:UsersPage,
          settings:SettingsPage
        } 
      }
    ]
  }
];

假设三个组件:DashboardPage.vueUsersPage.vueSettingsPage.vue,它们都有一个名为 getData 的函数。现在,假设你希望在某个 JavaScript 文件中调用 SettingsPage.vueUsersPage.vue 组件中的 getData 函数。

为了实现这个目标,你可以使用 parserInstance 函数来创建多任务,并指定对应的视图名称即可。

js
// xxxx.js
import {parserInstance} from '@/uni-simple-router'

const result = parserInstance(`dashboard`,[
  // 调用 `SettingsPage.vue` 实例任务
  [
    (instance)=>instance.getData(), `settings`
  ],
  // 调用 `UsersPage.vue` 实例任务
  [
    (instance)=>instance.getData(), `users`
  ],
])
console.log(`执行结果:${result}`)

通常情况下,通过导航方法打开新页面并传递参数是非常简单的,但在某些平台和特定的 API 上可能存在一些限制。例如,在小程序中使用 pushTab 方法跳转到原生 tabbar 页面时无法传递参数,返回页面时也无法传递参数。然而,使用 parserInstance 函数可以很好地解决这些问题。

通过 parserInstance 函数,你可以获取目标页面的实例,并直接操作该实例来传递参数或执行其他任务。这种方式绕过了特定导航方法的限制,提供了更灵活的参数传递和页面间的交互方式。

为原生 tabbar 页面传参

js
router.pushTab({name:`newTab`})
.then(()=>{
  //导航成功,传递通讯数据
  parserInstance(`newTab`,[
    (instance)=>{
      // 调用 newTab 页面的方法
      instance.newPageNotify(`来自星星的它`)

      // 或者修改 newTab 页面的数据
      instance.title = `自定义title`
    }
  ])
})

返回上一级页面传参

js
router.back(1)
.then(()=>{
  parserInstance(`lastPage`,[
    (instance)=>{
      // 调用 lastPage 页面的方法
      instance.setParams(`返回页面时传递的参数`)
    }
  ])
})

parserInstance 任务中访问实例

在开发 Vue.js 组件时,常见的写法有选项式 (Options API) 和组合式 (Composition API)。由于这两种写法中,组件实例暴露的函数位置不同,很多开发者在切换或混用这两种写法时会遇到问题。因此,有必要详细讲解如何在不同写法中正确访问和使用组件实例的函数。

  • 选项式
ts
// name 为 test 的 xxx.vue
<script>
	export default {
		methods: {
			notify(msg){
				uni.showModal({
					title:`上帝函数通知`,
					content:msg,
					showCancel:false
				})
			}
		}
	}
</script>

// 在 parserInstance 函数任务中调用方法
parserInstance(`test`,[
  (instance)=>{
    instance.notify(`来自星星的它`)
  }
]) || uni.showToast({ title:`test页面未挂载`,icon:`error` });
  • 组合式
ts
// name 为 test 的 xxx.vue
<script setup>
function notify(msg){
	uni.showModal({
		title:`上帝函数通知`,
		content:msg,
		showCancel:false
	})
}

defineExpose({
	notify
})
</script>

// 在 parserInstance 函数任务中调用方法
parserInstance(`test`,[
  (instance)=>{
    instance.$.exposed.notify(`来自星星的它`);
  }
]) || uni.showToast({ title:`test页面未挂载`,icon:`error` });

更多详细资料可查看 defineExpose() 或者 #164801

parserInstance 函数签名

ts
export type InstanceSpaceRule<T> = (instance:T)=>any

export type ExecInstanceTaskRule<T> = Array<
    [
        InstanceSpaceRule<T>
    ] | [
        InstanceSpaceRule<T>,
        string
    ] | InstanceSpaceRule<T>
>

export function parserInstance<T>(
    name:string,
    taskList:ExecInstanceTaskRule<T>
):null|true {
  // ...
}
组件之间的通信 has loaded