【Vue项目总结】组件通信处理方案

栏目: JavaScript · 发布时间: 6年前

内容简介:Vue组件之间的通信是我们在项目中常常碰到的,而选择合适的通信方式尤为重要,这里总结下作者在实际项目中所运用到的通信方案,如有遗漏,请大家见谅。文章代码具体见Vue中常见的是

Vue组件之间的通信是我们在项目中常常碰到的,而选择合适的通信方式尤为重要,这里总结下作者在实际项目中所运用到的通信方案,如有遗漏,请大家见谅。文章代码具体见 DEMO ;文章首发于 imondo.cn

父子组件

Vue中常见的是 父与子 组件间的通信,所要用到的关键字段是 props$emit

props 接受父组件传给子组件信息的字段,它的类型: Array<string> | Object ;详细解释可以参考 文档

$emit 由子组件触发事件向上传播给父级消息。

示例:

// Parent

<template>
  <div class="parent">
    我是父组件
    <p>来自子级的回答:{{ childMsg }}</p>
    <Child :msg="msg" @click="handleClick"/>
  </div>
</template>
<script>
import Child from "./Child";
export default {
  name: "Parent",
  components: {
    Child
  },
  data() {
    return {
      msg: "叫你吃饭了",
      childMsg: ''
    };
  },
  methods: {
    // 接收来自子级的事件消息
    handleClick(val) {
      this.childMsg = val;
    } 
  }
};
</script>

// Child
<template>
  <div class="child">
    <p>我是子组件</p>
    <p>父级来的信息: {{ msg }}</p>
    <button @click="handleClick">回答父级</button>
  </div>
</template>
<script>
export default {
  name: "Child",
  // 接收父级传来的信息
  props: {
    msg: String
  },
  methods: {
    // 向父级传播事件消息
    handleClick() {
      this.$emit('click', '我知道了');
    }
  },
};
</script>

效果如下:

【Vue项目总结】组件通信处理方案

祖孙组件

有时候我们可能会碰到组件间的无限嵌套,这是我们使用 props 时无法向下无限极传递数据的,这是我们可以用到 provide/injectprovide 可以向其子孙组件传递数据,而不关子孙组件的层级有多深,使用 inject 都可以拿到数据。详细解释可以参考 文档

示例:

// Grand
<template>
  <div class="grand">
    <p>我是祖父</p>
    <Parent />
  </div>
</template>

<script>
export default {
  name: "Grand",
  provide: {
    grandMsg: '都来吃饭'
  },
  components: {
    Parent
  }
};
</script>

// Parent
<template>
  <div class="parent">
    我是父组件
    <p>祖父的信息:{{ grandMsg }}</p>
    <Child />
  </div>
</template>
<script>
import Child from "./Child";
export default {
  name: "Parent",
  components: {
    Child
  },
  inject: {
    grandMsg: {
      default: ''
    }
  }
};

// Child
<template>
  <div class="child">
    <p>我是子组件</p>
    <p>爷爷的信息: {{ grandMsg }}</p>
  </div>
</template>
<script>
export default {
  name: "Child",
  inject: {
    grandMsg: {
      default: ''
    }
  }
};
</script>

效果如下:

【Vue项目总结】组件通信处理方案

provideinject 绑定并不是可响应的。我们可以通过传递祖父级的实例 this 或着使用 observable 来使传递的数据是响应的。

// Grand
<template>
  <div class="grand">
    <p>我是祖父</p>
    <input type="text" v-model="msg" placeholder="输入祖父的消息"/>
    <Parent />
  </div>
</template>
<script>
import Parent from "./Parent";
export default {
  name: "Grand",
  provide() {
    return { // 利用函数 provide 返回对象
      grandVm: this // 传递实例
    };
  },
  ...
  data() {
    return {
      msg: ""
    };
  }
};
</script>

// Child
<template>
  <div class="child">
    <p>我是子组件</p>
    <p>爷爷的实例信息: {{ grandVmMsg }}</p>
  </div>
</template>
<script>
export default {
  name: "Child",
  inject: {
    grandVm: {
      default: () => {
        "";
      }
    }
  },
  computed: {
    grandVmMsg() {
      return this.grandVm.msg;
    }
  }
};
</script>

效果如下:

【Vue项目总结】组件通信处理方案

使用 observable 让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。

示例:

// Grand
provide() {
  this.read = Vue.observable({
    msg: ''
  })
  return {
    read: this.read
  };
}

效果如下:

【Vue项目总结】组件通信处理方案

兄弟组件

同级别组件相互间的通信,我们可以使用 EventBus 或着 Vuex

简单的 EventBus 示例:

// Bus.js
import Vue from "vue";
export default new Vue();

// Child
<div class="child">
  <p>我是子组件一</p>
  <button @click="handleClick">组件一事件</button>
</div>
<script>
import Bus from "./Bus";
export default {
  name: "Child",
  methods: {
    handleClick() {
      Bus.$emit("click", "嘿,老铁");
    }
  }
};
</script>

// ChildOne
<div class="child">
  <p>我是子组件二</p>
  <p>兄弟叫我:{{ msg }}</p>
</div>
<script>
import Bus from "./Bus";
export default {
  name: "ChildOne",
  data() {
    return {
      msg: ""
    };
  },
  mounted() {
    Bus.$on("click", msg => {
      this.msg = msg;
    });
  }
};
</script>

效果如下:

【Vue项目总结】组件通信处理方案

v-modelsync

v-model 是我们用 ElementUI 常见的表单绑定值方式;可以直接修改子组件修改父组件传入的值,简化了我们组件通信的逻辑。

示例:

// ModelCom
<div class="child">
  <input type="text" @input="handleInput">
</div>
<script>
export default {
  name: "ModelSync",
  methods: {
    // 通过绑定表单input中的input事件,向上触发input事件来修改值
    handleInput(e) {
      const value = e.target.value;
      this.$emit('input', value);
    }
  }
};
</script>

// Home
<ModelSync v-model="msg"/>

效果如下:

【Vue项目总结】组件通信处理方案

sync 修饰符也可以是我们的 prop 进行 双向绑定

它需要我们在子组件内触发 this.$emit('update:prop', val) 事件

// ModelCom
<input type="text" @input="handleChange">
...
props: ['value'],
methods: {
  handleChange(e) {
    const value = e.target.value;
    // 触发更新
    this.$emit('update:value', value);
  }
}

// Home
<ModelSync :value.sync="syncMsg"/>

效果如下:

【Vue项目总结】组件通信处理方案

$children$parent

我们可以在组件中通过当前的实例对象访问到组件的 $children$parent 来找到各自组件的父级组件或子级组件实例。

示例:

// Child
<div class="child">
  <p>我是子组件</p>
  <p>来自父组件的msg: {{ msg }}</p>
</div>
...
<script>
export default {
  name: "ChildParent",
  data() {
    return {
      value: ''
    }
  },
  computed: {
    msg() {
      return this.$parent.value;
    }
  },
  created() {
    console.log(this.$parent); 
  }
}

// Parent
<input v-model="value" />

通过在父组件中输入值可以看到子组件数据也同时更新了

【Vue项目总结】组件通信处理方案

$attrs$listeners

$attrs 可以通过 v-bind="$attrs" 将组件上的特新都(class 和 style 除外)传入内部组件;传入的值与 inheritAttrs 的设置有关,通常封装高级组件。

当我们 inheritAttrs 设置 true ;组件渲染DOM时写在组件的特性会渲染上去;

【Vue项目总结】组件通信处理方案

$listeners 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。

具体详细可见 文档

示例:

// Attr
<div class="child">
  <p>Attr</p>
  <p>这是$attrs:{{ placeholder }}</p>
  <p>这是$listeners:{{ test }}</p>
  <button @click="$listeners.click">监听了$listeners</button>
</div>
...
<script>
export default {
  name: "AttrListen",
  inheritAttrs: true,
  props: {
    test: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      placeholder: this.$attrs.placeholder
    }
  }
};
</script>

// Home
<AttrListen placeholder="这是个attr" :test="value" v-bind="$attrs" v-on="$listeners" @click="handleListen"/>

效果如下:

【Vue项目总结】组件通信处理方案

通过封装查找组件

通过封装函数来向上或向下派发事件

参考见 Vue.js组件精讲

// emitter.js
function broadcast(componentName, eventName, params) {
    this.$children.forEach(child => {
        const name = child.$options.name;

        if (name === componentName) {
            child.$emit.apply(child, [eventName].concat(params));
        } else {
            broadcast.apply(child, [componentName, eventName].concat([params]));
        }
    });
}
export default {
    methods: {
        dispatch(componentName, eventName, params) {
            let parent = this.$parent || this.$root;
            let name = parent.$options.name;

            while (parent && (!name || name !== componentName)) {
                parent = parent.$parent;

                if (parent) {
                    name = parent.$options.name;
                }
            }
            if (parent) {
                parent.$emit.apply(parent, [eventName].concat(params));
            }
        },
        broadcast(componentName, eventName, params) {
            broadcast.call(this, componentName, eventName, params);
        }
    }
};

通过封装函数来查找指定任意组件

参考见 Vue.js组件精讲

// 由一个组件,向上找到最近的指定组件
function findComponentUpward (context, componentName) {
    let parent = context.$parent;
    let name = parent.$options.name;

    while (parent && (!name || [componentName].indexOf(name) < 0)) {
        parent = parent.$parent;
        if (parent) name = parent.$options.name;
    }
    return parent;
}
export { findComponentUpward };

// 由一个组件,向上找到所有的指定组件
function findComponentsUpward (context, componentName) {
    let parents = [];
    const parent = context.$parent;

    if (parent) {
        if (parent.$options.name === componentName) parents.push(parent);
        return parents.concat(findComponentsUpward(parent, componentName));
    } else {
        return [];
    }
}
export { findComponentsUpward };

// 由一个组件,向下找到所有指定的组件
function findComponentsDownward (context, componentName) {
    return context.$children.reduce((components, child) => {
        if (child.$options.name === componentName) components.push(child);
        const foundChilds = findComponentsDownward(child, componentName);
        return components.concat(foundChilds);
    }, []);
}
export { findComponentsDownward };

// 由一个组件,找到指定组件的兄弟组件
function findBrothersComponents (context, componentName, exceptMe = true) {
    let res = context.$parent.$children.filter(item => {
        return item.$options.name === componentName;
    });
    let index = res.findIndex(item => item._uid === context._uid);
    if (exceptMe) res.splice(index, 1);
    return res;
}
export { findBrothersComponents };

总结

项目中组件的通信方式大概常用的是上面几种方案,我们可以通过不同的方式来实现组件通信,但是选择合适组件通信方式可以使我们事半功倍。写的不当之处,望指正~


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

HTML Dog

HTML Dog

Patrick Griffiths / New Riders Press / 2006-11-22 / USD 49.99

For readers who want to design Web pages that load quickly, are easy to update, accessible to all, work on all browsers and can be quickly adapted to different media, this comprehensive guide represen......一起来看看 《HTML Dog》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具