韩国毛茸茸的丰满妇女

<strike id="j6ksu"></strike>
<th id="j6ksu"></th>
  1. <th id="j6ksu"></th>

    Vue系列入门教程(15)-vuex核心内容

    Web前端 潘老师 7个月前 (09-12) 837 ℃ (4) 扫码查看

    本文基于上一篇文章:Vue系列入门教程(14)-vuex入门案例
    VueX中的核心成员列表如下:

    • state: 存放状态,类似于vue组件中的data
    • mutations:操作state成员,必须是同步方法,类似于vue组件中的methods
    • getters: 加工state成员给外界,类似于vue组件中的computed
    • actions: 异步操作
    • modules: 模块化状态管理

    state的使用在上篇文章演示过了,存储在 Vuex 中的数据和 Vue 实例中的 data 遵循相同的规则,可以通过this.$store在所有组件中获取。

    有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数,不适应vuex情况下,我们可能会用计算属性:

    computed: {
      doneTodosCount () {
        return this.$store.state.todos.filter(todo => todo.done).length
      }
    }
    

    如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

    案例1:通过属性访问

    1、将上篇文章中的vuex-app项目中store下的index.js修改如下:

    import Vue from 'vue'
    // 导入vuex
    import Vuex from 'vuex'
    //挂载Vuex
    Vue.use(Vuex)
    
    //创建Vuex对象
    const store = new Vuex.Store({
        state:{
            products:[
                {name:"小米手机",price:"5000",country:"CN"},
                {name:"苹果手机",price:"6000",country:"US"},
                {name:"魅族手机",price:"4000",country:"CN"},
                {name:"Vivo手机",price:"3000",country:"CN"}
            ]
        },
        getters:{
            // Getter 接受 state 作为其第一个参数,过滤出中国品牌手机:
            cnProducts:state => {
                return state.products.filter(product => product.country == 'CN');
            }
        }
    })
    // 默认导出store
    export default store
    

    2、在components目录下,新建Product.vue:

    <template>
        <div>
            <ul>
                <li v-for="(item,i) in cnProducts" :key="i">
                    商品名:{{item.name}},价格:{{item.price}},国家:{{item.country}}
                </li>
            </ul>
        </div>
    </template>
     
    <script>
        export default {
            computed:{
                cnProducts(){
                    // Getter 会暴露为 $store.getters 对象,可以以属性的形式访问这些值:
                    return this.$store.getters.cnProducts;
                }
            }
        }
    </script>
     
    <style scoped>
     
    </style>
    

    Getter 会暴露为 $store.getters 对象,可以以属性的形式访问这些值,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的

    3、修改App.vue如下:

    <template>
      <div id="app">
            <Product></Product>
      </div>
    </template>
    
    <script>
    import Product from './components/Product.vue'
    
    export default {
      name: 'app',
      components: {
            Product
      }
    }
    </script>
    
    <style>
    </style>
    

    4、运行测试:
    Vue系列入门教程(15)-vuex核心内容

    案例2:通过方法访问

    你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
    1、修改store\index.jsStore对象如下:

    //创建Vuex对象
    const store = new Vuex.Store({
            state:{
                products:[
                    {name:"小米手机",price:"5000",country:"CN"},
                    {name:"苹果手机",price:"6000",country:"US"},
                    {name:"魅族手机",price:"4000",country:"CN"},
                    {name:"Vivo手机",price:"3000",country:"CN"}
                ]
            },
            getters:{
                // Getter 接受 state 作为其第一个参数,过滤出中国品牌手机:
                getProductByCountry:state =>(country)=> {
                    return state.products.filter(product => product.country == country);
                }
            }
    })
    

    2、修改Product.vue的computed如下:

    computed:{
        cnProducts(){
            // Getter 会暴露为 $store.getters 对象,可以以方法的形式访问这些值:
            return this.$store.getters.getProductByCountry("CN");
        }
    }
    

    3、测试结果还是一样

    注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。

    1、mutations是操作state数据的方法的集合,比如对该数据的修改、增加、删除等等。我 们之前说过在组件中不能直接修改state,更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。

    2、mutations方法都有默认的形参:([state] [,payload])

    • state是当前VueX对象中的state
    • payload是该方法在被调用时传递参数使用的

    案例

    1)将小米手机名称改为华为手机
    修改store\index.jsStore如下:

    //创建Vuex对象
    const store = new Vuex.Store({
            state:{
                products:[
                    {name:"小米手机",price:"5000",country:"CN"},
                    {name:"苹果手机",price:"6000",country:"US"},
                    {name:"魅族手机",price:"4000",country:"CN"},
                    {name:"Vivo手机",price:"3000",country:"CN"}
                ]
            },
            getters:{
                // Getter 接受 state 作为其第一个参数,过滤出中国品牌手机:
                getProductByCountry:state =>(country)=> {
                    return state.products.filter(product => product.country == country);
                }
            },
            mutations:{
                // 将小米手机更名为华为手机
                change(state){
                    state.products[0].name="华为手机";
                }
            }
    })
    

    2)将Product.vue修改如下:

    <template>
        <div>
            <ul>
                <li v-for="(item,i) in cnProducts" :key="i">
                    商品名:{{item.name}},价格:{{item.price}},国家:{{item.country}}
                </li>
            </ul>
            <button @click="handleClick">更改</button>
        </div>
    </template>
     
    <script>
        export default {
            methods:{
                handleClick(){
                    // 通过$store.commit调用mutations中方法
                    this.$store.commit("change");
                }
            },
            computed:{
                cnProducts(){
                    // Getter 会暴露为 $store.getters 对象,可以以属性的形式访问这些值:
                    return this.$store.getters.getProductByCountry("CN");
                }
            }
        }
    </script>
     
    <style scoped>
     
    </style>
    

    3)测试点击按钮:
    Vue系列入门教程(15)-vuex核心内容
    4)在实际生产过程中,会遇到需要在提交某个mutations时需要携带一些参数给方法使用,即 mutation 的 载荷(payload)。
    a)单个值提交时:
    修改store\index.jsmutations如下:

    mutations:{
        // 将小米手机更名为某手机
        change(state,playload){
            state.products[0].name=playload;
        }
    }
    

    修改Product.vuehandleClick如下:

    handleClick(){
        // 通过$store.commit调用mutations中方法,传递单个参数
        this.$store.commit("change","OPPO手机");
    }
    

    测试:
    Vue系列入门教程(15)-vuex核心内容
    b)多个值组装成对象提交:
    修改store\index.jsmutations如下:

    mutations:{
        // 将小米手机更名为某手机
        change(state,playload){
            // state.products[0] = playload;这样写页面不会跟着变
            state.products.splice(0,1,playload);
        }
    }
    

    修改Product.vuehandleClick如下:

    handleClick(){
        // 通过$store.commit调用mutations中方法,提交对象
        this.$store.commit("change",{name:"OPPO手机",price:7000,country:"CN"});
    }
    

    测试:
    Vue系列入门教程(15)-vuex核心内容
    c)另一种提交方式
    修改Product.vuehandleClick如下:

    handleClick(){
        // 通过$store.commit调用mutations中方法,提交对象
        this.$store.commit({
            type:"change",
            phone:{name:"OPPO手机",price:7000,country:"CN"}
        });
    }
    

    修改store\index.jsmutations如下:

    mutations:{
        // 将小米手机更名为某手机
        change(state,playload){
            // playload接收的commit提交的整个对象,type和phone都是其中属性
            state.products.splice(0,1,playload.phone);
        }
    }
    

    测试效果一样。
    5)Mutation 需遵守 Vue 的响应规则
    为了配合Vue的响应式数据,我们在Mutations的方法中,应当使用Vue提供的方法来进行操作。如果使用delete或者xx.xx = xx的形式去删或增,则Vue不能对数据进行实时响应。
    a)Vue.set 为某个对象设置成员的值,若不存在则新增
    例如对state对象中添加一个title成员:
    store\index.jsmutations新增如下方法:

    // 给state添加属性
    addTitle(state){
        Vue.set(state,"title","手机列表");
    }
    

    修改Product.vuetemplate如下:

    <template>
        <div>
            <h1>{{$store.state.title}}</h1>
            <ul>
                <li v-for="(item,i) in cnProducts" :key="i">
                    商品名:{{item.name}},价格:{{item.price}},国家:{{item.country}}
                </li>
            </ul>
            <button @click="handleClick">更改</button>
        </div>
    </template>
    

    修改Product.vuehandleClick如下:

    handleClick(){
        // 通过$store.commit调用mutations中方法,添加属性
        this.$store.commit("addTitle");
    }
    

    测试:
    Vue系列入门教程(15)-vuex核心内容
    b)Vue.delete 删除成员,
    例如删除state对象中的title成员:Vue.delete(state,“title”);在此就不做演示了。

    由于直接在mutation方法中进行异步操作,将会引起数据失效。所以提供了Actions来专门进行异步操作,最终提交mutation方法。
    Actions中的方法有两个默认参数:

    • context 上下文(相当于箭头函数中的this)对象
    • payload 挂载参数

    我们在第三大点中案例中第4)小点c部分将小米手机改为OPPO手机,现在们希望点击更改按钮后果两秒再去执行change方法,由于setTimeout是异步操作,所以需要使用actions
    修改store\index.jsmutationsactions方法如下:

    mutations:{
        change(state,playload){
            state.products.splice(0,1,playload.phone);
        }
    }
    actions:{
        // 两秒后提交change
        asyncChange(context,palyload){
            // playload接收的使整个对象
            setTimeout(()=>{
                context.commit("change",palyload);
            },2000);
        }
    }
    

    在组件中调用,修改Product.vuehandleClick方法如下:

    handleClick(){
        // 通过$store.dispatch调用actions中方法
        this.$store.dispatch({
            type:"asyncChange",
            phone:{name:"OPPO手机",price:7000,country:"CN"}
        });
    }
    

    测试发现点击按钮,2秒后成功更改。

    1、当应用变得非常复杂时,store 对象就有可能变得相当臃肿。可以采用模块化管理模式。Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 statemutationsactionsgetters、甚至是嵌套子模块——从上至下进行同样方式的分割。

    const moduleA = {
      state: () => ({ ... }),
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }
    
    const moduleB = {
      state: () => ({ ... }),
      mutations: { ... },
      actions: { ... }
    }
    
    const store = new Vuex.Store({
      modules: {
        a: moduleA,
        b: moduleB
      }
    })
    
    store.state.a // -> moduleA 的状态
    store.state.b // -> moduleB 的状态
    

    2、组件内调用模块a的状态:

    this.$store.state.a
    

    commit提交或者dispatch某个方法和以前一样,会自动执行所有模块内的对应type的方法:

    this.$store.commit('change')
    this.$store.dispatch('asyncChange')
    

    3、模块的局部状态
    1)对于模块内部的 mutationgetter,接收的第一个参数是模块的局部状态对象。

    const moduleA = {
      state: () => ({
        count: 0
      }),
      mutations: {
        increment (state) {
          // 这里的 `state` 对象是模块的局部状态
          state.count++
        }
      },
    
      getters: {
        doubleCount (state) {
          return state.count * 2
        }
      }
    }
    

    2)同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

    const moduleA = {
      // ...
      actions: {
        incrementIfOddOnRootSum ({ state, commit, rootState }) {
          if ((state.count + rootState.count) % 2 === 1) {
            commit('increment')
          }
        }
      }
    }
    

    3)对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:

    const moduleA = {
      // ...
      getters: {
        sumWithRootCount (state, getters, rootState) {
          return state.count + rootState.count
        }
      }
    }
    

    Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

    • 应用层级的状态应该集中到单个 store 对象中。
    • 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
    • 异步逻辑都应该封装到 action 里面。

    只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

    对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:
    Vue系列入门教程(15)-vuex核心内容
    请参考购物车示例
    但也有人习惯store中目录结构如下:

    store:.
    │  actions.js
    │  getters.js
    │  index.js
    │  mutations.js
    │  mutations_type.js   ##该项为存放mutaions方法常量的文件,按需要可加入
    │
    └─modules
            Astore.js
    

    对应的内容存放在对应的文件中,和以前一样,在index.js中存放并导出storestate中的数据尽量放在index.js中。而modules中的Astore局部模块状态如果多的话也可以进行细分。


    版权声明:本站所有文章,如无特殊说明,均为本站原创。全部下载资源版权归原作者所有。任何个人或组织,若未征得本站同意,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系潘老师进行处理。
    喜欢 (7)
    请潘老师喝杯Coffee吧!】
    分享 (0)

    您必须 微信登录 才能发表评论!

    (4)个小伙伴在吐槽
    1. 模块内部的getters有三个参数(state,getters,rootState),第二个参数是什么?rootstate是根节点的状态还是父节点的状态?如果是根节点的状态,那怎么取到父节点的状态?
      Que Sera Sera 2020-09-16 19:37 回复
      • 潘老师
        state是模块私有的状态,getters是全局的getters,rootState是根节点状态,我不明白你说的父节点是指的什么?是子节点再去分成modules吗?
        潘老师 2020-09-17 08:53 回复
    韩国毛茸茸的丰满妇女