【Vue项目总结】组件通信处理方案
栏目: JavaScript · 发布时间: 5年前
内容简介: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>
效果如下:
祖孙组件
有时候我们可能会碰到组件间的无限嵌套,这是我们使用 props
时无法向下无限极传递数据的,这是我们可以用到 provide/inject
; provide
可以向其子孙组件传递数据,而不关子孙组件的层级有多深,使用 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>
效果如下:
provide
和 inject
绑定并不是可响应的。我们可以通过传递祖父级的实例 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>
效果如下:
使用 observable
让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。
示例:
// Grand provide() { this.read = Vue.observable({ msg: '' }) return { read: this.read }; }
效果如下:
兄弟组件
同级别组件相互间的通信,我们可以使用 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>
效果如下:
v-model
与 sync
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"/>
效果如下:
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"/>
效果如下:
$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" />
通过在父组件中输入值可以看到子组件数据也同时更新了
$attrs
与 $listeners
$attrs
可以通过 v-bind="$attrs"
将组件上的特新都(class 和 style 除外)传入内部组件;传入的值与 inheritAttrs
的设置有关,通常封装高级组件。
当我们 inheritAttrs
设置 true
;组件渲染DOM时写在组件的特性会渲染上去;
$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.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 };
总结
项目中组件的通信方式大概常用的是上面几种方案,我们可以通过不同的方式来实现组件通信,但是选择合适组件通信方式可以使我们事半功倍。写的不当之处,望指正~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。