nativescript-vue-dynamo
Nativescript-Vue 动态组件路由器
npm i --save nativescript-vue-dynamo

Nativescript-Vue-Dynamo

Nativescript-Vue-Dynamo 是一个适合 NativeScript-Vue 的动态组件路由器实现。

它使用 Vue 动态组件 来模拟 Nativescript-Vue 应用程序的正常导航模式。它通过连接到 Vue-RouterVuex 来实现这一点。此实现还通过使用相同的动态组件行为(以嵌套方式)来支持“子路由”,允许您在父/子路由之间进行导航。

为了明确:我们实际上并没有使用 Vue-Router 的底层机制来在应用程序中导航,因为这在历史上尝试使用 Nativescript-Vue 时已经是一个问题。然而,我们正在做一些事情,比如将主路由配置作为公共共享配置连接到,然后使用 router.afterEach 钩子来更新我们在状态管理器中保留的路由历史。当路由历史状态发生变化时,动态组件将导致应用程序加载新的路由或页面。这有效地模仿了正常的导航模式。

另一点必须指出:我们也没有使用 Nativescript-Vue 的底层机制来导航。没有进行 $navigateTo$navigateBack 操作。因此,我们在状态管理器内部保持类似于 Nativescript 的导航堆栈,以便我们可以根据需要跟踪前后移动。

此项目结合了 Vuex-Router-SyncNativescript-Vue-Navigator 中的许多相同想法,并以一种模拟 Nativescript-Vue 应用程序中的传统路由体验的方式将它们结合起来。

快速入门

npm install --save nativescript-vue-dynamo

主入口点(main.js 或 main.native.js)

import Vue from 'nativescript-vue';
import App from './App.vue';
import store from './store';
import router, { routes } from './router';

import Dynamo from 'nativescript-vue-dynamo';
Vue.use( Dynamo, {
store,
router,
routes,
});

Vue-Router 配置

在您的 Vue-Router 配置内部,您可能希望像下面这样将您的路由配置拆分成它自己的数组。然后,您可以使用 Vue-Router 提供的许多额外选项,例如添加 meta 对象。大多数内置路由钩子也应该可用以帮助您。注意所有路由的元标签中的 routeHistoryName。对于子路由,注意 parentRouteHistoryName。这两项应按此方式使用,以帮助 Dynamo 跟踪其位置以及它需要去往何处。最后,注意第一个子路由中的 alias 属性。这有助于告诉 Dynamo 哪个子路由是默认子路由。

export const routes = [
{
name: 'home',
path: '/home',
component: () => import(/* webpackChunkName: "home" */ '~/views/Home'),
meta: {
title: 'Home',
auth: true,
routeHistoryName: 'main',
},
},
{
name: 'login',
path: '/login',
component: () => import(/* webpackChunkName: "login" */ '~/views/Login'),
meta: {
title: 'Login',
auth: false,
routeHistoryName: 'main',
},
},
{
name: '',
path: '/first',
component: () => import(/* webpackChunkName: "first" */ '~/views/First'),
props: true,
meta: {
title: 'First',
auth: true,
routeHistoryName: 'main',
},
children: [
{
name: 'dynamo-one',
alias: '/',
path: '/dynamo-one',
component: () => import(/* webpackChunkName: "dynamo-one" */ '~/views/Dynamo-One'),
meta: {
title: 'Dynamo One',
auth: true,
routeHistoryName: 'first',
parentRouteHistoryName: 'main',
},
},
{
name: 'dynamo-two',
path: '/dynamo-two',
component: () => import(/* webpackChunkName: "dynamo-two" */ '~/views/Dynamo-Two'),
meta: {
title: 'Dynamo Two',
auth: true,
routeHistoryName: 'first',
parentRouteHistoryName: 'main',
},
},
],
}
]

App.vue

在您的 App.vue 内部,您可以像下面这样做些简单的事情。注意我们向组件发送了两个必需的属性

  1. defaultRoute - 这将确保加载的第一个页面是此值指定的页面。它还必须与您的路由器配置中的一个实际路由匹配。
  2. routeHistoryName - 此名称将成为加载的 frame/divid,并将用于通过状态管理器跟踪我们正在导航的 frame/div

这两个选项是该包正确运行的必要条件。

<template>
<Page actionBarHidden="true">
<Dynamo
:routeHistoryName="'main'"
:defaultRoute="'home'"
/>

</Page>
</template>

App.vue 中,您甚至可以围绕 Dynamo 组件提供自己的包装器。这可以是 StackLayout 或其他任何布局组件,如 GridLayout。请注意,当 Dynamo 组件更新自身时,任何替代布局项都不会被替换。如果您想要一个底部导航按钮区域,并且按钮被选中时切换 Grid 的第一行项目,这可能很有用。

<template>
<Page actionBarHidden="true">
<GridLayout row="*, auto, auto, auto">
<Dynamo
:routeHistoryName="'main'"
:defaultRoute="defaultRoute"
row="0"
/>

<Button text="First" @tap="$goTo({ path: '/first'})" row="0" />
<Button text="Second" @tap="$goTo({ name: 'second', params: { msg: 'Hello, this is a prop' }})" row="1" />
<Button text="Logout" @tap="$logout('main')" row="2" />
</GridLayout>
</Page>
</template>

导航

因为我们连接到了 Vue-Router,所以那里可用的许多程序性导航助手也应该可以在 Nativescript-Vue 中使用。因此,像 route.push()route.back() 这样的功能应该可以工作。其他一些可能不行,如果您发现某个功能不起作用,请提交一个问题。

在上面的示例中,我们提供了两种在 $goTo 导航助手中路由的方法。有关此函数的更多信息,请参阅下面的导航助手部分。第一种传递一个对象,该对象应类似于您为 vue-routerroute.push 传递的对象,而第二种还向路由传递了 params。传递给路由的任何参数都将作为 prop 传递到路由。这允许您动态地将属性传递到路由。

子路由

您还可以使用“子路由”功能。您将像在 Vue-Router 中那样构建子路由配置,但必须在子路由的配置中添加一个 meta 标签。

meta: {
title: 'Dynamo One',
auth: true,
routeHistoryName: 'first',
parentRouteHistoryName: 'main',
},

通过添加 meta take 并添加 parentRouteHistoryName 属性,您将有效地激活组件之间的父子关系,并模拟许多 Vue-Router 的父子功能。请注意,parentRouteHistoryName 属性等于先前使用的 routeHistoryName,而 routeHistoryName 现在是全新的。您只需在父视图中正常添加 Dynamo 组件即可。注意上面路由配置中的 routeHistoryName 与下面组件中的 routeHistoryName prop 相匹配。

<template>
<Page>
<ActionBar :title="navbarTitle" />
<Dynamo
:routeHistoryName="'first'"
:defaultRoute="'dynamo-one'"
/>

</Page>
</template>

您还可以提供一个模拟 Nativescript 内置的 clearHistory 导航选项的选项。您可以提供一个名为 clearHistory 的路由参数,这将重置对应 routeHistoryName 的路由历史状态。

例如,假设您正在登录页面,登录成功后,您会导航到主页:

$router.push({ name: 'home', params: { routeHistoryName: 'main', clearHistory: 'true'}});

this.$goTo('home', true);

请注意,我们使用了 clearHistory: true 以防止使用后退按钮返回登录页面,或者只是将 true 作为 this.$goTo 的第二个 prop 传递。

可选导航助手

此包还通过 Vue.prototype 提供了一些额外的导航助手,可以帮助您在应用程序中进行导航。

  1. $goTo(location, routeHistoryName, parentRouteHistoryName?, clearHistory?, onComplete?, onAbort?)

    参数 类型 必需 目的
    location 字符串或 Location 必需 您想去的地方。如果这是一个字符串,则将导航到该路由。如果它是 vue-router 位置对象,则将在导航时考虑包含的选项。
    clearHistory 布尔值 清除状态管理器中保留的导航历史。默认为 false
    onComplete 函数 在导航成功完成时(在所有异步钩子解决后)被调用的回调。
    onAbort 错误处理器 在导航被中止时(导航到同一路由,或导航到当前导航完成之前的另一个路由)调用的回调。

    为了方便,我们在幕后构建了一个router.push。我们这样做是出于方便的考虑,因为我们不仅添加了必需的路由参数,还添加了一些可选的路由参数来帮助我们跟踪导航。如果您愿意,仍然可以像上面的登录示例那样使用router.push

  2. $goBack() - 它将检查您是否正在导航回同级路由,或者是否在返回父级路由。它还会检查这是否是帧中的最后一个页面,如果有父级路由,它将切换到该路由。

  3. $goBackToParent(routeHistoryName, parentRouteHistoryName) - 提供子路由的routeHistoryName和父级路由的parentRouteHistoryName,它将导航回路由树。实际上,当您使用$goBack()并且需要切换回父级路由时,会调用此方法。

事件处理和引用

Dynamo组件充当您关心的组件之间的“中间人”。因此,关系看起来像这样:父级 -> 子组件(Dynamo) -> 孙子组件。这意味着您将无法像通常那样处理事件或使用引用来调用孙子组件上的函数。由于这个原因,我们提供了处理这两种常见模式的方法。

事件处理

当您调用Dynamo组件时,您可以添加一个额外的参数,该参数包含一个非常具体的引用方式,其中必须包含routeHistoryName。注意,我们使用v-onfirst作为短横线命名法中的第一个字符串。您需要使用的模式是“routeHistoryName + -event-handler”。这会让Dynamo知道要将事件冒泡到哪个父级组件。

<Dynamo
:routeHistoryName="'first'"
:defaultRoute="'dynamo-one'"
:functionHandler="functionHandler"
@first-event-handler="eventHandler"
/>

然后,在您的父组件中,您可以包含一个类似以下函数

public eventHandler(e){
console.log('first.vue - eventHandler - ', e);
}

在您的孙子组件中,您可以像通常那样发出事件:this.$emit('dynamo-event', "emit event one");。注意事件名是dynamo-event。这是必须的事件名,因为它硬编码在Dynamo组件中。

理论上,您可以使用单个事件处理器在父组件中完成多项任务,例如使用switch语句,或者更高级地通过事件传递一个对象并将其转换为调用其他地方的函数。以下是一个示例对象:{ function: 'anotherMethod', data: 'data' },然后在您的事件处理器函数中,您可以执行以下操作,它将自动调用您的anotherMethod方法,并传递data

public eventHandler(e){
this[e.function](e.data)
}

引用和调用孙子函数

在您的Dynamo组件中,您可以包含一个名为functionHandler的选项,然后让它引用父组件中的一个属性。实际上,我们正在将一个属性传递给Dynamo组件,该组件又将该属性传递给孙子组件。

为了方便起见,我们在下面的示例中将函数命名为functionHandler

<Dynamo
:routeHistoryName="'first'"
:defaultRoute="'dynamo-one'"
:functionHandler="functionHandler"
@first-event-handler="eventHandler"
/>

然后,您可以在script标签的导出中执行以下操作。在示例中提供的parentButton方法只是示例中提供的一个方便方法,在按钮点击后调用。然后,parentButton方法替换了this.functionHandler的值。提供的对象与上面的事件处理器示例类似,但方向相反。它引用了在孙子组件上调用的方法:parentToChild以及要传递给孙子组件方法的某些数据。

public functionHandler: object = {};

public parentButton() {
console.log('parentButton clicked');
this.functionHandler = { method: 'parentToChild', data: 'hello there' };
}

然后在您的子组件内部,您可以这样做。请注意,我们不仅设置了Prop,还设置了watcher。这使我们能够使用这个单一的Prop作为调用子组件中多个方法的入口。把它看作是一对多的关系。在watcher内部,我们包含了一个动态调用其他方法的示例。

在上面的示例中,我们将parentToChild作为要调用的方法,并传递了hello there的数据。这意味着在下面的示例中,我们将parentToChild - hello there写入控制台。此外,我们还包含了一个返回事件的示例。这是可选的,并非必需。

@Prop() functionHandler: object = {};
@Watch('functionHandler')
onfunctionHandlerChanged(val: Function) {
this[val.method](val.data);
}

public parentToChild(data: string) {
console.log('parentToChild - ', data);
this.$emit('dynamo-event', "emit event two from parentToChild function");
}

演示项目

查看演示项目,该项目使用上述许多示例实现了此插件。注意在utils/global-mixin-native类文件中,有一个拦截Android返回按钮行为的示例,并将其连接到$goBack()导航助手。