一、组件间通信方式回顾
1、父组件给子组件传值
子组件中通过props接受数据 props有两种形式,一种是数组形式 一种是对象形式 对象形式可以约定传值类型
父组件中给子组件通过相应属性传值
2、子组件给父组件传值
通过自定义事件触发
<child :fontSize="hFontSize" v-on:enlargeText="hFontSize += $event"></child>
其中这个写法中行内注册事件里的$event,就是子组件触发事件时传入的值,在事件处理函数中是不能使用$event的
3、不相关组件中的传值
需要运动到事件总线或者事件中心,就是生成一个新的vue实例,他的目的是运动$emit和$on用来触发和注册事件
//1-- bus.js
import Vue from 'vue'
export default new Vue()
//2-- sibiling1.vue
import bus from './eventbus'
export default {
props: {
num: Number
},
// 在vue中不建议对props里属性直接修改 ,在create时将props的值赋值给data里
created () {
this.value = this.num
},
data () {
return {
value: -1
}
},
methods: {
sub () {
if (this.value > 1) {
this.value--
// 用新生成的bus也就是vue实例触发自定义函数numchange
bus.$emit('numchange', this.value)
}
},
add () {
this.value++
bus.$emit('numchange', this.value)
}
}
}
// 3--- sibiling2.vue
import bus from './eventbus'
export default {
data () {
return {
msg: ''
}
},
created () {
// 在另一个组件中的create里用新生成的vue实例里的$on去注册自定义事件
bus.$on('numchange', (value) => {
this.msg = `您选择了${value}件商品`
})
}
}
4、通过ref获取子组件
ref的两个作用:1、在普通HTML标签中使用ref获取的是DOM,2、在组件标签上使用ref,获取到的是组件实例
// --------------------child
<template>
<div>
<h1>ref Child</h1>
<input ref="input" type="text" v-model="value">
</div>
</template>
<script>
export default {
data () {
return {
value: ''
}
},
methods: {
focus () {
this.$refs.input.focus()
}
}
}
//-------------------parent
<template>
<div>
<h1>ref Parent</h1>
<child ref="c"></child>
</div>
</template>
<script>
import child from './04-Child'
export default {
components: {
child
},
mounted () {
this.$refs.c.focus()
this.$refs.c.value = 'hello input'
}
}
二、Vuex
1、vuex 基本概念
什么时候使用vuex? 非必要的时候不要使用vuex比如项目不大和组件中通信不多的时候不用使用vuex付出的价值小于成本。
在中大型的单页面应用中使用vuex带来的收益会更大一些 多个视图依赖同一状态;来自不同视图的行为需要变更同一状态
2、vuex的核心概念
1、state是我们管理的全局状态,把状态绑定到视图上
2、用户和视图交互的时候通过dispatch分发actions,此时不提交mutation是因为在actions中会处理一些异步逻辑,在处理完异步请求后可以提交mutation进行状态的更改
3、mutation必须是同步的,同步状态的更改都要通过mutation,这样做的目的是可以通过mutation追踪所有状态的变化
store:是仓库store是使用vuex的核心每一个应用有且只能有一个store,store是一个容器包含一个应用的大部分状态,我们不能改变store中的状态,必须使用mutation的形式提交状态
state:是状态保存在store中,因为store是唯一的所以状态也是唯一的,称为单一状态树,但是所有状态都保存在state中的话会让程序难以维护,可以通过模块解决该问题,这里的状态时响应式的
Getter:就像vue里面的计算属性,方便从一个属性派生出其他的值,它内部可以对计算的结果进行缓存,只有当依赖的状态发生改变的时候才会重新计算
Mutation:状态的变化必须通过提交mutation来完成
Action:action和mutation类似,action可以进行异步的操作,内部改变状态的时候都需要提交mutation
Module:如果使用单一状态树,所有的状态都会集中到一个对象上,当应用变得复杂时store对象就会变得相当臃肿,为了解决这个问题vuex用module分割成模块便于维护
3、state
在页面中使用state的时候总会重复出现this.$state.属性 为了解决这个问题在vuex中提供了mapState这个方法,在计算属性中使用mapState vuex可以自动将store里的state属性转换成计算属性中对应的属性
computed: {
// ...mapState(['count', 'msg'])
...mapState({ num: 'count', message: 'msg' }),
}
mapState中接受一个数组,数组中传入的是需要取出的属性
如果视图中已经存在count和message属性,那么我们就可以传入对象,就可以修改映射的计算属性的名字,前面是你修改成的属性名,后面是需要映射的属性名
4、Getter
getters: {
reverseMsg (state) {
return state.msg.split('').reverse().join('')
}
},
上面是在Store中的写法
同样在视图页面中也可以用mapGetter映射getters中的属性,他映射过来的同样是一个对象,需要用...相位扩展符去展开
computed: {
...mapGetters(['reverseMsg']),
},
接受一个数组数组中是getters中的属性名
5、Mutation
mutations: {
increate (state, payload) {
state.count += payload
}
},
在仓库的mutation中的increate方法传递两个参数,一个是state,一个是载荷,payload载荷就是调用方法时传递的额外参数
在视图中用$store.commit()调用commit中的方法,传入的参数第一个是mutation中的方法名,第二个参数是额外参数载荷
在视图中可以调用mapMutation方法映射仓库中mutation里的方法,同样他映射过来的是一个对象,对象中存储的方法需要用展开符展开
methods: {
...mapMutations(['increate']),
}
6、Actions
actions: {
increateAsync (context, payload) {
setTimeout(() => {
context.commit('increate', payload)
}, 2000)
}
},
actions中的increateAsync方法接受两个参数,第一个参数是context上下文,里面保存着仓库中的state,mutation和getters,第二个参数同样是载荷调用方法时传递的额外参数
context里的commit的第一个参数就是mutation中的方法名
在视图中通过$store.dispatch()调用actions中的异步方法,第一个参数接受actions中的方法名,第二个参数是传入的额外参数载荷
同样在视同中可以通过...mapActions进行映射
methods: {
...mapActions(['increateAsync']),
}
6、Module
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
}
在store的模块文件夹下到处state、getters、mutations、actions
import products from './modules/products'
modules: {
products,
cart
}
在index.js的文件夹下引入projects模块
在视图中如果模块开启了命名空间在mapState中调用的时候第一个参数可以传入命名空间的名字也就是引入的模块的名字
...mapState('products', ['products'])