我有一个组件的故事,要求我的Vuex商店中的ACTION执行API调用。但是,无法通过Storybook找到商店:Unhandled promise rejection TypeError: "this.$store is undefined"
。
我试图通过created
和mounted
Vue生命周期挂钩访问商店,但是每个挂钩都返回了undefined
。
我的Vuex商店在我的应用程序中正常工作。
我使用故事书5.0.1
和vuex 3.1.1
。
这是我的故事书config.js
:
// Taken from https://davidwalsh.name/storybook-nuxt & https://github.com/derekshull/nuxt-starter-kit-v2/blob/master/.storybook/config.js
import { addParameters, configure } from '@storybook/vue';
import { withOptions } from '@storybook/addon-options';
import { setConsoleOptions } from '@storybook/addon-console';
import { create } from '@storybook/theming';
import Vue from 'vue';
import VueI18n from 'vue-i18n';
// Vue plugins
Vue.use(VueI18n);
setConsoleOptions({
panelExclude: [],
});
// Option defaults:
addParameters({
options: {
/**
* show story component as full screen
* @type {Boolean}
*/
isFullScreen: false,
/**
* display panel that shows a list of stories
* @type {Boolean}
*/
showNav: true,
/**
* display panel that shows addon configurations
* @type {Boolean}
*/
showPanel: true,
/**
* where to show the addon panel
* @type {String}
*/
panelPosition: 'bottom',
/**
* sorts stories
* @type {Boolean}
*/
sortStoriesByKind: false,
/**
* regex for finding the hierarchy separator
* @example:
* null - turn off hierarchy
* /\// - split by `/`
* /\./ - split by `.`
* /\/|\./ - split by `/` or `.`
* @type {Regex}
*/
hierarchySeparator: /\/|\./,
/**
* regex for finding the hierarchy root separator
* @example:
* null - turn off multiple hierarchy roots
* /\|/ - split by `|`
* @type {Regex}
*/
hierarchyRootSeparator: /\|/,
/**
* sidebar tree animations
* @type {Boolean}
*/
sidebarAnimations: true,
/**
* enable/disable shortcuts
* @type {Boolean}
*/
enableShortcuts: true,
/**
* theme storybook, see link below
*/
theme: create({
base: 'light',
brandTitle: '',
brandUrl: '',
// To control appearance:
// brandImage: 'http://url.of/some.svg',
}),
},
});
const req = require.context('../src/components', true, /\.story\.js$/)
function loadStories() {
req.keys().forEach((filename) => req(filename))
}
configure(loadStories, module);
这是我组件的故事:
import { storiesOf } from '@storybook/vue';
import { withReadme } from 'storybook-readme';
import { withKnobs } from '@storybook/addon-knobs';
import HandoffMainView from './HandoffMainView.vue';
import readme from './README.md';
storiesOf('HandoffMainView', module)
.addDecorator(withReadme([readme]))
.addDecorator(withKnobs)
.add('Default', () => {
/* eslint-disable */
return {
components: { HandoffMainView },
data() {
return {
isLoading: true,
component: {
src: '',
data: [],
},
};
},
template: '<handoff-main-view :component="component" />',
};
});
这是我的组成部分:
<template>
<main class="o-handoff-main-view">
<div class="o-handoff-main-view__content">
<div
:class="[
'o-handoff-main-view__background',
background ? `o-handoff-main-view__background--${background}` : false
]"
>
<loader
v-if="isLoading"
:color='`black`'
class="o-handoff-main-view__loader"
/>
<div
v-else
class="o-handoff-main-view__ui-component"
:style="getUiComponentStyle"
>
<img
:src="uiComponent.src"
alt=""
>
<handoff-main-view-layer-list
:layers="uiComponent.data"
/>
</div>
</div>
</div>
<div class="o-handoff-main-view__controls">
<handoff-main-view-zoom-handler
:default-zoom-level="zoomLevel"
:on-change="updateZoomLevel"
/>
</div>
</main>
</template>
<script>
import { mapActions } from 'vuex';
import Loader from '../../01-atoms/Loader/Loader.vue';
import HandoffMainViewZoomHandler from '../HandoffMainViewZoomHandler/HandoffMainViewZoomHandler.vue';
import HandoffMainViewLayerList from '../HandoffMainViewLayerList/HandoffMainViewLayerList.vue';
export default {
components: {
Loader,
HandoffMainViewZoomHandler,
HandoffMainViewLayerList,
},
props: {
background: {
type: String,
default: 'damier',
},
component: {
type: Object,
required: true,
},
},
data() {
return {
isLoading: true,
zoomLevel: 1,
uiComponent: {
src: null,
}
};
},
mounted() {
this.setUiComponentImage();
},
methods: {
...mapActions('UiComponent', [
'ACTION_LOAD_SIGNED_URLS'
]),
async setUiComponentImage() {
const uiComponentImg = new Image();
const signedUrls = await this.ACTION_LOAD_SIGNED_URLS([this.component.id]);
uiComponentImg.onload = () => {
this.isLoading = false;
};
uiComponentImg.src = this.uiComponent.src;
},
},
};
</script>
答案 0 :(得分:1)
在故事中传递新的商店实例(或嘲笑)
import Vuex from "vuex";
import { storiesOf } from '@storybook/vue';
import { withReadme } from 'storybook-readme';
import { withKnobs } from '@storybook/addon-knobs';
import HandoffMainView from './HandoffMainView.vue';
import readme from './README.md';
storiesOf('HandoffMainView', module)
.addDecorator(withReadme([readme]))
.addDecorator(withKnobs)
.add('Default', () => {
/* eslint-disable */
return {
components: { HandoffMainView },
data() {
return {
isLoading: true,
component: {
src: '',
data: [],
},
};
},
template: '<handoff-main-view :component="component" />',
store: new Vuex.Store({ // here
modules: {
namespaced: true,
actions: ...
}
}
};
});
答案 1 :(得分:0)
我打赌您的应用中的某个地方(可能是main.js
),您正在执行以下操作:
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state,
mutations,
getters,
});
然后,在创建Vue应用程序时,您会呼叫new Vue({store, i18n...})
。
您已经在config.js
中使用'i18n'模块来伪造Vue。您还需要导入Vuex和那里的商店。
现在,在您的故事书设置中必须导入商店(或模拟商店)可能是您的组件过大或与商店耦合过多的味道。
通常,故事书更适合于显示那些显示具有专用功能的内容(表单控件,事物列表...)的组件。这些组件通常通过道具和事件与应用程序的其余部分通信。我们称其为演示组件。
相反,与商店通信的组件通常是视图或页面,它们协调状态并与后端通信,并向后端提供数据。
我认为您应该在故事书上仅展示演示性组件,并避免谈论其中的全局模块。至少,我相信这是故事书背后的精神以及主要用途。这可能是因为您没有在故事书中找到太多关于如何模拟商店的文档:我认为,故事书项目通常通常不会首先连接到vuex。
答案 2 :(得分:0)
如果您使用的是Nuxt.js,请按以下步骤操作:
./ storybook / store.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store = new Vuex.Store({
state: require("../store/index.js").state,
getters: require("../store/index.js").getters,
actions: require("../store/index.js").actions,
mutations: require("../store/index.js").mutations,
modules: {
ads: {
namespaced: true,
state: require("../store/ads.js").state,
getters: require("../store/ads.js").getters,
actions: require("../store/ads.js").actions,
mutations: require("../store/ads.js").mutations
},
features: {
namespaced: true,
state: require("../store/features.js").state,
getters: require("../store/features.js").getters,
actions: require("../store/features.js").actions,
mutations: require("../store/features.js").mutations
},
user: {
namespaced: true,
state: require("../store/user.js").state,
getters: require("../store/user.js").getters,
actions: require("../store/user.js").actions,
mutations: require("../store/user.js").mutations
},
}
});
export default store
然后在您的故事中:
// ...
import store from '@/.storybook/store';
export default {
title: 'MyComponent'
};
export const MyComponentStory = () => ({
store: store,
// ...
})