出现这样的错误:
import { createStore } from 'vuex'
import { postModule } from './postModule'
export default createStore({
modules: {
/* post - название модуля */
post: postModule,
},
})
<!-- component PostModule.js -->
import axios from 'axios'
export const postModule = {
state: () => ({
posts: [],
isPostsLoading: false,
selectedSort: '',
searchQuery: '',
page: 1,
limit: 10,
totalPages: 0,
sortOptions: [
{ value: 'title', name: 'По названию' },
{ value: 'body', name: 'По содержанию' },
],
}),
getters: {
sortedPosts(state) {
return [...state.posts].sort((post1, post2) =>
post1[state.selectedSort]?.localeCompare(post2[state.selectedSort])
)
},
sortedAndSearchedPosts(state, getter) {
return getter.sortedPosts.filter((post) =>
post.title.toLowerCase().includes(state.searchQuery.toLowerCase())
)
},
},
mutations: {
setPosts(state, posts) {
state.posts = posts
},
// setIsPostsLoading(state, bool) {
// state.isPostsLoading = bool
// },
setSelectedSort(state, selectedSort) {
state.selectedSort = selectedSort
},
setSearchQuery(state, query) {
state.searchQuery = query
},
setPage(state, page) {
state.page = page
},
setTotalPages(state, totalPages) {
state.totalPages = totalPages
},
},
actions: {
/* commit - нужен для вызова мутаций, dispatch - для вызова других actions */
async fetchPosts({ state, commit }) {
try {
commit('setIsPostsLoading', true)
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts`,
{
params: {
_page: state.page,
_limit: state.limit,
},
}
)
commit(
'setTotalPages',
Math.ceil(res.headers['x-total-count'] / state.limit)
)
commit('setPosts', res.data)
} catch (error) {
console.log(error)
} finally {
commit('setIsPostsLoading', false)
}
},
async loadMorePosts({ state, commit }) {
try {
commit('setPage', state.page + 1)
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts`,
{
params: {
_page: state.page,
_limit: state.limit,
},
}
)
commit(
'setTotalPages',
Math.ceil(res.headers['x-total-count'] / state.limit)
)
/* В этом случае мы посты не перезаписываем, а добавляем в конец массива */
commit('setPosts', [...state.posts, ...res.data])
} catch (error) {
console.log(error)
}
},
},
namespaced: true,
}
<!-- component PostsPageWithStore.vue -->
<template>
<div>
<h1>Страница с постами</h1>
<!-- <my-input v-focus v-model="searchQuery" placeholder="Поиск..." /> -->
<div class="app__btns">
<my-button @click="showDialog">Cоздать пост</my-button>
<!-- <my-select v-model="selectedSort" :options="sortOptions" /> -->
</div>
<my-dialog v-model:show="dialogVisible">
<post-form @create="createPost" />
</my-dialog>
<post-list
:posts="sortedAndSearchedPosts"
@remove="removePost"
v-if="!isPostsLoading"
/>
<div v-else class="loader">Загрузка...</div>
<div v-intersection="loadMorePosts" class="observer"></div>
</div>
</template>
<script>
import PostForm from '@/components/PostForm.vue'
import PostList from '@/components/PostList.vue'
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'
export default {
components: { PostList, PostForm },
data() {
return {
dialogVisible: false,
}
},
methods: {
...mapMutations({
setPage: 'post/setPage',
}),
...mapActions({
loadMorePosts: 'post/loadMorePosts',
fetchPosts: 'post/fetchPosts',
}),
/* мы подписались на это событие и принимаем post из компонента PostForm */
createPost(post) {
this.posts.push(post)
this.dialogVisible = false
},
removePost(post) {
this.posts = this.posts.filter((p) => p.id !== post.id)
},
showDialog() {
this.dialogVisible = true
},
},
mounted() {
this.fetchPosts()
},
computed: {
...mapState({
posts: () => state.post.posts,
isPostsLoading: () => state.post.isPostsLoading,
selectedSort: () => state.post.selectedSort,
searchQuery: () => state.post.searchQuery,
page: () => state.post.page,
limit: () => state.post.limit,
totalPages: () => state.post.totalPages,
sortOptions: () => state.post.sortOptions,
}),
...mapGetters({
sortedPosts: 'post/sortedPosts',
sortedAndSearchedPosts: 'post/sortedAndSearchedPosts',
}),
},
watch: {},
}
</script>
<style>
.app__btns {
margin: 15px 0;
display: flex;
justify-content: space-between;
}
.page__wrapper {
display: flex;
margin-top: 15px;
}
.page {
border: 1px solid black;
padding: 10px;
}
.current-page {
border: 3px solid teal;
}
.observer {
height: 30px;
background: green;
}
</style>
使用 vuex,我意识到由于 postModule.js 组件中的
isPostsLoading: false
状态,应用程序正在中断。我们将这个状态传递给 PostsPageWithStore.vue 组件,如果你注释掉这个状态,那么一切正常。如何解决?
注意:阅读Vuex主页上的警告:
Pinia 是新的默认值
[...] 您可以将其视为具有不同名称的 Vuex5。
在
mapState
助手内部,state
不是来自组件上下文。它是函数的参数。命名它 state
是一种可读性约定(您不必保留):
// ...
computed: {
...mapState({
posts: (state) => state.post.posts,
isPostsLoading: (whatever) => whatever.post.isPostsLoading,
selectedSort: (_) => _.post.selectedSort
// ...
})
},
// ...
不过,您可能更喜欢这种语法:
computed: {
...mapState('post', [
'posts', 'isPostsLoading', 'selectedSort', // ...
])
}
// this.posts is this.$store.state.post.posts
// this.isPostsLoading is this.$store.state.post.isPostsLoading
或者,如果你想将状态属性分配给不同的本地名称:
computed: {
...mapState('post', {
localNameForPosts: 'posts',
isLoading: 'isPostsLoading'
// ...
})
}
// this.localNameForPosts is this.$store.state.post.posts
// this.isLoading is this.$store.state.post.isPostsLoading
如果您需要,这些语法也可用于
mapGetters
和 mapActions
。例如:
computed: {
...mapGetters('post', ['sortedPosts', 'sortedAndSearchedPosts']),
}
相关文档: