加入在线会议:美东时间周四上午 11 点,Storybook 9 发布及 AMA(问我任何事)

Vue 3 Router

一个 Storybook 装饰器,可用于为您的路由感知组件构建 stories。

在 Github 上查看

Storybook Vue3 Router

minified + gzip size Npm package monthly downloads Release CodeFactor Storybook

一个 Storybook 装饰器,可用于使用您的 Vue 3 路由感知组件。

如果您想为使用 <router-view><router-link> 的 Vue 3 组件构建 stories,您需要使用 vue-router 包装您的 stories,此插件可以轻松实现这一点。

对于只需要访问 $route$router 属性的用户,还有一个模拟路由装饰器选项。

如何使用

此装饰器适用于 Storybook 的组件故事格式 (CSF)提升的 CSF 注解,这是 Storybook 6 以来推荐的 stories 编写方式。它尚未与storiesOf API一起测试。

Storybook v6:请使用包版本 2.x

Storybook v7:请使用包版本 3+

请参阅迁移指南

安装装饰器

npm install --save-dev storybook-vue3-router
// or
yarn add --dev storybook-vue3-router

在您的 stories 中使用

默认设置将创建一个 vue-router 实例,包含 2 个路由(//about)——这些可以在 defaultRoutes.ts 文件中查看。

/* import storybook-vue3-router */
import { vueRouter } from 'storybook-vue3-router'

/* ...story setup... */

/* your story export */
export const Default = Template.bind({})

/* adding storybook-vue3-router decorator */
Default.decorators = [
  /* this is the basic setup with no params passed to the decorator */
  vueRouter()
]

演示

您可以在此演示中查看发布的示例 stories

高级用法

此装饰器附带可选参数,用于在 Storybook 中自定义 vue-router 的实现。

自定义路由

/* define our custom routes */
const customRoutes = [
  {
    path: '/',
    name: 'home',
    component: HomeComponent // this would need to be defined/imported into the `.stories` file
  },
  {
    path: '/about',
    name: 'about',
    component: AboutComponent // this would need to be defined/imported into the `.stories` file
  }
]

/* adding storybook-vue3-router decorator */
Default.decorators = [
  /* pass custom routes to the decorator */
  vueRouter(customRoutes)
]

自定义路由(带守卫)

/* define our custom routes */
const customRoutes = [
  // ...
  {
    path: '/admin',
    name: 'admin',
    component: AdminComponent,
    /* add per-route navigation guard */
    beforeEnter: (to, from, next) => {
      // ...
    }
  }
]

/* adding storybook-vue3-router decorator */
Default.decorators = [
  /* pass custom routes to the decorator */
  vueRouter(customRoutes)
]

自定义路由(带初始路由)

默认情况下,装饰器会将起始路由默认为 /,如果您想更改此设置,可以将其作为参数传递给装饰器。

/* define our custom routes */
const customRoutes = [
  {
    path: '/',
    name: 'dashboard',
    component: Dashboard
  },
  {
    path: '/intro',
    name: 'intro',
    component: Intro
  }
]

### With Router Options
We can pass [Vue Router options](https://router.vuejs.net.cn/api/index.html#history) into our decorator.

```typescript
/* adding storybook-vue3-router decorator */
Default.decorators = [
  /* pass vueRouterOptions to the decorator */
  vueRouter(undefined, {
    vueRouterOptions: {
      linkActiveClass: 'my-active-class',
      linkExactActiveClass: 'my-exact-active-class'
      ...etc
    }
  })
]

router.isReady()

如果您使用 router.isReady() 设置了路由,并且/或者您的组件在 created 生命周期钩子中需要特定的路由/路由数据,您可能需要使用 asyncVueRouter 导出项。

此导出项提供的路由器在路由准备就绪之前不会渲染 story。

Story 设置

import { asyncVueRouter } from 'storybook-vue3-router'

/* define our custom routes */
const customRoutes = [
  {
    path: '/',
    name: 'dashboard',
    component: Dashboard
  },
  {
    path: '/intro',
    name: 'intro',
    component: Intro
  }
]

/* adding storybook-vue3-router decorator */
Default.decorators = [
  /* pass initialRoute to the decorator */
  asyncVueRouter(customRoutes, {
    initialRoute: '/intro'
  })
]

Preview.js 异步设置

为了使用 async 路由设置方法,您需要修改 .storybook/preview.js 文件,将 stories 包装在 Vue 3 的 <Suspense> 组件中。这是因为装饰器需要一个 async setup() 来正确地 await router.isReady()。您可以修改 preview 为

const preview = {
  decorators: [
    (story) => ({
      components: { story },
      template: '<Suspense><story /></Suspense>',
    }),
  ],
};

export default preview;

请参阅示例文件夹以获取更高级的用法。

装饰器参数


function vueRouter(routes: RouteRecordRaw[], options?: { initialRoute?: string, beforeEach?: NavigationGuard, vueRouterOptions?: RouterOptions })
function asyncVueRouter(routes: RouteRecordRaw[], options?: { initialRoute?: string, beforeEach?: NavigationGuard, vueRouterOptions?: RouterOptions })

模拟路由

并非总是需要完整的 vue-router——例如,如果您没有使用 <router-view><router-link> 的组件,那么使用 mockRouter 导出项可能就能满足您的需求(并减少 stories 中使用的导入)。

注意:mockRouter 仅适用于使用选项 API this.$route 和/或 this.$router 的情况,不适用于使用 vue router 组合式 API(如 useRoute()useRouter())的用例。

在您的 stories 中使用 mockRouter

默认设置将从 vue-router 创建模拟的 $router$route,这使您可以为使用编程导航和基于路由逻辑的组件创建 stories。

我们还可以将自定义选项传递给 mockRouter 装饰器

{ 
  meta?: Array<string>, 
  params?: Array<string>, 
  query?: Array<string>
}
/* import storybook-vue3-router mockRouter */
import { mockRouter } from 'storybook-vue3-router'

/* ...story setup... */

/* your story export */
export const Default = Template.bind({})

/* adding storybook-vue3-router mockRouter decorator */
Default.decorators = [
  mockRouter({
    meta: ['some_meta'],
    params: ['some_param'],
    query: ['some_query']
  })
]

您可以在我们的Storybook 演示站点和我们的代码示例中看到 mockRouter 的示例

v2.x 迁移到 v3+

⚠️ 破坏性变更 ⚠️

v3.x 版本不再使用 vueRouter 装饰器的默认导出,您需要更新为使用命名导出。

/* DONT */
import vueRouter from 'storybook-vue3-router'
/* DO */
import { vueRouter } from 'storybook-vue3-router'

v1.x 迁移到 v2.x

从 v1 迁移带来了一些破坏性变更

// v1.x - 2nd param is used to pass `beforeEach` router guard
// in this example the guard is used to fire a storybook action with `to` and `from` router objects
vueRouter(customRoutes, (to, from) => action('ROUTE CHANGED')({ to: to, from: from })) // LEGACY

// v2.1 - 2nd param is used to pass additional options to the decorator
vueRouter(customRoutes, {
  /* add global beforeEach guard */
  beforeEach: (to, from) => action('ROUTE CHANGED')({ to: to, from: from })
})

如果您之前在第二个参数中使用了 v1 的路由守卫,则需要重构以使用路由特定的路由守卫 (推荐),或者您可以使用 beforeEach 选项传递全局路由守卫。

v2.0 不包含此 beforeEach 选项,请升级到 v2.1

⚠️ 警告

当使用全局 beforeEach 选项时,如果存在使用此装饰器的现有 story,则必须强制刷新页面才能设置特定的 story 路由守卫,这会对用户体验/性能产生轻微影响。请查看演示了解示例:README > With Router Guards > Global Guard - 当点击 'Global Guard' 链接时,您会注意到页面被刷新以应用全局守卫(由于之前已存在的 stories)。

如果您仅对一个 story 使用此装饰器,则不会出现此问题。

在解决此问题后,为了支持使用不同路由设置创建多个 stories,注意到这导致全局 beforeEach 函数在每个路由上都被添加。例如,每次您点击不同的 story 时,新的 beforeEach 钩子都会被添加——但之前的不会被移除,这导致多个守卫在与“当前活动”story 无关的 stories 上触发。

创建者
  • nickmcburney
    nickmcburney
适用于
    Vue
标签