ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

大前端- vue - vuex状态管理

2022-02-06 20:03:32  阅读:145  来源: 互联网

标签:vue 前端 state products 组件 import vuex store


一:组件间通信方式

state:驱动应用的数据源。

view:以声明方式将state映射到视图。

actions:响应在view上的用户输入导致的状态变化。

状态管理:
请添加图片描述

组件间的通信方式:
  1. 父组件给子之间的传值(父子组件)
    1. 子组件中通过props接收数据。2. 父组件中给子组件通过相应属性传值

子组件

<template>
  <div>
    <h1>Props Down Child</h1>
    <h2>{{ title }}</h2>
  </div>
</template>

<script>
export default {
 // props接收父组件传递的参数
  // props: ['title'],
  props: {
    title: String
  }
}
</script>

父组件

<template>
  <div>
    <h1>Props Down Parent</h1>
    // 传递title给子组件
    <child title="My journey with Vue"></child>
  </div>
</template>

<script>
import child from './01-Child'
export default {
  components: {
    child
  }
}
</script>

  1. 子组件给父组件传值
    子组件:
    this.$emit('enlargeText', 0.1)
<template>
  <div>
    <h1 :style="{ fontSize: fontSize + 'em' }">Props Down Child</h1>
    <button @click="handler">文字增大</button>
  </div>
</template>

<script>
export default {
  props: {
    fontSize: Number
  },
  methods: {
    handler () {
     // 在子组件中使用父组件传递过来的事件,并传递值
      this.$emit('enlargeText', 0.1)
    }
  }
}
</script>

父组件:

<template>
  <div>
    <h1 :style="{ fontSize: hFontSize + 'em'}">Event Up Parent</h1>

    这里的文字不需要变化
	// @enlargeText="enlargeText" 传递事件给子组件
    <child :fontSize="hFontSize" @enlargeText="enlargeText"></child>
    <child :fontSize="hFontSize" @enlargeText="enlargeText"></child>
    <child :fontSize="hFontSize" @enlargeText="hFontSize += $event"></child>
  </div>
</template>

<script>
import child from './02-Child'
export default {
  components: {
    child
  },
  data () {
    return {
      hFontSize: 1
    }
  },
  methods: {
    enlargeText (size) {
      this.hFontSize += size
    }
  }
}
</script>

  1. 不相关组件之间的传值
    也是使用事件的方式:bus:事件中心

eventBus.js

import Vue from 'vue'
export default new Vue()

子组件1:

bus.$emit('numchange', this.value) bus.$emit触发事件, numchange:自定义事件名,this.value传递的参数

<template>
  <div>
    <h1>Event Bus Sibling01</h1>
    <div class="number" @click="sub">-</div>
    <input type="text" style="width: 30px; text-align: center" :value="value">
    <div class="number" @click="add">+</div>
  </div>
</template>

<script>
import bus from './eventbus'

export default {
  props: {
    num: Number
  },
  created () {
    this.value = this.num
  },
  data () {
    return {
      value: -1
    }
  },
  methods: {
    sub () {
      if (this.value > 1) {
        this.value--
        //  bus.$emit('numchange', this.value)触发事件
        // numchange:自定义事件名,this.value传递的参数
        bus.$emit('numchange', this.value)
      }
    },
    add () {
      this.value++
      bus.$emit('numchange', this.value)
    }
  }
}
</script>

<style>
.number {
  display: inline-block;
  cursor: pointer;
  width: 20px;
  text-align: center;
}
</style>

子组件2
bus.$on:注册:自定义事件numchange,() => {}:回调函数

<template>
  <div>
    <h1>Event Bus Sibling02</h1>

    <div>{{ msg }}</div>
  </div>
</template>

<script>
import bus from './eventbus'
export default {
  data () {
    return {
      msg: ''
    }
  },
  created () {
  
    bus.$on('numchange', (value) => {
      this.msg = `您选择了${value}件商品`
    })
  }
}
</script>
  1. 组件间通信方式回顾-通过 ref 获取子组件
    ref的2个作用:
    1. 在普通的html标签上使用ref,获取到的是DOM
    2. 在组件标签上使用ref,获取到的是组件实例
    child.vue
<template>
  <div>
    <h1>ref Child</h1>
    <input ref="input" type="text" v-model="value">
  </div>
</template>

<script>
export default {
  data () {
    return {
      value: ''
    }
  },
  methods: {
    focus () {
     // 在普通的html 上使用ref
      this.$refs.input.focus()
    }
  }
}
</script>

parent.vue

<template>
  <div>
    <h1>ref Parent</h1>

    <child ref="c"></child>
  </div>
</template>

<script>
import child from './04-Child'
export default {
  components: {
    child
  },
  mounted () {
  	// 在组件上使用ref,获取到的是组件的实例
    this.$refs.c.focus()
    this.$refs.c.value = 'hello input'
  }
}
</script>
  1. 状态管理方案 - Vuex
    Vuex:集中式的状态管理
    在组件中不能直接更改状态的属性,如果想要更该需要在store中actions中的定义函数修改,这样做的好处是:能记录store中的数据的所有的变更。

解决的问题:多个视图依赖同一状态
来自不同视图的行为需要变更同一状态

store.js

// state: 存储的状态
// setUserNameAction:通过视图和用户交互的时候更改状态用的。
// debug:方便调试,如果为true,在通过action修改数据的时候会打印日志。
export default {
  debug: true,
  state: {
    user: {
      name: 'xiaomao',
      age: 18,
      sex: '男'
    }
  },
  setUserNameAction (name) {
    if (this.debug) {
      console.log('setUserNameAction triggered:', name)
    }
    this.state.user.name = name
  }
}

在组件中使用:

  1. 引入store
  2. 使用store.state.user
<template>
  <div>
    <h1>componentA</h1>
    user name: {{ sharedState.user.name }}
    <button @click="change">Change Info</button>
  </div>
</template>

<script>
// 1. 引入store
import store from './store'
export default {
  methods: {
    change () {
    // 使用store中的action更改数据
      store.setUserNameAction('componentA')
    }
  },
  data () {
    return {
      privateState: {},
      // 使用:store.state
      sharedState: store.state
    }
  }
}
</script>

vuex核心概念和基本使用

什么是vuex?
vuex是专门为vuejs设计的状态管理库。
vuex采用集中式的方式存储需要共享的状态。
vuex的作用是进行状态管理,解决复杂组件通信,数据共享。
vuex集成到了devtools中,提供了time-travel时光旅行历史回滚功能。

什么情况下使用vuex?
非必要的情况不需要使用vuex。
大型的单页应用程序:1.多个视图依赖同一状态。2.来自不同视图的行为需要变更同一状态。

vuex的核心概念:

请添加图片描述
上述图的描述:
state:state中存储数据
vue components:把状态绑定到组件中渲染到用户界面展示给用户。用户可以和视图交互,例如点击按钮(加入购物车):通过dispatch分发actions,此处不直接提交mutations,因为actions中可以做异步操作,(购买的时候需要发送异步请求,异步请求结束之后,再通过提交mutation记录状态的更改,)
mutations: mutations必须是同步的,所有状态的更改都要通过mutations,这样做的目的是:通过mutations可以追踪到所有状态的改变,阅读代码的时候更容易分析应用内部的状态改变,还可以记录每一次状态的改变,实现高级的调试功能。

核心概念:
Store:
state:响应式的状态
Getter: 有点类似于vue中的计算属性,方便从一个属性派生出其他的值。内部可以对计算的结果进行缓存,只有当依赖的状态发生改变的时候才会重新计算。
Mutation:状态的变化必须通过提交mutation来完成。
Actions: action和mutation类似,不同的是action可以进行异步的操作。内部改变状态的时候都需要提交mutation
Module:模块,当数据变得非常大的时候可以采用模块的方式。

如果使用?整体的代码结构
store.js

import Vue from 'vue'
import Vuex from 'vuex'


Vue.use(Vuex)

export default new Vuex.Store({

  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
})

main.js

import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

console.log(store)

使用state:

import Vue from 'vue'
import Vuex from 'vuex'


Vue.use(Vuex)

export default new Vuex.Store({

  state: {
  	count:0,
  	msg: 'hello vuex'
  },
  getters: {
  	// 把msg倒序输出
  	reverseMsg(state) {
		return state.msg.split('').reverse().join()
	}
  },
  mutations: {
  },
  actions: {
  },
})

在组件中使用:
方法1: $store.state.count

<template>
  <div id="app">
     count:{{ $store.state.count }}
    msg: {{ $store.state.msg }}
{{count}}
{{msg}}
   </div>
<template>

方法2:mapState

import { mapState } from 'vuex'
computed: {
	// 方法1:写成数组的方式
	//...mapState(['count', 'msg'])
	// 方法2:函数方式的写法
	count: state => state.count,
	msg: state => state.msg,
	// 方法3:写成对象的形式,可以解决命名冲突的问题
	...mapState({num: 'count', message: 'msg'}),
}

Gutter(在视图中显示数据): 类似于计算属性,想把store中的数据处理一下再显示到页面上。

 getters: {
  	// 把msg倒序输出
  	reverseMsg(state) {
		return state.msg.split('').reverse().join()
	}
  },

在组件中使用:
方式1: 直接在模版中使用:$store.getters.reverseMsg方式2:使用mapGetters`


<div>
	{{$store.getters.reverseMsg}}
	{{reverseMsg}}
</div>
<script>
	import { mapGetters} from 'vuex'
	computed: {
		...mapGetters([reverseMsg])
	}
</script>

Mutation(修改状态):如何修改状态?状态的修改必须经过提交mutation,mutation必须是同步执行,这样可以保证能够在mutation中收集到所有的状态修改。不要在mutation中使用异步方式,如果需要使用异步则使用action
用法:

// 点击【增加】按钮
mutations: {
	increate(state, paylod) {
		state.count + = paylod
	}
}

在组件中使用:
方式1:$store.commit('increate', 2)

<button @click="$store.commit('increate', 2)">增加</button>

方式2: mapMutations

<button @click="increate(3)">增加</button>


import {mapMutations} from 'Vuex'
method:{
	...mapMutations(['increate']),
}

Action: 如果需要使用异步操作,则使用action,当异步结束后如果需要更改状态,需要提mutation来修改state。因为所有的状态更改都是使用mutation。例如:如果需要获取商品数据,则需要在action 中发送请求,异步执行完毕,获取到商品数据之后,需要再提交mutation,把数据记录到state中。

Action使用:

actions: {
	increateAsync(context, payload) {
	// setTimeout是异步任务
      setTimeout(() => {
        context.commit('increate', payload)
      }, 2000)		
	}
}

在组件中使用:
方法1:$store.dispatch('increateAsync', 5)
方法2: mapActions

<button @click="$store.dispatch('increateAsync', 5)">Action</button> 

<button @click="increateAsync(6)">Action</button>

import { mapActions } from 'vuex'
methods: {
    ...mapActions(['increateAsync']),
  }

Module:模块化处理state

目录结构:
请添加图片描述
index.js

import Vue from 'vue'
import Vuex from 'vuex'
// 模块化处理state
import products from './modules/products'
import cart from './modules/cart'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0,
    msg: 'Hello Vuex'
  },
  getters: {
    reverseMsg (state) {
      return state.msg.split('').reverse().join('')
    }
  },
  mutations: {
    increate (state, payload) {
      state.count += payload
    }
  },
  actions: {
    increateAsync (context, payload) {
      setTimeout(() => {
        context.commit('increate', payload)
      }, 2000)
    }
  },
  modules: {
    products,
    cart
  }
})

modules/cart.js

const state = {}
const getters = {}
const mutations = {}
const actions = {}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}

modules/products.js

const state = {
  products: [
    { id: 1, title: 'iPhone 11', price: 8000 },
    { id: 2, title: 'iPhone 12', price: 10000 }
  ]
}
const getters = {}
const mutations = {
  setProducts (state, payload) {
    state.products = payload
  }
}
const actions = {}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}

在组件中使用模块的方法和state属性
方法1:$store.state.products.products. 第一个products指的是模块的名字,第2个products指的是state中的属性
方法2:mapMutations

products: {{ $store.state.products.products }} <br>
<button @click="$store.commit('setProducts', [])">Mutation</button>

方法2:

 products: {{ products }} <br>
<button @click="setProducts([])">Mutation</button>

import {mapMutations} from 'vuex'
computed: {
	// 需要注意:mapState的第一个参数:products指的是模块的名字
	// 第2个参数:['products']指的是state中的属性
    ...mapState('products', ['products'])
  },
methods: {
	// 需要注意:mapMutations的第一个参数:products指的是模块的名字
	// 第2个参数:['setProducts']指的是mutation中的方法名字
    ...mapMutations('products', ['setProducts'])
  }

vuex严格模式:
所有的状态更改必须经过mutation,但是这只是一个口头约定,如果在组件中$store.state.count = 1也是可以修改的的,不会抛出错误,但是这样状态state的修改就变得不可追踪,devtools页没有办法追踪,开启严格模式之后,如果这样修改的话,就会抛出错误。

开启严格模式:strict: process.env.NODE_ENV !== 'production',

import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'

Vue.use(Vuex)

export default new Vuex.Store({
 // 开启严格模式
  strict: process.env.NODE_ENV !== 'production',
  state: {
    count: 0,
    msg: 'Hello Vuex'
  },
  getters: {
    reverseMsg (state) {
      return state.msg.split('').reverse().join('')
    }
  },
  mutations: {
    increate (state, payload) {
      state.count += payload
    }
  },
  actions: {
    increateAsync (context, payload) {
      setTimeout(() => {
        context.commit('increate', payload)
      }, 2000)
    }
  },
  modules: {
    products,
    cart
  }
})

开启之后的报错:
请添加图片描述
不要在生产模式开启,因为会深度监听影响性能。

购物车案例

购物车功能:
1.用到了node写的接口:node server.js
2.
页面:
请添加图片描述

请添加图片描述
请添加图片描述

当刷新购物车页面之后当,前购物车中的数据是存储在本地的。

项目代码地址:
三个组件:商品列表组件(products.vue),
购物车组件(cart.vue),
鼠标悬浮【我的购物车】展示购物车中的商品组件(pop-cart.vue)

商品列表组件:

模拟实现vuex

myVuex/index.js

let _Vue = null
class Store {
  constructor (options) {
    const {
      state = {},
      getters = {},
      mutations = {},
      actions = {}
    } = options
    // _Vue.observable(state):把数据变成响应式的数据
    this.state = _Vue.observable(state)
    this.getters = Object.create(null)
    Object.keys(getters).forEach(key => {
      Object.defineProperty(this.getters, key, {
        get: () => getters[key](state)
      })
    })
    this._mutations = mutations
    this._actions = actions
  }

  commit (type, payload) {
    this._mutations[type](this.state, payload)
  }

  dispatch (type, payload) {
    this._actions[type](this, payload)
  }
}

function install (Vue) {
  _Vue = Vue
  _Vue.mixin({
    beforeCreate () {
      if (this.$options.store) {
        _Vue.prototype.$store = this.$options.store
      }
    }
  })
}

export default {
  Store,
  install
}

标签:vue,前端,state,products,组件,import,vuex,store
来源: https://blog.csdn.net/weixin_38245947/article/details/122799486

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有