加入直播:周四,美国东部时间上午 11 点,Storybook 9 版本发布与 AMA

Vue 3 Router

一个 Storybook 装饰器,允许你为你支持路由的组件构建故事。

在 Github 上查看

Storybook Vue3 Router

minified + gzip size Npm package monthly downloads Release CodeFactor Storybook

一个 Storybook 装饰器,允许你使用你的 Vue 3 支持路由的组件。

如果你想为使用 <router-view><router-link> 的 Vue 3 组件构建故事,那么你需要用 vue-router 包裹你的故事,这个插件将让你轻松做到这一点。

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

如何使用

这个装饰器兼容 Storybook 的组件故事格式 (CSF)提升的 CSF 注释,这是自 Storybook 6 以来推荐的故事编写方式。它尚未与 storiesOf API 进行测试。

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

Storybook v7: 请使用包版本 3+

请参阅迁移指南

安装装饰器

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

在你的故事中使用

默认设置将创建一个 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()
]

演示

你可以在这个演示站点上看到发布的示例故事

高级用法

这个装饰器附带可选参数,用于自定义你在 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 导出。

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

故事设置

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 文件,用 Vue 3 的 <Suspense> 组件包裹故事。这是因为装饰器需要一个 async setup() 来正确地 await router.isReady()。你可以修改 preview 如下

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

export default preview;

请参阅examples 文件夹以了解更多高级用法。

装饰器参数


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 导出可能就能满足你的需求(并减少故事中使用的导入)。

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

在你的故事中使用 mockRouter

默认设置将从 vue-router 创建模拟的 $router$route,这使你能够为使用编程式导航和基于路由的逻辑的组件创建故事。

我们还可以将自定义选项传递给 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 选项时,如果存在其他故事也使用了此装饰器,则必须强制重新加载页面以设置特定的故事路由守卫,这会对用户体验/性能产生轻微影响。请查看演示了解示例:README > With Router Guards > Global Guard - 当点击“Global Guard”链接时,你会注意到页面会刷新以应用全局守卫(由于之前存在的故事)。

如果你只为单个故事使用此装饰器,则不会出现此问题。

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