Storybook Vue3 Router
一个 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 上触发。