美文网首页
Vuex数据流实践

Vuex数据流实践

作者: Julian1009 | 来源:发表于2017-09-08 14:34 被阅读0次

    最近在写一个课程管理类的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数据流实践

          本文链接:https://www.haomeiwen.com/subject/vvhbwttx.html