Vue组件间数据通信的几种方式
1 props emit
Vue组件间的响应式通信方式之一,数据通过props以单向数据流的形式从父组件流动到子组件中。
单向流动性会防止子组件意外改变父级组件的状态,避免应用的数据流向难以理解。
不能处理非父子组件通信的情况
父传子 props
通过bind指令,父组件数据单向下行流向子组件的props
子传父 $emit
子组件抛发事件a并传入数据作为形参,在父组件挂载结点的子组件标签中监听a事件.
由于父级模板里的所有内容都是在父级作用域中编译的,因此a事件的回调函数写在父组件的methods里面.
methods中拿到形参后用this设置给data数据,这样就完成子组件到父组件的传递
<div id="app">
<h1>{{username}}</h1>
<myinput :value="username" @myinput="inputHandler"></myinput>
</div>
<script>
const myinput = ('my-input', {
props:['username'],
template: `<input type='text' :value='username' @input="myemit">`,
methods:{
myemit(){
console.log(this.$el.value);
this.$emit("myinput",this.$el.value);
}
}
});
var vm = new Vue({
el: "#app",
data: {
username: 'aaa'
},
methods: {
inputHandler(val) {
this.username = val;
}
},
components: {
myinput
}
});
</script>
2 边界条件
边界条件主要通过vue的三个实例属性实现根组件,父组件,子组件之间的数据传递
vm.$parent:当前组件的父实例.
vm.$root:组件树的根实例,如果没有父实例,vm.$root指向自己.
vm.$refs:注册过 ref 特性 的所有 DOM 元素和组件实例.
ref 与 $ref
ref用于给子元素或子组件注册引用信息
$ref
是非响应式的,在组件渲染完成之后生效,只能用于父组件拿到子组件实例,不能跨级
任意设置了ref属性的子组件标签,都可以在其父组件中通过this.$refs.refvalue
拿到对应的实例
<body>
<div id="app">
<son ref="getSon"></son>
</div>
<script>
const son = {
template: `
<h1>
parent-----{{this.$parent.print}}
<br>
root-------{{this.$root.print}}
</h1>
`,
data() {
return {
print: "this is son"
}
},
}
var vm = new Vue({
el: "#app",
data: {
print: 'this is root'
},
components: {
son
},
mounted() {
console.log(this.$refs.getSon.print);
}
});
</script>
</body>
3 依赖注入
作为组件数据传递的一种方式,依赖注入允许我们提供给任何后代组件的数据。
只需要在父组件中通过provide函数返回一个数据对象,就可以在后代组件中使用inject注入provide提供的数据,实现组件数据的通信。
依赖注入的特点在于,不局限于父子组件,不论多深的嵌套关系,都可以用它实现组件数据之间的通信。
还有一点值得注意,依赖注入是非响应式的。
<body>
<div id="app">
<son></son>
</div>
<script>
const grandson = {
inject: ['getApple'],
template: `
<h2>get -{{getApple}}- from grandfather</h2>
`
}
const son = {
template: `
<h1>
<grandson></grandson>
</h1>
`,
components: {
grandson
}
}
var vm = new Vue({
el: "#app",
data: {
apple: 'a fresh apple'
},
provide: function () {
return {
getApple: this.apple
}
},
components: {
son
}
});
</script>
</body>
4 eventbus 事件总线
使用一个全局的vue实例作为载体,通过这个载体去监听和抛发事件
将需要传递的数据放入$emit函数的参数列表,只要在某一组件中监听了此事件,就可以通过回调函数拿到此数据。
基于此,通过事件总线,可以实现非父子组件之间的数据通信,如兄弟级、跨级组件通信。
<body>
<div id="app">
<son ></son>
<button @click="giveFoodHandler">抛发事件</button>
</div>
<script>
var eventbus = new Vue();
const grandson = {
template: `
<h2 >get -{{getApple}}- from grandfather</h2>
`,
data() {
return {
getApple: ""
}
},
created(){
eventbus.$on("getFood",(apple)=>{
this.getApple = apple;
})
}
}
const son = {
template: `
<h1>
<grandson></grandson>
</h1>
`,
components: {
grandson
}
}
var vm = new Vue({
el: "#app",
data: {
apple: 'a fresh apple'
},
components: {
son
},
methods: {
giveFoodHandler() {
eventbus.$emit("getFood", this.apple);
}
}
});
</script>
</body>
5 vuex 状态管理
vuex是vue的状态管理模式,它的核心是store仓库对象,它主要用于存放应用的状态.
在Vuex中,更改状态的唯一方式是mutation,mutation是同步的,
异步逻辑需要放在action中,但它最后也需要commit到mutation中实现状态的更改,
使用store的commit与dispatch来进行组件之间的响应式数据状态管理.
<body>
<div id="app">
<counter-btn type="decrement"></counter-btn>
<counter-span></counter-span>
<counter-btn type="increment"></counter-btn>
</div>
<script>
Vue.component('counter-btn', {
props: ['type'],
template: `
<button @click='handleClick' >{{btntext}}</button>
`,
computed: {
btntext() {
return this.type === 'decrement' ? '-' : '+';
}
},
methods: {
handleClick() {
if (this.type === 'decrement') {
store.commit('decrement', { num: 1 });
return;
}
this.$store.dispatch('increment', { num: 1 });
}
}
});
Vue.component('counter-span', {
template: `<span>{{$store.state.count}}</span>`,
});
var store = new Vuex.Store({
state: {
count: 0
},
//同步
mutations: {
increment(state, obj) {
state.count += obj.num;
},
decrement(state, obj) {
state.count -= obj.num;
}
},
// 异步
actions: {
increment(state, obj) {
setTimeout(() => store.commit('increment', obj), 0);
}
}
});
var vm = new Vue({
el: "#app",
store
});
</script>
</body>
总结
pros emit:
优点:
响应式
单向数据流防止子组件意外改变父级组件的状态,常用于父子组件通信
缺点:
但是单行向下的bind+props在父子嵌套过多的场景下显得过于繁琐。
无法运用在非父子组件通信的场景中
边界条件
优点:
使用起来较为方便,在子组件标签上增加ref属性即可使用
缺点:
在组件渲染完成之后生效
非响应式
依赖注入
优点:
不像props emit用起来那么繁琐
祖先组件不需要知道那些后代组件使用它提供的属性,后代组件也不需要知道被注入的属性来自哪里。
不局限于父子组件,不论多深的嵌套关系,都可以用它实现组件数据之间的通信。
缺点:
但这样的方式也有一个明显的缺陷,那就是不支持响应式
事件总线
优点:
eventbus是全局的,意味着可以在任意两个组件中通过事件的抛发和监听来传递数据。
缺陷:
不支持响应式
vuex
优点:
通过store状态管理,可以灵活的在父子/非父子/兄弟组件之间进行响应式数据传递
缺点
刷新浏览器会使得vuex中的state置为初始状态,考虑结合本地存储如localstorage进行使用
如果数据不存在跨组件共享,那么也不建议使用vuex
vuex不适合小型项目