单向传值(props):父组件向子组件传值,任何类型的值都可以传给一个 prop。
实现概述:父组件通过指令向子组件传值,子组件通过props接收。
// 父组件-App.vue
<template>
<div id="app">
<!-- <com age="19"></com> // 静态传递 prop -->
<com :age="age"></com> <!-- // 动态传递 prop -->
</div>
</template>
<script>
import com from './components/com' // 引入子组件
export default {
name: 'app',
components: {
com // 注册子组件
},
data () {
return {
age: 108 // 父组件定义变量
}
}
}
</script>
// 子组件 com.vue
<template>
<div>child components {{name}} {{age}}</div>
</template>
<script>
export default {
props: ['age'], // 子组件用props接收父组件传递的值
data () {
return {
name: 'com'
}
}
}
</script>
单向传值:子组件向父组件传值,自定义事件。结合父组件传值达到了父子组件通信的功能。
实现概述:
1、父组件通过指令将数组传递给子组件,子组件通过props接收,此时数据流向是从父组件到子组件
2、子组件通过自定义事件将行为传给父组件,父组件触发自定义事件并改变数据源,此时形成一个闭环的数据流
// 子组件 com.vue
<template>
<div>
child components{{age}}
<!-- 子组件通过$emit()自定义事件并可以传参 -->
<button @click="$emit('patch', 66)">给父组件传值</button>
</div>
</template>
<script>
export default {
props: ['age']
}
</script>
<template>
<div id="app">
<!-- 父组件触发子组件的自定义事件 -->
<com :age="ages"
@patch="msg">{{ages}}</com>
</div>
</template>
<script>
import com from './components/com'
export default {
name: 'app',
components: {
com
},
data () {
return {
ages: 18
}
},
methods: {
msg: function (ag) { // 父组件接收子组件的参数及其它操作
this.ages++
window.console.log(ag)
}
}
}
</script>
多组件数据共享: Vuex
我们知道每一个组件都有自己的 data,那么多个组件如何共享数据?这就引出了 state 的概念,可以把多个组件共有的属性统一由state进行管理。但是组件并不能直接访问或操作state里的数据源,而是通过 mutations 来进行如何对数据源进行操作。而改变数据的行为触发由 Actions 来完成,Vue 为 actions的触发提供了一个 commit 的概念,由action 触发通过 mutations 对数据进行操作,从而改变 state 数据源。
那么 vue 的组件(components)如何操作 state 数据源呢?components 通过鼠标、键盘等交互行为触发 (Dispatch) Vuex 的Actions,Actions 对操作进行提交,找到对应的 mutations,进而对 state进行改变。这就是 Vuex 的完整数据流。上代码:
首先,安装 vuex
npm i vuex
新建 store.js
touch src/store.js
Vue中可以存放数据的有组件的 data、computed、props,而state就是放在 computed里。
// store.js
import Vue from 'vue' //引入 vue
import Vuex from 'vuex' // 引入vuex
Vue.use(Vuex) // 将Vue 的全局组件 Vuex 加入到 Vue 的运行框架中
const state = { //数据源
count: 1
}
const mutations = { //定义数据操作方法
increment(state) { // 传入数据源
state.count++ // 操作数据源
},
decrement(state) { // 同上
state.count--
}
}
// actions 不能直接修改数据, 它会接收 vue 组件的用户行为,进一步触发(commit)操作,由mutations 对数据源state进行修改。补充说明:commit 携带一个参数,这个参数就是 mutations 里的某一个行为,这个行为会对数据源state进行操作,并使vue组件重新render数据变化
const actions = { // 定义数据源操作行为
increment: ({
commit
}) => { // 解构赋值
commit('increment') // 对应 mutations里的increment,告诉mutations 进行increment 操作
},
decrement: ({
commit
}) => { // 解构赋值
commit('decrement') // 对应 mutations里的decrement,告诉mutations 进行decrement 操作
}
}
// 导出 vuex 的实例
export default new Vuex.Store({
state,
mutations,
actions
})
项目入口文件:
import Vue from 'vue'
import App from './App'
import store from './store' // 引入 store 文件
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
// 通过在根实例中注册 store 选项,把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件,子组件可以通过 this.$store 访问
}).$mount('#app')
vue组件(component):
在组件中分发 Actions:
在组件中使用 this.$store.dispatch('xxx')
分发 action,或者使用 mapActions
辅助函数将组件的 methods 映射为 store.dispatch
调用(需要先在根节点注入 store
):
// vuex.vue
<template>
<div class="vuex">
vuex {{$store.state.count}}
<!-- 用 $store 取 store 的state数据源 -->
<button @click="increment">增加</button>
<button @click="decrement">删减</button>
</div>
</template>
<script>
import { mapActions } from 'vuex' // 引入 vuex的 actions
export default {
methods: mapActions([ // 将methods 映射为 vuex 的 actions
'increment',
'decrement'
])
}
</script>
延伸:Vuex 模块化
先创建好模块目录及模块文件
mkdir src/store // 创建 store 目录
mkdir src/store/modules // 创建store 模块目录
touch src/store/modules/a.js // 创建 a 模块
touch src/store/modules/b.js // 创建 b 模块
touch src/store/index.js // 创建 store 模块入口文件
touch src/components/a.vue // 创建子组件a
touch src/components/b.vue // 创建子组件b
// a.js
const state = {
money: 10
}
const mutations = {
add(state) {
state.money++
},
reduce(state) {
state.money--
}
}
const actions = {
add: ({
commit
}) => {
commit('add')
},
reduce: ({
commit
}) => {
commit('reduce')
}
}
export default {
namespaced: true, //开启命名空间
state,
mutations,
actions
}
// b.js
const state = {
count: 1
}
const mutations = {
add(state, param) { // mutations 从 actions 上接收 param 参数并且使用
state.count += param
},
reduce(state) {
state.count--
}
}
const actions = {
add: ({
commit
}, param) => { // 这里param参数是从用户交互action行为上接收的参数
commit('add', param)
},
reduce: ({
commit
}) => {
commit('reduce')
}
}
export default {
namespaced: true, //开启命名空间
state,
mutations,
actions
}
// index.js store 入口文件
import Vue from 'vue' // 引入 vue
import Vuex from 'vuex' // 引入 vuex
import money from './modules/a' // 引入模块a
import count from './modules/b' // 引入模块b
Vue.use(Vuex) // 引入Vue的全局模块 Vuex
export default new Vuex.Store({
modules: { // 导出模块
money, // 子模块a
count // 子模块b
}
})
// a.vue
<template>
<div class="a">
<!-- 注意:这里从money模块下取state数据 money -->
page a{{$store.state.money.money}}
<button @click="add">添加</button>
<button @click="reduce">删减</button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
methods: mapActions('money', ['add', 'reduce'])
// 这里mapActions 接收两个参数,第一个参数是store 子模块名,第二个参数是actions的行为
}
</script>
// b.vue
<template>
<div class="b">
<!-- 注意:这里从count模块下取state数据 count-->
page b{{$store.state.count.count}}
<!-- 想要mutation 对数据的操作可控,可以在用户交互的 action上传参,在b.js 的actions 接收参数 -->
<button @click="add(2)">添加</button>
<button @click="reduce">删减</button>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
methods: mapActions('count', ['add', 'reduce'])
// 这里mapActions 接收两个参数,第一个参数是store 子模块名,第二个参数是actions的行为
}
</script>
// App.vue
<template>
<div id="app">
<!--在dom里引入组件a和b-->
<pagea />
<pageb />
</div>
</template>
<script>
import pagea from './components/a' // 引入组件a
import pageb from './components/b' // 引入组件b
export default {
name: 'app',
components: {
pagea, // 注册组件 a
pageb //注册组件 b
}
}
</script>
// 项目主入口文件 main.js
import Vue from 'vue'
import App from './App'
import store from './store/index' // 引入 store 模块入口文件
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store // 把Vuex的实例引入Vue实例中
}).$mount('#app')