基于 Vue.js 的数据统计系统 (三)

3,313 阅读3分钟
上一章我们介绍statistics模块的entry.js,其中定义了路由以及对应的view页面,在view中可以依照Vue.js模块定义的data/created/computed等去书写逻辑。

本章我们介绍以下内容:

  • 如何使用components来加载模块
  • 如何使用echarts来展示数据,以及如何调用echarts等插件
  • 如何引入vuex来管理状态

一,vuex

Vuex 是一个专门为 Vue.js 应用所设计的集中式状态管理架构。它借鉴了 FluxRedux 的设计思想,但简化了概念,并且采用了一种为能更好发挥 Vue.js 数据响应机制而专门设计的实现。
在构建大型应用时,不同组件,模块以及数据的管理会变得很复杂,参考介绍:
我们在单独使用 Vue.js 的时候,通常会把状态储存在组件的内部。也就是说,每一个组件都拥有当前应用状态的一部分,整个应用的状态是分散在各个角落的。然而我们经常会需要把状态的一部分共享给多个组件。一个常见的解决策略为:使用定制的事件系统,让一个组件把一些状态“发送”到其他组件中。这种模式的问题在于,大型组件树中的事件流会很快变得非常繁杂,并且调试时很难去找出究竟哪错了。

这里我们直接步入正题,这里我们安装vuex以及vue-resource(通过api获取数据):

$ cnpm install vuex vue-reource --save

在statistics目录下创建文件夹_store,并创建三个文件:

  • store.js
  • getters.js
  • actions.js

故名思议,getters是从store中获取数据的方法,所以内容很简单:

// getters.js

// 这个 getter 函数会返回 count 的值
// 在 ES6 里你可以写成:
// export const getCount = state => state.count
// export function getCount (state) {
//   return state.count
// }

export const getPostsPage = state => state.posts.page
export const getPostsList = state => state.posts.list
export const getPostsItem = state => state.posts.post
export const getChartList = state => state.chart.list
export const getChartName = state => state.chart.name
export const getChartData = state => state.chart.data

当然了,actions也就一样是很简单了:

// actions.js

// action 会收到 store 作为它的第一个参数
// 既然我们只对事件的分发(dispatch 对象)感兴趣。(state 也可以作为可选项放入)
// 我们可以利用 ES6 的解构(destructuring)功能来简化对参数的导入
// export const incrementCounter = function ({ dispatch, state }) {
//   dispatch('INCREMENT', 1)
// }

export const nextPage = ({dispatch}) => {
  dispatch('POSTS_NEXT', 1)
}

export const prevPage = ({dispatch}) => {
  dispatch('POSTS_PREV', 1)
}

相对复杂一点是store,简介如下:

// store.js

import Vue from 'vue'
import Vuex from 'vuex'
import VueResource from 'vue-resource'

import Api from './_api'
import ChartList from './_chartList'
import ChartData from './_chartData'
import ChartSerializer from './_chartSerializer'

// 告诉 vue “使用” vuex
Vue.use(Vuex)
Vue.use(VueResource)

// 创建一个对象来保存应用启动时的初始状态
// 注意,chart必须有,不然会一开始显示的表类型不正常
// 这里我们定义的其实就是一个posts来显示文章以及charts来存储echarts数据
const state = {
  posts: {
    page: 1,
    list: [
      { 'id': 1, 'title': 'cjb', 'content': 'first' },
      { 'id': 2, 'title': 'sb', 'content': 'second' },
      { 'id': 3, 'title': 'sb pm', 'content': 'third' }
    ],
    post: {},
    data: null
  },
  chart: {
    list: ChartList,
    name: 'pie',
    data: {
      tooltip: {},
      xAxis: {
      },
      yAxis: {},
      series: []
    }
  }
}

// 定义一个方法通过接口处理数据并更新到store
const apiQuery = (path, query) => {
  Vue.http.get(Api[path].url, { params: query }).then((res) => {
    // success callback
    state.chart.data = ChartSerializer[Api[path].serialize](JSON.parse(res.body))
  }, (res) => {
    // error callback
    console.log(res)
  })
}

// 创建一个对象存储一系列我们接下来要写的 mutation 函数
// 这里其实是对应到actions中的各种方法
const mutations = {
  POSTS_NEXT (state, offset) {
    state.posts.page = state.posts.page + offset
  },
  POSTS_PREV (state, offset) {
    state.posts.page = state.posts.page - offset
  },
  CHART_UPDATE (state, cname, query) {
    state.chart.name = cname
    if (cname === 'nano_active' || cname === 'nano_active_map') {
      apiQuery(cname, query)
    } else {
      state.chart.data = ChartData[cname]
    }
  }
}

export default new Vuex.Store({
  state,
  mutations
})

其实简单的说,store模块,其实相当于这样:

store = {}

export default {
    // getters
    getPost: (id, ops) => {
        // code 
        return store.posts[id] 
    }

    // action
    addPost: (id, ops) => {
        // code
        // ajax or something else
        store.posts.push(...) 
    }
}

当然了,其核心是一种状态管理的思路以及实现方法,像上面这种简单粗暴的方法就尽量别用了~


二,使用echarts展示数据






  @import "../_less/v2/base";
  @import "../_less/component/animation";
  .pikaday {
    overflow: auto;
    padding: 1em;
    .mui-textfield {
      margin-left: 16px;
    }
  }

这样呢,数据就可以显示出来了,对了,注意这里的nano和data来自于vuex:


对了,细心的童鞋们肯定发现了这里使用的chart模块,其实内容是这样:






  @import '../../_less/v2/base';
  @import '../../_less/component/animation';
  .echart {
    padding: 1em 0;
    width: 100%;
    height: 100%;
    min-height: 700px;
  }

如上,就可以将数据传入chart的prop中,并且,数据更新的时候,chart自动更新,当然,又有关联了,v-echarts其实是一个directives,我们是注册在entry.js中的:

// entry.js

import echarts from '../_directives/echarts'

Vue.directive('echarts', echarts)

那么问题来了,echart如何使用,我们继续来看Vue.js中自定义指令的方法是酱紫:

export default {
  deep: true,
  params: [],
  paramWatchers: {
  },
  bind: function () {
  },
  update: function (val, oldVal) {
  },
  unbind: function () {
  }
}

所以呀,其实是这样加载:

import echarts from 'echarts'

// 在bind方法中
// this.el其实就是对应的element了,至于其他的事件之类的,这里不多说
// 所以最简单的echarts引入方法是这样:

this.instance = echarts.init(this.el)

完整的简单用法是酱紫:

import Vue from 'vue'
import echarts from 'echarts'
import chinaJson from './echarts/china.json'

echarts.registerMap('china', chinaJson)

export default {
  deep: true,
  params: [],
  paramWatchers: {
  },
  bind: function () {
    var _this = this
    Vue.nextTick(function () {
      // init echarts instance
      // console.error('bind')
      _this.instance = echarts.init(_this.el)
    })
  },
  update: function (val, oldVal) {
    var _this = this
    var options = val

    Vue.nextTick(function () {
      // console.error('update')
      // 这里我加了dispose,否则多个chart时,数据会在原数据基本上更新
      _this.instance.dispose()
      _this.instance = echarts.init(_this.el)
      _this.instance.setOption(options)
    })
  },
  unbind: function () {
    var _this = this
    // console.error('unbind')
    _this.instance.dispose()
  }
}

哈哈哈,好像components被略过了,其实很简单,就是在components对象中加入一个Chart,像这个样子就可以了:

export default {
  name: 'ChartView',
  components: {
    Chart
  }
}

好了,这一张就是这样了,vue-resource的用法很简单,看看代码就懂了,直接上效果图:


参考: