Vue组件之间通信方式总结

Vue组件之间通信方式总结

Vue组件之间通信方式总结

一、props$emit (v-model)

父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件来做到的。我们最早接触vue框架的双向绑定指令v-model其实是在子组件中vue框架自动帮你绑定了一个value的prop属性,同时在子组件内部值value改变的时候通过this.$emit(‘input’,value)帮你广播了一个input事件出去,同时还在外面的组件帮你监听了这个input事件,从而使你用v-model绑定的属性实现双向绑定的效果。

props和$emit示例

1.Vue.component('child', {
2. data() {
3. return {
4. mymessage: this.message
5. }
6. },
7. template: `
8. <div>
9. <input type="text" v-model="mymessage" @input="passData(mymessage)"> </div>
10. `,
11. props: ['message'], //得到父组件传递过来的数据
12. methods: {
13. passData(val) {
14. //触发父组件中的事件
15. this.$emit('getChildData', val)
16. }
17. }
18.})
19.Vue.component('parent', {
20. template: `
21. <div>
22. <p>this is parent compoent!</p>
23. <child :message="message" v-on:getChildData="getChildData"></child>
24. </div>
25. `,
26. data() {
27. return {
28. message: 'hello'
29. }
30. },
31. methods: {
32. //执行子组件触发的事件
33. getChildData(val) {
34. console.log(val)
35. }
36. }
37.})
38.var app = new Vue({
39. el: '#app',
40. template:
41. `<div>
42. <parent></parent>
43. </div>`
44.})

在上面的例子中,有父组件parent和子组件child。

  1. 父组件传递了message数据给子组件,并且通过v-on绑定了一个getChildData事件来监听子组件的触发事件。

  2. 子组件通过props得到相关的message数据,最后通过this.$emit触发了getChildData事件。

v-model示例

1.Vue.component('child', {
2. props: {
3. value: String, //v-model会自动传递一个字段为value的prop属性
4. },
5. data() {
6. return {
7. mymessage: this.value
8. }
9. },
10. methods: {
11. changeValue() {
12. this.$emit('input', this.mymessage); //通过如此调用可以改变父组件上v-model绑定的值
13. }
14. },
15. template:
16. `<div>
17. <input type="text" v-model="mymessage" @change="changeValue">
18. </div>`
19.});
20.Vue.component('parent', {
21. template:
22. `<div>
23. <p> this is parent compoent! </p>
24. <p></p>
25. <child v-model="message"></child>
26. </div>`,
27. data() {
28. return {
29. message: 'hello'
30. }
31. }
32.});
33.var app = new Vue({
34. el: '#app',
35. template: `
36. <div >
37. <parent > < /parent>
38. </div>`
39.});

二、parentchildren

父组件可以通过$children来访问子组件实例内部的属性与方法,同样的道理,子组件可以用过$parent来访问父组件实例内部的属性与方法。

示例代码

1.Vue.component('child', {
2. props: {
3. value: String, //v-model会自动传递一个字段为value的prop属性
4. },
5. data() {
6. return {
7. mymessage: this.value
8. }
9. },
10. methods: {
11. changeValue() {
12. this.$parent.message = this.mymessage; //通过如此调用可以改变父组件的值
13. }
14. },
15. template:
16. `<div>
17. <input type="text" v-model="mymessage" @change="changeValue">
18. </div>`
19.});
20.Vue.component('parent', {
21. template:
22. `<div>
23. <p> this is parent compoent! </p>
24. <button @click = "changeChildValue">test</button>
25. <child></child>
26. </div>`,
27. methods: {
28. changeChildValue() {
29. this.$children[0].mymessage = 'hello';
30. }
31. },
32. data() {
33. return {
34. message: 'hello'
35. }
36. }
37.});
38.var app = new Vue({
39. el: '#app',
40. template:
41. `<div>
42. <parent></parent>
43. </div>`
44.});

三、attrslisteners

第一种方式处理父子组件之间的数据传输有一个问题:如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想传递数据给组件C怎么办呢?
如果采用第一种方法,我们必须让组件A通过prop传递消息给组件B,组件B在通过prop传递消息给组件C;要是组件A和组件C之间有更多的组件,那采用这种方式就很复杂了。Vue 2.4开始提供了$attrs$listeners来解决这个问题,能够让组件A之间传递消息给组件C。

示例代码

1.Vue.component('C', {
2. template:
3. `<div>
4. <input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)">
5. </div>`,
6. methods: {
7. passCData(val) {
8. //触发父组件A中的事件
9. this.$emit('getCData', val)
10. }
11. }
12.});
13.
14.Vue.component('B', {
15. data() {
16. return {
17. mymessage: this.message
18. }
19. },
20. template:
21. `<div>
22. <input type="text" v-model="mymessage" @input="passData(mymessage)">
23. <!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
24. <!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
25. <C v-bind="$attrs" v-on="$listeners"></C>
26. </div>`,
27. props: ['message'],//得到父组件传递过来的数据
28. methods: {
29. passData(val) {
30. //触发父组件中的事件
31. this.$emit('getChildData', val)
32. }
33. }
34.});
35.Vue.component('A', {
36. template:
37. `<div>
38. <p>this is parent compoent!</p>
39. <B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
40. </div>`,
41. data() {
42. return {
43. message: 'hello',
44. messagec: 'hello c' //传递给c组件的数据
45. }
46. },
47. methods: {
48. getChildData(val) {
49. console.log('这是来自B组件的数据')
50. },
51. //执行C子组件触发的事件
52. getCData(val) {
53. console.log("这是来自C组件的数据:" + val)
54. }
55. }
56.});
57.var app = new Vue({
58. el: '#app',
59. template:
60. `<div>
61. <A></A>
62. </div>`
63.});

四、中央事件总线

上面几种方式处理的都是父子组件之间的数据传递,而如果两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式。新建一个Vue事件bus对象,然后通过bus.$emit触发事件,bus.$on监听触发的事件。

Alt text

示例代码

1.Vue.component('brother1', {
2. data() {
3. return {
4. mymessage: 'hello brother1'
5. }
6. },
7. template:
8. `<div>
9. <p>this is brother1 compoent!</p>
10. <input type="text" v-model="mymessage" @input="passData(mymessage)">
11. </div>`,
12. methods: {
13. passData(val) {
14. //触发全局事件globalEvent
15. bus.$emit('globalEvent', val);
16. }
17. }
18.});
19.Vue.component('brother2', {
20. template:
21. `<div>
22. <p>this is brother2 compoent!</p>
23. <p>brother1传递过来的数据:</p>
24. </div>`,
25. data() {
26. return {
27. mymessage: 'hello brother2',
28. brothermessage: ''
29. }
30. },
31. mounted() {
32. //绑定全局事件globalEvent
33. bus.$on('globalEvent', (val) => {
34. this.brothermessage = val;
35. })
36. }
37.});
38.//中央事件总线
39.var bus = new Vue();
40.var app = new Vue({
41. el: '#app',
42. template:
43. `<div>
44. <brother1></brother1>
45. <brother2></brother2>
46. </div>`
47.});

五、boradcastdispatch

vue1.0中提供了这种方式,但vue2.0中没有,但很多开源软件都自己封装了这种方式,比如min ui、element ui和iview等。
比如如下代码,一般都作为一个mixins去使用, broadcast是向特定的父组件,触发事件,dispatch是向特定的子组件触发事件,本质上这种方式还是$on$on$on$emit的封装,但在一些基础组件中却很实用。

1.function broadcast(componentName, eventName, params) {
2. this.$children.forEach(child => {
3. var name = child.$options.componentName;
4. if (name === componentName) {
5. child.$emit.apply(child, [eventName].concat(params));
6. } else {
7. broadcast.apply(child, [componentName, eventName].concat(params));
8. }
9. });
10.}
11.export default {
12. methods: {
13. dispatch(componentName, eventName, params) {
14. var parent = this.$parent;
15. var name = parent.$options.componentName;
16. while (parent && (!name || name !== componentName)) {
17. parent = parent.$parent;
18. if (parent) {
19. name = parent.$options.componentName;
20. }
21. }
22. if (parent) {
23. parent.$emit.apply(parent, [eventName].concat(params));
24. }
25. },
26. broadcast(componentName, eventName, params) {
27. broadcast.call(this, componentName, eventName, params);
28. }
29. }
30.};

六、provideinject

父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。

1.Vue.component('child', {
2. inject: ['for'],//得到父组件传递过来的数据
3. data() {
4. return {
5. mymessage: this.for
6. }
7. },
8. template:
9. `<div>
10. <input type="tet" v-model="mymessage">
11. </div>`
12.});
13.Vue.component('parent', {
14. template:
15. `<div>
16. <p>this is parent compoent!</p>
17. <child></child>
18. </div>`,
19. provide: {
20. for: 'test'
21. },
22. data() {
23. return {
24. message: 'hello'
25. }
26. }
27.});
28.var app = new Vue({
29. el: '#app',
30. template:
31. `<div>
32. <parent></parent>
33. </div>`
34.});

七、Vuex处理组件之间的数据交互

如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。

八、sessionStoragelocalStorageCookie

这个是利用缓存来实现页面及应用间的通讯,现在一样可以用于组件间的通讯。

九、URL参数

URL参数和缓存一样的道理都可以用于页面间的数据通讯,但是只适合简单的参数传递,复杂的组件及页面通讯还是建议使用上面介绍的几种方法来实现。

坚持技术分享,您的支持将鼓励我继续创作!
7* WeChat Pay

微信打赏

7* Alipay

支付宝打赏