经过阅读了官方文档和大量网友的博文,融入本身的想法,整理出对组件通讯的见解,第一次发文,若有不周敬请谅解哦~javascript
组件实例的做用域是孤立的,但组件间的联系和交互不可避免。当组件间进行数据交互时,组件通讯必不可少。
html
因而组件的通讯能够分为两种状况:vue
父子组件间通讯(父传子,子传父),非父子组件通讯。java
step1:父组件用prop自定义属性传递属性,prop有字面量语法和动态语法。字面量只能将数据按字符串的形式传递;动态语法相似于v-bind,能够将父组件数据的实时变化传递给子组件。
vuex
step2:子组件须要用props来显式地声明prop。子组件props的写法有两种:
数组
父将函数用prop传递给子,子在调用这个函数时,将数据做为参数传递给父组件app
<child2 :changeParent="changeMyself"></child2>复制代码
methods: {
// 父组件改变自身数据的方法
changeMyself (chidData) {
this.parentChangeData = chidData
}
}复制代码
子组件中:
export default {
// 子组件显示声明props父组件的函数
props: {
changeParent: {
requried: true, // 表明是否为必传
type: Function // 表明数据类型
}
},
data () {
return {
childData: '我要把数据传给父'
}
}
}复制代码
<button @click="changeParent(childData)">点我传递数据给父组件</button>复制代码
子$emit自定义一个事件,第一个参数为事件名,第二个参数为数据。父v-on绑定一个事件监听器,在绑定的method中以参数形式得到子组件的数据。异步
子组件中:
函数
methods: {
// 自定义事件$emit暴露数据,数据做为参数
changeParent () {
this.$emit('change', this.childData)
}
}复制代码
父组件中:
组件化
<!-- 父组件绑定监听,获得子组件数据--> <child3 @change="changeMyself"></child3>复制代码
methods: {
// 父组件改变自身数据的方法
changeMyself (chidData) {
this.parentChangeData = chidData
}
}复制代码
用ref对子组件进行标记,父组件直接经过this.$refs[子组件ref].[子组件属性/方法]
来得到子组件的数据。
注意:"$refs 只在组件渲染完成后才填充,而且它是非响应式的。它仅仅做为一个直接访问子组件的应急方案——应当避免在模版或计算属性中使用 $refs 。" 官网这么说的,只是应急用,通常不推荐这样使用
思考:单项数据流
Prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,可是反过来不会。这是为了防止子组件无心间修改了父组件的状态,来避免应用的数据流变得难以理解。
另外,每次父组件更新时,子组件的全部 prop 都会更新为最新值。这意味着你不该该在子组件内部改变 prop。若是你这么作了,Vue 会在控制台给出警告。
在两种状况下,咱们很容易忍不住想去修改 prop 中数据:
Prop 做为初始值传入后,子组件想把它看成局部数据来用;
Prop 做为原始数据传入,由子组件处理成其它数据输出。
对这两种状况,正确的应对方式是:
定义一个局部变量,并用 prop 的值初始化它:
定义一个计算属性,处理 prop 的值并返回:
单项数据流致使子组件不能直接去改动父组件的数据。但真实场景中,有不少时候须要在子组件中更改父组件的数据。
父有一个改变其自身数据的方法,用prop传给子,子在须要改变父组件数据的时候调用该方法。(与回调参数方法一致)
子组件经过事件发送,$emit事件将数据传给父组件,父组件监听后本身执行改变自身数据的方法。(与子传父的第二种方法一致)
本身想到的方法:父组件有一个改变自身数据的方法,子经过this.$parent.[父组件方法]直接调用父组件方法,将子组件的数据做为参数传递给父组件从而改变父组件数据
父组件中:
methods: {
// 父组件改变自身数据的方法
changeMyself (chidData) {
this.parentChangeData = chidData
}
}复制代码
子组件中:
export default {
data () {
return {
childData: '我是子要传给父的数据'
}
},
methods: {
change () {
// 经过直接访问的方法调用
this.$parent.changeMyself(this.childData)
}
}
}复制代码
但以上的方法都是基于事件触发的,不能保证子父组件的数据时刻同步
4.使用.sync绑定修饰符在父组件prop中显示强调双向绑定。子组件在watch中使用$emit(‘update:data’,newVal)监听,更新父组件prop传过来的数据,父组件不须要再监听该update方法。
父组件中:
<!-- 显式绑定.sync修饰符 --> <child6 :parentData.sync= "parentData"></child6>复制代码
子组件中:
props: ['parentData']复制代码
// 子组件进行数据监听
watch: {
childData (newVal, oldVal) {
this.$emit('update:parentData', newVal)
}
}复制代码
这里的自定义事件与子传父的自定义事件有一些不一样。
<child6 :parentData.sync="parentData"></child6>
会被扩展为:
<child6 :parentData"parentData" @update:parentData="val => parentData = val"></child6>
思考:
自定义事件发生时候运行的响应表达式是<child6 :parentData="parentData" @update:parentData="val => parentData = val"></child6>
中的 "val => bar = val"
。
在子传父的“经过$emit事件从子组件向父组件中传递数据” 里,自定义事件发生时候运行的响应表达式是:<child @chang="changeMyself"></child>
中的changeMyself
。
对前者, 表达式 val => bar = val
意味着强制让父组件的数据等于子组件传递过来的数据, 这个时候,咱们发现父子组件的地位是平等的。 父能够改变子(数据), 子也能够改变父(数据)。
对后者, changeMyself
是在父组件中定义的, 在这个函数里, 能够对从子组件接受来的数据作任意的操做或处理, 决定权彻底落在父组件中, 也就是: 父能够改变子(数据), 但子不能直接改变父(数据)!, 父中数据的变更只能由它本身决定。
有一个投机取巧的办法:在父用prop传数据给子时,若子组件中props的类型为对象或数组时,能够直接在子组件中修改这个prop来的数据,且不会被vue检测报错,但这样会使得数据流变得更加难以分析,同时,当props的类型为引用数据类型时,要注意在子组件中对对象进行深拷贝,防止隐性修改父组件的对象。
父组件中:
<!-- 父组件把改变数据的方法传给child2,把数据传给child,将数据通讯提高到父组件层次 --> <child2 :changeParent="changeMyself"></child2> <child :parent="parentChangeData"></child>复制代码
child2中:
<!-- 子组件调用方法,将数据做为参数 --> <button @click="changeParent(childData)">点我传递数据给父组件</button>复制代码
export default {
// 子组件显示声明props父组件的函数
props: {
changeParent: {
requried: true, // 表明是否为必传
type: Function // 表明数据类型
}
},
data () {
return {
childData: '我要把数据传给父'
}
}
}复制代码
child中:
// 显示声明props父组件的数据
props: {
parent: {
requried: true, // 表明是否为必传
type: String, // 表明数据类型
default: '我是默认值'
}
}复制代码
把须要跨页面传递的数据放到url后面,跳转到另外页面时直接获取url字符串获取想要的参数便可。
{
path: '/params/:id'
name: '',
component: Sample
}复制代码
<router-link :to="params/12">跳转路由</router-link>复制代码
在跳转后的组件中用$route.params.id去获取到这个id参数为12,但这种只适合传递比较小的数据,数字之类的。
首先建立一个中央时间总线,在须要使用的组件里引入。组件A用this.Bus.$emit('eventName', value)
触发事件,组件B用this.Bus.$on('eventName', value => { this.print(value) })
接收事件。在$emit
以前,必须已经$on
, 所以广泛采用在created钩子中进行$on
.
// 新建Bus.js文件
import Vue from 'vue'
export default new Vue()复制代码
组件A传送数据:
<script>
// 在须要使用的组件中引用
import Bus from '@/components/Bus'
export default {
data () {
return {
childData: '我是兄弟A,把个人数据放进eventBus'
}
},
methods: {
submit () {
// 触发事件
Bus.$emit('change', this.childData)
}
}
}
</script>复制代码
组件B接收数据:
get () {
// 监听接收事件
Bus.$on('change', value => {
this.myData = value
})
}复制代码
// 组件销毁时,解除绑定
destroyed () {
Bus.$off('change')
}复制代码
组件间的通讯均可以用EventBus实现, 不管有多少个组件,只要保证eventName不同就能够了,专门设置一个空的Bus实例来做为中央事件总线,而不是直接访问root,更清晰也更利于管理。
传统的eventBus只负责$emit和$on,与数据没有任何的交集。这使得数据不是“长效”的,只在$emit后生效,而且同一组件屡次生成会屡次$on,路由切换时,还要考虑新组件的绑定和旧组件的解除绑定。
方法:考虑将$on放在Bus中完成,修改Bus为:
// 新建Bus.js文件
// Bus进行监听,将数据直接放在Bus中
import Vue from 'vue'
const Bus = new Vue({
data () {
return {
child7Val: ''
}
},
created () {
this.$on('change', value => {
this.child7Val = value
})
}
})
export default Bus复制代码
发出数据的组件不变
<script>
// 在须要使用的组件中引用
import Bus from '@/components/Bus'
export default {
data () {
return {
childData: '我是兄弟A,把个人数据放进eventBus'
}
},
methods: {
submit () {
// 触发事件
Bus.$emit('change', this.childData)
}
}
}
</script>复制代码
接收数据的组件修改成用计算属性直接从Bus中存取数据,使用计算属性是为了保证数据的动态性。
computed: {
child7Val () {
return Bus.child7Val
}
}复制代码
个人理解是,Vuex至关于一个大型的专门用来储存共享变量的仓库。
在安装Vuex以后,建立store.js文件
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
// 存放共享变量
msg: '我是原始数据'
},
getter: {
// 至关于store中的计算属性
mymsg: state => {
return state.msg
}
},
mutations: {
// 修改state,vue推荐使用大写
MUTATIONSMSG (state, payload) {
state.msg = payload.msg
}
},
actions: {
// 与mutations相似,支持异步
mutationsMsg (context, payload) {
context.commit('MUTATIONSMSG', payload)
}
},
modules: {
app
},
strict: process.env.NODE_ENV !== 'production'
})
export default store复制代码
当使用mutations来修改state时:
组件A使用store中的数据:
<template> <div class="child"> <h3>组件A</h3> {{$store.state.msg}} </div> </template>复制代码
组件B修改store中的数据:
<script>
export default {
data () {
return {
myData: '组件B的数据'
}
},
methods: {
get () {
this.$store.commit('MUTATIONSMSG', this.myData)
}
}
}
</script>复制代码
当使用actions修改state时:
<script>
export default {
data () {
return {
myData: '组件B的数据'
}
},
methods: {
get () {
this.$store.dispatch('mutationsMsg', this.myData)
}
}
}
</script>复制代码
state用来存放共享变量,经过this.$store.state.[变量]来获取共享变量的值。
getter,能够增长一个getter派生状态,(至关于store中的计算属性),store.getters.方法名()用来得到共享变量的值。
mutations用来存放修改state的方法(至关于set)。
actions也是用来存放修改state的方法,不过action是在mutations的基础上进行。在actions先commit mutations里的方法,再由使用actions的组件进行dispatch
组件化的思想就是但愿组件的独立性,数据能互不干扰
经过组件A直接去修改组件B的值,好比双向绑定,虽然方便,但增长了组件间的耦合性。最好就是若是组件A要修改组件B的值,那么就将修改的数据暴露出去,由B获得数据后,自行修改。