前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue全家桶之vuex

vue全家桶之vuex

作者头像
一粒小麦
修改2019-07-18 17:52:34
1.5K0
修改2019-07-18 17:52:34
举报
文章被收录于专栏:一Li小麦

状态管理可以简单理解为vue中的某些全局的data属性。 当组件状态增多时,整个应用和状态分散在每个组件和实例中。部分还会出现状态共享。这时最好的方案就是vuex。

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

安装vuex:

代码语言:javascript
复制
// vue add vuex
npm install vuex --save

在src下面新建一个store文件夹:里面有index.js

代码语言:javascript
复制
// vuex
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state:{
    //存放状态
    },
    mutations:{
    //修改状态
    },
    actions:{

    }

})
state

对于业务来说,需要把isLogin

Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

单状态树和模块化并不冲突——在后面的章节里我们会讨论如何将状态和状态变更事件分布到各个子模块中。

代码语言:javascript
复制
//引入store
import store from '../store'
  data () {
    return {
      count:store.state.isLogin,
    }
  },

这种获取方式有点低效了,有没有更好的方式呢?

全局注册

在main.js中:

代码语言:javascript
复制
import store from './store'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  // 把 store 象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
  store,
  router,
  components: { App },
  template: `<App/>`
})

在组件中就能通过this.$store.isLogin访问到了。 还可以进行各种骚操作,比如说,登录后显示“欢迎回来”

代码语言:javascript
复制
 <router-link v-if="!$store.state.isLogin" to='/login'>login</router-link>
<span v-else>welcome back</span>
mutation 定义修改状态的方法

每当创建一个状态,就一定有一个方法去修改它的状态,称之为mutations。如果需要修改store中的值唯一的方法就是提交mutation来修改.

Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

代码语言:javascript
复制
const store = new Vuex.Store({
  state: {
    isLogin:false
  },
  mutations: {
    login (state) {
      // 变更状态
      state.isLogin=true;
    }
  }
})
commit 提交一个“事件”,触发修改

你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:

代码语言:javascript
复制
// login.vue
login() {
      this.$store.commit("login");
      const redirect = this.$route.query.redirect;

      this.$router.push({ path: redirect });

      // console.log(this.$router)
    }
action(dispatch,派发状态)

登录实际上是一个异步操作。 action并不能更新状态,只能提交状态更新事件。 而异步操作都应该是在action中来派发的。

代码语言:javascript
复制
// 在action中也定义一个loginAction方法。接收一个上下文:context
    actions:{
        loginAction(context){
            console.log(context);
            window.setTimeout(()=>{
                context.commit('login')
            },1000)
        }
    }

登录时,通过action派发一个状态更新

代码语言:javascript
复制
// login.vue
    login() {
    //   this.$store.commit("login");
      this.$store.dispatch(`loginAction`)
      const redirect = this.$route.query.redirect;
      this.$router.push({ path: redirect });
    }
  }

打印上下文context,发现这个就是store本身。 现在等1秒之后,成功了。但是应该返回外界操作成功还是失败。所以应当return一个promise对象。

代码语言:javascript
复制
actions:{
        loginAction(context){
            //console.log(context);
            return new Promise(resolve=>{
                window.setTimeout(()=>{
                    context.commit('login');
                    resolve(true)
                },1000);
            })          
        }
    }

loginAction成功之后返回一个resolve,因此可以then做后续判断:

代码语言:javascript
复制
// login.vue
    login() {
          this.$store.dispatch('loginAction').then(isLogin=>{
          if(isLogin){
            const redirect = this.$route.query.redirect||'/';
            this.$router.push({ path: redirect });
          }
        })
      }
    }

那么登录的功能已经完成了,但是还可以给点安慰剂比入,登录之后不让点击按钮:

代码语言:javascript
复制
// login.vue
<template>
  <div>
    <h1>this is login page</h1>

    <button :disabled="loading" @click="login">{{loading?'signing...':'login'}}</button>
  </div>
</template>

<script>
export default {
  data() {
      return {
          loading: false
      }
  },
  methods: {
    login() {
      this.loading=true;
      this.$store.dispatch('loginAction').then(isLogin=>{
          this.loading=false;
          const redirect = this.$route.query.redirect||'/';
          this.$router.push({ path: redirect });
      })
    }
  }
};
</script>

也可以传参数。 甚至可以做各种复杂业务逻辑

代码简化重构

上面的代码,已经有点乱了。比如说,isLogin能否更加简单?

mapState

当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:

代码语言:javascript
复制
// 在单独构建的版本中辅助函数为 Vuex.mapState

import {mapState} from 'vuex'
export default {
  computed: mapState(['isLogin']),
  data() {...}
}

当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。 你可以愉快的用this.isLogin来指代this.$store.state.isLogin了。

对象展开运算符

mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是自从有了对象展开运算符(现处于 ECMAScript 提案 stage-4 阶段),我们可以极大地简化写法:

代码语言:javascript
复制
computed: {
  localComputed () { /* ... */ },
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    // ...
  })
}

使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。

mapActions

当你引入mapActions,也可以用

代码语言:javascript
复制
 methods: {
    ...mapActions['loginActions'],
    login() {
      this.loading=true;
      this.loginAcions.then(isLogin=>{
          this.loading=false;
          const redirect = this.$route.query.redirect||'/';
          this.$router.push({ path: redirect });
      })
    }
  }

登录时需要传参,你可以

代码语言:javascript
复制
 this.loginAcions({username:'dangjingtao',password:'123456'}).then(...

接收函数时:

代码语言:javascript
复制
loginAction(context,payloads){
// payloads就是你的参数。
}
getter(获取状态的计算属性)
代码语言:javascript
复制

// main.js
export default new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

Getter相当于vue中的computed计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算,这里我们可以通过定义vuex的Getter来获取,Getters 可以用于监听、state中的值的变化,返回计算后的结果,这里我们修改Hello World.vue文件如下:

代码语言:javascript
复制
 data () {
    return {
      totdodone:this.$store.getters.doneTodos
    }
  },

也是相当地简单。

模块化

页面多时,还需要对不同页面的对应状态和方法做模块化。

代码语言:javascript
复制
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import Cart from './cart';

Vue.use(Vuex) 
// Cart={...}

export default new Vuex.Store({
    modules:{
        Cart,
    },
    // ..


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-05-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一Li小麦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • state
    • 全局注册
    • mutation 定义修改状态的方法
      • commit 提交一个“事件”,触发修改
      • action(dispatch,派发状态)
      • 代码简化重构
        • mapState
          • 对象展开运算符
            • mapActions
              • getter(获取状态的计算属性)
                • 模块化
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档