从插件组件说到vue的slot上监听
最开始写网页的时候我很崩溃,为什么写的代码很快就不能看了,一坨一坨的,对自己写的东西觉得非常没有安全感,以至于很长一段时间里我都在做重复而难以维护的事情。我感觉前端开发有两件事儿是特别需要重视的,一个就是可维护性,一个就是复用性。
可维护性有很多因素,我觉得最重要的就是模块化,遥想现在的老系统的代码都是要么js一坨,要么script标签引入一坨而且顺序还得小心翼翼,真是太惨了。。。
而可复用性就是今天要记录的一些东西,虽然说的是vue里具体的解决方案。可是很多东西都是相通的。
比如最开始的时候:
第一天,我有一个table页面,好的,我html一画,js数据一拿,事件一监听完工;
第二天,我又有一个这样的table页面,差不多,好的,我把昨天写的copy过来,改改,完工;
第三天,我又有一个这样的table页面,也差不多,好的个鬼。。。。我要疯了,难道我还要copy吗?好吧,忍了。。。copy去
第四天,好了我没有同样的页面,总算松了一口气,可是需求让我把每个table都加上排序功能,好的,啊。。不对,我要加在哪里。。。第四天我总于崩溃了。。。
后来,我发现了有jq插件这种好东西,尼玛啊我怎么这么傻,用用用起来,最开始觉得还挺好用呢?那为什么好用呢?研究一下吧,发现就是一个道理,把常用的东西封装起来!就如同我想用锤子,不再是每次拿个铁,然后段成平的,然后按上把手,用完扔掉,而是直接买一把万能的锤子不就得了。扯远了,jq插件它大致的思路是什么呢?
html写个模板 =》js里读取模板 =》处理配置参数 =》处理事件监听 =》渲染到页面里 =》暴露参数和方法给用户使用
这下好了,我每次写table的时候不再copy了,html里把模板一写,js钩子一挂;js里传好参数和添加需要的回调,最后初始化运行;完事儿,好的,这样开发好像比之前爽歪歪了。
又n天过去了。。。每天写table啊,真是table跟我有愁啊,每次页面都要先去写html,然后再切换到js啊。然后和其他插件混合在一起,有些插件要删除?好吧,仔细的寻找html和js,才能小心的把两处都删除掉。。。感受到了么,我是不是应该把html模板和js搞在一起啊,可是大家不都说逻辑和样式分离么,哎。。。管他呢,我觉得html不算样式啊,css才是哇,html和js写一起多好维护啊,好吧。。然后就开始了在js里拼接字符串。。。
好的,这下引入也好办了,html只用写个钩子就行了,嗯嗯。。感觉好一些了。。。可是js里每次都要写初始化语句啊,真是。。好吧,那就在钩子上做手脚,加data-*来特殊标识插件,然后js插件里自动初始化,好的。。反正又少了一句。。
可是插件慢慢多了起来,这种方式好像也有问题了,比如:
- 命名冲突
- js里拼字符串很酸爽啊
- 各个插件代码调用风格差异挺大,很别扭有木有
- 页面js钩子太多,看一眼页面不知道用了什么有木有
- 不同插件想套嵌,什么?没错想套嵌。。。
- 。。。
郁闷之时,这时候听说了组件化,什么什么啊?好的试试vue吧。。。矣,上面的问题好像都解决的不错啊,哎。。不要为了学框架而学啊,能解决问题就是好技术是不?
其实是想记录这么一种情况的,可是竟然扯了这么多。。。。
就是我有一个组件,我想其中一些内容是定制的而不是写死的,如果是jq插件呢,就是把它弄成一个可配参数,然后用户传一堆html字符串过去。。。然后我想监听自己传的内容,jq插件就再给一个可配参数,把监听函数传个它(html字符串中预设好js钩子,在可配函数绑在这个钩子上)。vue里呢?组件世界里提供了slot的概念,其实内部也是编译自定义的slot到组件的里,可是怎么监听这个slot的事件呢?先看看作者是怎么说的:(上英文。。。)
You cannot listen to events on . It can end up rendering anything: text, plain element, multiple nodes... the behavior will be unpredictable.
It seems you are trying to make a slot container communicate with a slot child - in most cases this means the two components are coupled by-design, so you can do something like this.$parent.$emit(...) from the child, and listen to that event in the parent with this.$on(...).
I am still open to ideas on improving the ways slot parent and child can communicate, but events on doesn't really sound like a right direction to me.
大致意思是:你不能直接在上监听。slot会渲染的东西太多了,例如文字、单个元素、多个节点等,这些都是不可预测的行为。如果你想让slot和slot的子组件通信,在大多数情况下意味着使用slot的组件和slot包裹的组件是coupled by-design的,就是说是需要一起规划的组件并不是独立的,所以你可以这么做:
this.$parent.$emit(...) // 在slot的组件里直接触发父组件的事件
this.$on(...) // 在父组件上注册好事件等待slot的组件来通信
作者说他也在思考如何提高slot的父组件和子组件的通信方式,但是直接<slot @="">这种方式应该不是一个对的方向。
例如:我们要开发一个平铺的单选筛选组件:
东 | 南 | 西 |北 | 中
我们希望我们调用的代码是这个样的:
<x-select-text class="ml-10" @x-select-item-clicked="changeStatus" :activeId="typeId">
<x-select-text-item id="0">东</x-select-text-item>
<x-select-text-item id="4">南</x-select-text-item>
<x-select-text-item id="1">西</x-select-text-item>
<x-select-text-item id="3">北</x-select-text-item>
<x-select-text-item id="2">中</x-select-text-item>
</x-select-text>
我们的组件该怎么写呢?是的,我们靠slot来完成:
<template>
<div class="x-select-text">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'XSelectText',
data () {
return {}
},
props: {
activeId: {
type: [String, Number],
default: ''
},
seprator: {
type: String,
default: '|'
}
}
}
</script>
<style lang="scss" scoped>
.x-select-text {
display: inline-block;
vertical-align: middle;
&:before,&:after{
display: table;
content: "";
clear: both;
}
}
</style>
<template>
<span class="x-select-text-item">
<span class="x-select-text-item__inner" @click="clicked" :class="{highlight: activeId === id}">
<slot></slot>
</span><span class="x-select-text__separator"> {{ seprator }} </span>
</span>
</template>
<script>
export default {
name: 'XSelectTextItem',
props: {
id: {
type: [String, Number],
default: ''
}
},
computed: {
activeId () {
return this.$parent.activeId
},
seprator () {
return this.$parent.seprator
}
},
methods: {
clicked () {
this.$parent.$emit('x-select-item-clicked', {
id: this.id,
content: this.$slots.default[0].text
})
}
}
}
</script>
<style lang="scss" scoped>
.x-select-text-item {
float: left;
font-size: 16px;
line-height: 1;
&:last-child .x-select-text__separator {
display: none;
}
}
.x-select-text-item__inner {
cursor: pointer;
}
.x-select-text__separator {
color: rgb(191, 203, 217);
margin: 0px 8px;
}
</style>
恩,是的,就是靠this.$parent建立起slot子组件和父组件的通信关系的,使用起来比只写一个组件,例如传给一个数组[{name:'东', id="1"},...]清爽的多,参数的格式也更灵活多变,易于扩展吧。
哎~