Vuex数据流实践

最近在写一个课程管理类的Web SPA(单页应用)的工具,在最初的时候想着要用到vue全家桶,即Vue2 + Vuex + VueRouter。但是仅仅看完文档没有任何敏感的直觉(还是代码敲的少),只是拿来用。如今在数据流上出现点问题,在此总结归纳一下。

Talk is cheap. Show me the code.(神翻译:屁话少说,放码过来。)

代码

下面是部分代码:
app.js

// ...
import App from './components/Home.vue'
import store from './store.js'
import routes from './routes.js'
Vue.use(Router)

const router = new Router({
  mode: 'history',
  routes,
})

new Vue({
  el: '#default',
  store,
  router,
  components: { App },
  render: h => h(App),
  beforeCreate () {
    this.$store.dispatch('fetchCourses')
    this.$store.dispatch('fetchChapters')
    this.$store.dispatch('fetchLessons')
// ...
  },

// ...
});

Vue.store = store; 

store.js

import Vue from 'vue'
import Vuex from 'vuex'
import api from './api'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    coursesList: [],
    chaptersList: [],
    lessonsList: [],
// ... 
  },
  mutations: {
    SET_COURSESLIST (state, val) {
      state.coursesList = val
    },
    SET_CHAPTERSLIST (state, val) {
      state.chaptersList = val
    },
    SET_LESSONSLIST (state, val) {
      state.lessonsList = val
    },
// ...
  },
  actions: {
// 调用api获取初始值。
    fetchCourses (context) {
      api.courses(context)
    },
    fetchChapters (context) {
      api.chapters(context)
    },
    fetchLessons (context) {
      api.lessons(context)
    },
// ...
  },
  getters: {
    getCurrentCourse: (state, getters) => (id) => {
// 此处根据传入的 id 值将 state.courseList、 state.chaptersList、 state.lessonsList 拼装成需要的数据格式
      let course_id = id
      let current_course = coursesList.find(course =>course.id === course_id)
      let chapters = state.chaptersList.filter(chapter => chap.course_id === course_id);
      current_course.chapter = chapters
      current_course.chapter.forEach(chap => {
        chap.lesson = [...state.lessonsList.filter(lesson => lesson.chapter_id === chap.id)]
      })
    },
// ...
  },
})

根据vuex文档Action的描述,将异步请求在Action中进行,得到返回值后再提交给Mutation,进行同步赋值。

上面代码中store.getters.getCurrentCourse(id)拼装当前正在查看的课程,最终得到的大致结构如下:

{
  id: 1,
  course_title: '测试课程',
  course_content: '课程内容:这是一个测试课程',
  chapters: [
    {
      id: 1,
      course_id: 1,
      chapter_title: '第一章',
      lessons: [
        { id: 1, chapter_id: 1, lesson_title: '第一节', lesson_content: '章节内容:这是第一节课。'},
        { id: 2, chapter_id: 1, lesson_title: '第二节', lesson_content: '章节内容:这是第二节课。'},
      // ...
      ],
    },
    {
      id: 2,
      course_id: 1,
      chapter_title: '第二章',
      lessons: [
        { id: 1, chapter_id: 1, lesson_title: '第一节', lesson_content: '章节内容:这是第一节课。'},
        { id: 2, chapter_id: 1, lesson_title: '第二节', lesson_content: '章节内容:这是第二节课。'},
      // ...
      ],
    },
  ],
}

api.js

const APP_ROOT = 'http://my.website.com'
export default ({
    courses: function (store) {
    axios.get(APP_ROOT+'/get_courses').then((response) => {
      store.commit('SET_COURSESLIST', response.data)
    })
  },
  chapters: function (store) {
    axios.get(APP_ROOT+'/get_chapters').then((response) => {
      store.commit('SET_CHAPTERSLIST', response.data)
    })
  },
  lessons: function (store) {
    axios.get(APP_ROOT+'/get_lessons').then((response) => {
      store.commit('SET_LESSONSLIST', response.data)
    })
  },
})

上面代码将获取到的返回值提交给mutation。

由于是在客户端是浏览器,用户是可以在任何路由下刷新页面,所以为了保持数据,在生命周期beforeCreate去抓取数据。每次刷新先拿到数据,再进行拼装。

问题

思路是这样,但是实际中,尤其是在当前某个课时的页面刷新有时会出现问题,会经常在store.getters.getCurrentCourse(id)处报错,报错提示没有获取到chaptersList,或者提示没有获取到lessonsList。但是看到 Devtool 中 Vue 的state.chaptersListstate.lessonsList都是有值存在的。

经过排查发现刷新页面后,尤其是在查看当前课时的时候,需要展示完整的课程数据结构,当coursesList获取到并且被赋值,但是chaptersList、lesonsList有时候在还没有被赋值的时候,就已经开始调用store.getters.getCurrentCourse(id)去拼装我们需要的结构了,那这时候肯定会出错了。

本来以为是异步的问题,还在纠结如何能让异步的Action能做到按course->chapter->lesson顺序拿到数据,这样使用完全本末倒置没有理解Action的用法。

解决方法

解决起来其实并不难,只需要将这三个值在同一次请求获取到就解决这个问题了。

但是一次请求的数据量及请求速度,相比之前三次请求的数据量及请求速度,肯定会增大量降低速度,由于现在没有那么大的数据量,差异没有很明显,而且解决了现有的问题,当以后有了需求的时候,就需要对数据量和访问速度进行权衡。

传送门:Vuex中文文档、扩展运算符、对象的扩展运算符

你可能感兴趣的:(Vuex数据流实践)