Vue-Cli—05.Vuex简单使用 / 自定义插件 / 自定义指令

一、初始化Vuex

Vuex是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
如果一份数据需要在多个组件中使用,组件间传值又比较复杂,就可以使用vuex托管数据。

1、安装

npm install vuex --save

2、导入

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

3、创建状态管理对象 store

state选项:定义状态(状态就是数据)。
mutations选项:定义修改状态的方法(注意:这里面只能定义同步方法)。

export default new Vuex.Store({
  // 定义全局状态(状态就是数据)
  state:{
    car:{
        name:'奔驰',
        price:'40W'
    }
  },
  // 定义修改状态的方法
  mutations:{
    //该方法,修改汽车信息
    updateCar(state,val){
        state.car = val
    }
  }
})

4、注册给Vue

// 导入当前项目中的全局状态管理对象
import store from './store'
new Vue({
  // 在vue实例中使用全局状态管理对象
  store,
  render: h => h(App)
}).$mount('#app')

5、简单使用

$store:返回的是当前项目中的全局状态对象。
commit()方法:用于执行指定的mutations里面的方法。

(1)获取数据

在组件中,直接通过$store.state就可以获取到全局状态对象管理的状态数据,直接渲染到页面。

车辆名称:{{ $store.state.car.name }}
车辆价格:{{ $store.state.car.price }}

(2)修改数据

车辆名称:{{ $store.state.car.name }}
车辆价格:{{ $store.state.car.price }}
methods: {
  updateCar() {
    this.$store.commit("updateCar", { name: "奔驰", price: "60W" });
  }
}

二、核心概念

1、state

state选项:定义状态(状态就是数据)。

  state: {
    name:'张三'
  }

通过$store.state.数据名使用。

姓名:{{ $store.state.name }}

2、getters

getters选项:定义计算属性。方法的参数是状态对象。

  getters:{
    // 方法的第一个参数是全局状态
    nameInfo(state){
      return `我的名字叫${state.name}`
    }
  }

通过$store.getters.属性名使用计算属性。

{{ $store.getters.nameInfo }}

3、mutations

mutations选项:定义修改状态的方法(注意:这里的方法一般都是同步方法)。方法的第一个参数是状态对象,第二个参数是新值。

  mutations:{
    // 修改姓名
    updateName(state,val){
      state.name = val
    }
  },

通过commit()方法,调用mutations里面的方法。

this.$store.commit("updateName", '李四');

4、actions

actions选项:定义操作状态的方法(这里的方法可以定义异步方法)。
注意:actions里的方法最好不要直接操作state状态,而是通过调用mutations里面的方法去修改状态。所以,actions直接操作的是mutations。

  state: {
    carAddress:'意大利'
  },
  mutations:{
    //修改汽车的产地
    updateCarAddress(state,val){
      state.carAddress = val
    }
  },
  actions:{
    //修改汽车的产地
    //方法的第一个参数是全局状态管理对象,第二个参数是具体的值
    updateCarAddress(store,val){
      axios.get(val).then(({data})=>{
        // 方式一:这里可以直接修改状态
        // store.state.carAddress = data.address
        // 方式二:通过调用mutations里面的方法修改状态
        store.commit('updateCarAddress',data.address)
      })
    }
  }

通过dispatch()方法,调用actions里面定义的方法。

this.$store.dispatch('updateCarAddress','data/address.json')

5、modules

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

(1)定义模块

namespaced属性:默认情况下,action、mutation 和 getter 是注册在全局命名空间的。通过设置namespaced属性为true,将action、mutation 和 getter全部注册到私有命名空间中。

export default {
    namespaced:true,
    // 状态
    state:{
        planeName:'空客404',
        planePrice:'10Y',
        planeAddress:'中国'
    },
    // 计算属性
    getters:{
        planeInfo(state){
            return `飞机名称:${state.planeName},飞机价格:${state.planePrice},飞机产地:${state.planeAddress}`
        }
    },
    // 同步方法
    mutations:{
        updatePlaneName(state,val){
            state.planeName = val
        },
        updatePlanePrice(state,val){
            state.planePrice = val
        }
    },
    // 异步方法
    actions:{
        updatePlanePrice(store,val){
            setTimeout(() => {
                store.commit('updatePlanePrice',val)
            }, 500);
        }
    }
}

(2)在全局状态管理对象中导入模块

// 导入飞机模块
import plane from './modules/plane.js'

// 创建一个全局状态管理对象并导出
export default new Vuex.Store({
  // 模块
  modules:{
    // 飞机模块
    plane
  }
})

(3)使用模块

① 获取模块中的state状态

要从私有模块中获取数据,方式是:$store.state.模块名称.模块的数据

飞机名称:{{ planeName }}
planeName() {
  return this.$store.state.plane.planeName;
}

② 获取模块中的getters计算属性

要从私有模块中获取计算属性,方式是:$store.getters['模块名/计算属性']

{{planeInfo}}
planeInfo(){
    return this.$store.getters['plane/planeInfo']
}

③ 调用模块中的mutations定义的方法

调用私有模块里面的mutations定义的方法,方式是:$store.commit('模块名/方法名',新值)


updatePlaneName(){
  this.$store.commit('plane/updatePlaneName','波音747')
}

④ 调用模块中的actions定义的方法

调用私有模块里面的actions定义的方法,方式是:$store.dispatch('模块名/方法名',新值)


updatePlanePrice(){
  this.$store.dispatch('plane/updatePlanePrice','20Y')
}

三、Vuex使用

1、计算属性中转

直接在模板中使用全局状态管理数据,表达式会写的很长。所以可以使用计算属性。

  // getters选项定义计算属性
  getters:{
    carInfo(state){
      return `汽车名称:${state.carName},汽车价格:${state.carPrice},汽车产地:${state.carAddress}`
    }
  }

 
 
{{$store.getters.carInfo}}
{{carInfo}}
  //计算属性
  computed:{
    // 汽车信息
    carInfo(){
      // 返回全局状态管理里面的计算属性
      return this.$store.getters.carInfo
    }
  }

2、映射函数

通过映射函数mapState、mapGetters、mapActions、mapMutations,可以将vuex.store中的属性映射到vue实例身上,这样在vue实例中就能访问vuex.store中的属性了,便于操作vuex.store。

(1)导入映射函数

// 从vuex中,导入映射函数
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";

(2)使用映射函数生成计算属性

如果vuex里面state的数据名称 跟 页面中的计算属性名称相同,就可以使用mapState映射函数,自动生成页面中的计算属性。
如果vuex里面getters的数据名称 跟 页面中的计算属性名称相同,就可以使用mapGetters映射函数,自动生成页面中的计算属性。
注意:如果要映射模块里面的state/getters,函数的第一个参数设置为模块的名称。

汽车名称:{{ carName }}
汽车价格:{{ carPrice }}
汽车产地:{{ carAddress }}
{{ carInfo }}

飞机名称:{{ planeName }}
飞机价格:{{ planePrice }}
飞机产地:{{ planeAddress }}
{{ planeInfo }}
computed: {
  // mapState映射state
  ...mapState(["carName", "carPrice", "carAddress"]),
  // mapGetters映射getters
  ...mapGetters(["carInfo"]),

  // 映射私有模块里面的数据
  ...mapState('plane',['planeName','planePrice','planeAddress']),
  ...mapGetters('plane',['planeInfo'])
}

(3)使用映射函数生成方法

如果定义的方法名跟全局管理对象中mutations里面的方法名相同,并且定义的方法会带有一个参数,通过参数传递数据。满足该规则,就可以使用mapMutations映射函数生成方法。
如果定义的方法名跟全局管理对象中actions里面的方法名相同,并且定义的方法会带有一个参数,通过参数传递数据。满足该规则,就可以使用mapActions映射函数生成方法。
注意:如果要映射私有模块中mutations/actions里面的方法,函数的第一个参数设置为模块的名称。





methods: {
  // 映射全局管理对象中mutations里面的方法
  ...mapMutations(["updateCarName", "updateCarPrice"]),
  // 映射全局管理对象中actions里面的方法
  ...mapActions(["updateCarAddress"]),

  // 映射私有模块里面的方法
  ...mapMutations('plane',['updatePlaneName']),
  ...mapActions('plane',['updatePlanePrice'])
}

3、购物车模块

import axios from "axios";
export default {
  namespaced: true,
  state: {
    //商品数组
    goodses: [],
  },
  getters: {
    //总价
    totalPrice(state) {
      return state.goodses.filter((r) => r.ck).map((r) => r.price * r.count).reduce((a, b) => a + b, 0);
    },
    // 是否全选
    isCkAll(state) {
      return state.goodses.length>0 & state.goodses.every((r) => r.ck);
    },
  },
  mutations: {
    // 加载数据的同步方法
    loadGoodses(state, val) {
      state.goodses = val;
    },
    // 设置所有商品的状态
    ckAll(state, val) {
      state.goodses.forEach((r) => {
        r.ck = val;
      });
    },
    // 根据id删除商品
    delGoodsById(state, id) {
      let index = state.goodses.findIndex((r) => r.id == id);
      state.goodses.splice(index, 1);
    },
  },
  actions: {
    // 加载数据的异步方法
    loadGoodses(store, val) {
      axios.get(val).then(({ data }) => {
        store.commit("loadGoodses", data);
      });
    },
    // 设置所有商品的状态
    ckAll(store, val) {
      store.commit("ckAll", val);
    },
    // 根据id删除商品
    delGoodsById(store, id) {
        store.commit("delGoodsById", id);
    },
  },
};



四、key

v-for绑定key值,为什么不建议使用index作为key值?
如果只是展示列表数据,key值可以是索引;如果列表中的数据会经常发生变化,特别是列表数据的位置会发生变化,这时候key一定要设置为对象身上的唯一属性,比如:学号、工号、身份证号、手机号等等。目的是:当列表更新时,会大大提高列表重新渲染的性能损耗。
因为vue在渲染数据时,先将数据生成一份虚拟DOM,再将虚拟DOM生成对应的真实DOM挂载到页面中。当vue中的数据修改后,会重新生成一份虚拟DOM,并跟之前的虚拟DOM进行匹配,如果两份虚拟DOM中的key和key对应的值完全相同,不会重新生成对应的真实DOM;只有key和key对应的值不同的虚拟DOM,才会生成新的真实DOM并挂载的页面中。

  • {{ item }}
data() {
  return {
    list: [
      {
        id: 1001,
        name: "张学友",
        age: 21,
        sex: "男",
      },
      {
        id: 1002,
        name: "郭富城",
        age: 22,
        sex: "男",
      },
      {
        id: 1003,
        name: "黎明",
        age: 24,
        sex: "男",
      },
      {
        id: 1004,
        name: "周润发",
        age: 26,
        sex: "男",
      },
    ],
  };
},
methods: {
  // 添加员工的方法
  add() {
    let em = {
      id: Date.now(),
      name: "蔡依林",
      age: 22,
      sex: "女",
    };
    this.list.unshift(em);
  },
}

五、$nextTick()

$nextTick():需要传一个回调函数。将回调函数里面的代码延迟到DOM渲染完毕后执行。在修改数据之后立即使用它,然后等待 DOM 更新。



data() {
  return {
    // 汽车名称
    carName: "",
    // 汽车数组
    list: [
      {
        id: 1001,
        name: "宝马",
      },
      {
        id: 1002,
        name: "玛莎拉蒂",
      },
    ]
  };
},
methods: {
  addCar() {
    let car = {
      id: Date.now(),
      name: this.carName,
    };
    this.list.push(car);
    this.carName = "";
    // $nextTick方法,需要传一个回调函数。回调函数里面的代码在DOM更新完成后执行。
    this.$nextTick(() => {
      //  让最后一个li元素里面的input元素获取焦点
      //  focus()方法用于为元素设置焦点。
      this.$refs.list.lastChild.lastChild.focus();
    });
  }
}

六、$forceUpdate()

$forceUpdate():进行强制更新。调用这个方法会更新视图和数据,触发updated生命周期。



{{ person }}
data() {
  return {
    person: {
      name: "张学友",
      age: 20,
    }
  };
},
methods: {
  addSex() {
    // 添加响应式属性
    // this.$set(this.person,"sex","男")

    //   直接添加的属性,不具备响应式
    this.person.sex = "男";
    //  通过$forceUpdate()方法,迫使vue实例重新渲染
    this.$forceUpdate();
  }
}

数据更新之前
数据更新之后

七、自定义指令

自定义指令就是一个方法,方法的第一个参数传递的是指令所在的DOM元素,方法的第二个参数是给指令绑定的数据。

1、局部指令

(1)定义指令

data() {
    return {
        car:'

保时捷卡宴真好看

' } }, // 定义局部指令,所有的指令背后都是在操作DOM,我们将这种功能称之为:造轮子。 directives:{ // 注册一个局部自定义指令 'v-red',设置字体颜色为红色 red:function(el){ el.style.color="red" }, // 注册一个局部自定义指令 'v-myhtml',渲染html标签数据 myhtml(el,bind){ el.innerHTML = bind.value } }

(2)使用指令

好好学习

2、全局指令

(1)定义指令

// 定义全局自定义指令
import Vue from 'vue'
Vue.directive('mycolor',function(el,bind){
    el.style.color = bind.value
})

(2)main.js文件中导入指令

// 导入全局自定义指令
import './directives'

(3)使用指令

好好学习Vue

八、自定义插件

1、定义插件

定义一个插件,插件就是将给Vue添加的全局成员,归类到一起去,这样做利于后期维护。
插件本质上就是一个对象,该对象中必须包含一个install方法,方法的第一个参数是Vue,第二个参数是配置对象。install()方法,会在use的时候执行。Vue.use(插件),这里的Vue会作为install方法的第一个参数。

// 插件本质上就是一个对象
export default {
    // 该对象中必须包含一个install()方法
    install:function(Vue,options){
        // 可以直接给Vue添加成员
        Vue.sayHi = function(){
            console.log('大家好!我是Vue');
        },
        Vue.msg = "欢迎使用插件",
        // 可以在Vue的原型上扩展成员
        Vue.prototype.sayHello = function(){
            console.log('哈哈!我是Vue原型上的方法');
        },
        // 给Vue混入成员
        Vue.mixin({
            data() {
                return {
                    plane:{
                        name:'奔驰',
                        price:'100W'
                    }
                }
            },
            methods: {
                showPlane(){
                    console.log(this.plane.name,this.plane.price);
                }
            },
        }),
        // 注册全局组件
        Vue.component('b-box', {
            // 在脚手架环境中,只能通过渲染函数定义全局组件
            render(h) {
                return h('div',this.$slots.default)
            },
        }),
        // 注册全局指令
        Vue.directive('bgcolor', function(el,bind){
            el.style.backgroundColor = bind.value
        })
    }
}

2、导入插件

// 导入自定义插件
import myPlugin from './plugins'
// 注意:一定要use
Vue.use(myPlugin)

3、使用插件







我是淡蓝色
哈哈

你可能感兴趣的:(Vue-Cli—05.Vuex简单使用 / 自定义插件 / 自定义指令)