引入 Vuex Store 管理 Vue 组件数据状态的更新和获取
为了更优雅地管理页面组件的数据状态,你可以通过引入 Vuex 来存储和更新博客文章的数据信息。
封装 API 接口请求
学院君之前已经在构建单页面应用项目骨架中安装过 Vuex,所以这里直接使用就好了。
不过,在此之前,我们还要在 resources/js
目录下新建一个 api.js
来统一管理所有 API 接口请求处理:
const base = '/api/posts';
export default {
// 请求文章详情页 API
getPostData (id) {
return axios.get(base + '/' + id);
},
// 请求博客首页 API
getPostsData (page = 1) {
return axios.get(base, {
params: {page: page}
});
},
// 请求博客分类页 API
getCategoryPosts (name, page = 1) {
return axios.get(base + '/category/' + name, {
params: {page: page}
});
},
// 请求分页数据 API
getMorePosts (url) {
return axios.get(url);
}
}
这里,我们仅在每个方法中返回请求处理的 Promise 对象,具体的响应实体和错误处理放到 Vuex Store 中实现。
定义 Vuex Store
接下来,在同级目录下新建 stores.js
来定义 Vuex Store,目前我们的所有数据状态都是围绕博客文章,所以只需要定义一个跟级别的 Store 即可:
import Vue from 'vue';
import Vuex from 'vuex';
import PostAPI from './api';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
postData: {},
postLoaded: false,
postsData: [],
postsLink: {},
postsLoaded: false
},
getters: {
getPostData (state) {
return state.postData;
},
getPostLoaded(state) {
return state.postLoaded;
},
getPostsData(state) {
return state.postsData;
},
getPostsLink(state) {
return state.postsLink;
},
getPostsLoaded(state) {
return state.postsLoaded;
}
},
mutations: {
setPostData(state, post) {
state.postData = post;
},
setPostLoaded(state, loaded) {
state.postLoaded = loaded;
},
setPostsData(state, posts) {
state.postsData = posts;
},
setPostsLink(state, links) {
state.postsLink = links;
},
setPostsLoaded(state, loaded) {
state.postsLoaded = loaded;
}
},
actions: {
loadPost(context, id) {
PostAPI.getPostData(id).then((resp) => {
context.commit('setPostData', resp.data.data);
context.commit('setPostLoaded', true);
}).catch((err) => {
console.log(err);
});
},
loadPosts(context, name = null) {
let request;
if (name) {
request = PostAPI.getCategoryPosts(name);
} else {
request = PostAPI.getPostsData();
}
request.then((resp) => {
context.commit('setPostsData', resp.data.data);
context.commit('setPostsLink', resp.data.links);
context.commit('setPostsLoaded', true);
}).catch((err) => {
console.log(err);
});
},
loadMorePosts(context, url) {
PostAPI.getMorePosts(url).then((resp) => {
let postsData = context.getters.getPostsData;
postsData.push(...resp.data.data);
context.commit('setPostsData', postsData);
context.commit('setPostsLink', resp.data.links);
context.commit('setPostsLoaded', true);
}).catch((err) => {
console.log(err);
});
}
}
});
其中的 state
用于定义该实体的状态字段,getters
用于定义各个状态的「计算属性」,mutations
用于定义各个状态的更新逻辑(与 getters
对应的「setters」),actions
用于初始化不同状态值(真正编写业务逻辑对状态值进行初始化/更新的地方,支持异步操作,状态值的更新通过 commit
提交触发)。
详细的语法细节参考 Vuex 官方文档核心概念部分。
我们在 actions
中通过引入上一步编写的 API 接口请求方法,在请求成功后对状态值进行更新。
其实可以看到,我们这里是将上篇教程中散落在不同 Vue 组件中的 API 请求处理、模型数据属性初始化、更新和获取逻辑拆分到 api.js
和 stores.js
中完成了,这样一来,可以方便对 API 请求、数据状态做统一管理,提高代码复用性,将数据状态和 Vue 组件进行解耦也更加有利于大型项目的模块化开发。
在 Vue 实例中引入
编写好 Vuex Store 后,还需要将其引入到 Vue 实例中,才能在 Vue 组件中使用,我们在 app.js
中编写引入代码如下:
...
import routes from './routes';
import store from './stores';
...
const app = new Vue({
el: '#app',
store,
router: new VueRouter(routes)
});
在 Vue 组件中通过 Vuex 存取数据状态
至此,我们就可以正式在 Vue 页面组件中使用 Vuex 来更新和获取博客文章的数据状态了。
Home.vue
以首页 Home.vue
为例,重构页面组件代码如下:
...
<script>
import PostList from './common/List';
import Loading from "./common/Loading";
export default {
components: {PostList, Loading},
created() {
this.$store.dispatch('loadPosts');
},
computed: {
posts() {
return this.$store.getters.getPostsData;
},
links() {
return this.$store.getters.getPostsLink;
},
loaded() {
return this.$store.getters.getPostsLoaded;
}
},
methods: {
getMorePosts(nextPageUrl) {
this.$store.dispatch('loadMorePosts', nextPageUrl);
}
}
}
</script>
模板代码不需要动,只需要更改脚本代码即可:
- 通过
created
钩子函数定义数据状态初始化逻辑,注意我们需要通过 Store 实例的dispatch
方法调用actions
中的方法; - 定义计算属性,通过 Store 实例中定义的 getters 替换之前的组件模型数据属性值(
posts
、links
、loaded
)获取; - 之前定义的模型属性初始化方法都被迁移到 Vuex Store 了,这里仅保留了
getMorePosts
方法获取分页数据,具体实现也转化成了调用 Vuex Store 中定义的 actions 方法完成。
Category.vue
按照同样的思路重构分类列表 Category.vue
组件的实现代码:
...
<script>
import PostList from './common/List';
import Loading from "./common/Loading";
export default {
components: {PostList, Loading},
data () {
return {
name: ''
}
},
created () {
this.updateCategoryName();
},
watch: {
'$route': 'updateCategoryName'
},
computed: {
posts() {
return this.$store.getters.getPostsData;
},
links() {
return this.$store.getters.getPostsLink;
},
loaded() {
return this.$store.getters.getPostsLoaded;
}
},
methods: {
updateCategoryName () {
this.name = this.$route.params.name;
this.$store.dispatch('loadPosts', this.name);
},
getMorePosts(nextPageUrl) {
this.$store.dispatch('loadMorePosts', nextPageUrl);
}
}
}
</script>
Post.vue
和文章详情页 Post.vue
组件的实现代码:
...
<script>
import PostContent from './common/Content';
import Loading from "./common/Loading";
export default {
components: {PostContent, Loading},
created() {
this.$store.dispatch('loadPost', this.$route.params.id);
},
computed: {
post() {
return this.$store.getters.getPostData;
},
loaded() {
return this.$store.getters.getPostLoaded;
}
}
}
</script>
具体实现的细节就不一一介绍了。
你可以重新编译前端资源,刷新博客应用首页,然后体验分类页和详情页,可以看到页面的渲染效果和上篇教程的效果完成一样。
通过 Vue Devtools 跟踪 Vuex 数据状态
如果页面数据渲染不正常,你还可以在浏览器开发者工具中通过 Vue Devtools 的 Vuex 标签页跟踪各个数据状态的变化,方便调试和定位问题:
本项目的完整代码已提交到 Github 代码仓库:https://github.com/nonfu/demo-spa,为了以示区别,非 Vuex 版被打上了
without-vuex
标签。
No Comments