隔山打牛
顾名思义,就是隔着一层组件,中间封装的组件不动, 只动最底层组件和引用的组件, 这样子就做到了解耦,还可以把底层的值抛到引用组件, 处理我们想要的事务, 从而实现了组件的封装
背景
这是我做 avue-form 二开时碰到的一个问题,中间的一个组件(avue-form), 是使用了 avue 中间的插件, 下面是avue-form预览组件的核心代码:
ini
<el-drawer title="预览"
:visible.sync="previewVisible"
size="60%"
append-to-body
:before-close="handleBeforeClose">
<avue-form v-if="previewVisible"
ref="form"
class="afd-preview-form"
:option="option"
v-model="form"
@submit="handlePreviewSubmit"></avue-form>
<div class="afd-drawer-foot">
<el-button size="medium"
type="primary"
@click="handlePreviewSubmit">确定</el-button>
<el-button size="medium"
type="danger"
@click="handleBeforeClose">取消</el-button>
</div>
</el-drawer>
而我自定义的组件也是挂载在avue-form里面的, 属于最底层,我想要上抛事件, 但我又不想去扩展第三方的插件, 我该怎么办? 第一个我想到的, 用event-bus吧, 这个随意在组件里面穿梭都可以, 但通过以往的经验, 这个方法是下下策, 因为后期这样子的代码根本没法维护, 就像我很少用minxin和provide一样, 这些都是实在没办法了才用。我下载了第三方avue源码,试图能得到些启发。
vue
<component :is="getComponent(column.type,column.component)"
v-model="text"
v-bind="Object.assign(column,$uploadFun(column))"
v-on="event"
:column="Object.assign(column,params)"
:dic="dic"
:box-type="boxType"
ref="temp"
:disabled="column.disabled || disabled"
:readonly="column.readonly || readonly"
:placeholder="getPlaceholder(column)"
:props="column.props || props"
:propsHttp="column.propsHttp || propsHttp"
:size="column.size || size"
:table-data="tableData"
:type="type || column.type"
:column-slot="columnSlot"
@keyup.enter.native="enterChange">
<span v-if="params.html"
v-html="params.html"></span>
<template slot-scope="scope"
v-for="item in getSlotName(column,'T',$scopedSlots)?[column]:[]">
<slot :name="getSlotName(item,'T')"
v-bind="scope"></slot>
</template>
<template v-for="item in columnSlot"
slot-scope="scope"
:slot="item">
<slot v-bind="scope"
:name="item"></slot>
</template>
</component>
看到这里,我有点恼火,咱平常封装组件时,中间组件不都是写v-on="$listeners"和v-bind="$attrs"
么, 这样子最外层组件可以直接绑定值,传到最里层,最里面事件也可以上抛到最外层,我试着在底层自定义组件抛事件,外头啥都接不到!!!但v-on="event"是啥玩意??
能看出门道或者,这么用过的童鞋可以就不用往下看了,嗯,这篇文章就是解释这个v-on="event"的,没明白的,那么接着往下看:
js
computed: {
event () {
return this.column.event || {}
}
}
它是一个computed计算属性,还是一个对象??
搞个demo,深度剖析
后面,我弄了个简单demo,照猫画虎,还真让抛事件成功了!!!上demo:
这里我有三个组件,three是孙组件,贴三个组件的代码后面再讲解耦原理
这是App的
vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld :event='event' :msg="msg"/>
<div>{{text}}</div>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
data(){
return {
msg:'Welcome to Your Vue.js App',
event:{
agree:(val)=>{
this.hello(val)
}
}
}
},
methods:{
hello(val){
this.text=val
}
},
components: {
HelloWorld
}
}
</script>
这是HelloWorld的
vue
<template>
<div class="hello">
<h1 >{{ msg }}</h1>
<three v-on='event' :msg='msg'></three>
</div>
</template>
<script>
import three from './three.vue';
export default {
name: 'HelloWorld',
components:{
three },
props: {
event:Object,
msg: String
},
computed:{
},
methods:{
}
}
</script>
这是three的
vue
<template>
<button @click="click">dianji</button>
</template>
<script>
export default {
name:'ThreeSasdf',
methods:{
click(){
this.$emit('agree','我是three的消息!')
}
}
}
</script>
解析
这顿操作下来,我在app中真的把three中上抛的消息 '我是three的消息!' 给打印出来了!!也就是达到了,扩展组件three上抛事件,在app中定义
js
` event:{
agree:(val)=>{
this.hello(val)
},
....
}`
这样子可以处理各种事务了,其中event是个对象,里面还可以继续定义其他很多的事件名,只要在孙组件中上抛了,那么祖组件event对应定义相应的名字接收,完美的略过了中间层helloword组件,达到了封装和解耦的目的!!其中需要注意的是,app中的this是指向app组件的,有兴趣的可以克隆该工程,地址如下:
如果各位感觉文字对你有帮助,请点击一个小小的赞,您的点赞是我继续创作的动力源泉!!