slot与nextTick与translation与v-show不解之缘
为什么会写这个东西呢,主要是再项目遇到了一个贼怪的坑。
先来说明一下项目需求,我有一个滑动scroll
组件,就是类似这样 wrapper是传给滑动库的一个元素,solt是插槽分发
<template>
<div ref="wrapper">
<slot></slot>
</div>
</template>
slot是什么呢? emmm slot怎么用 官方文档有 其实vue中数据是通过props传给子组件的,那么dom怎么办 要传一个 template模板数据嘛,在vue中是可以用slot分发dom的
那slot到底是父组件的还是子组件的呢?
当然是父组件了,类比props,也是把父组件的某个数据传给子组件的。同理slot也是这样,儿子要吃饭,爸爸先占个座,等爸爸来了 就开饭,一个道理。。。
可是儿子怎么知道爸爸的动向呢?
现在来继续讲需求
我在父亲组件里
<template>
<scroll :data="somedata">
<ul v-if="somedata.length">
<li v-for(item in somedata)>{{somedata.some}}</li>
<ul>
</scroll>
</template>
...其余省略
这里的somedata
是计算属性或者异步获取的,标签里slot
分发的dom也就是v-for
异步加载的了,当然他得有数据
这里的需求就是 我父组件的dom数据somedat
变化之后需要将scroll
里插件库调用refresh
方法去更新一下高度或者宽度了。
ok开始解决 这多简单我在子组件里watch
一下传进来的somedata变化不久好了
//scroll.vue中
...省略
props:{
data:{
type:Array,
defalut:()=>[]
}
}
watch:{
data(){
this.scroll.refresh();
}
}
这不就行了。。。 试试告诉我们并没有那么简单...
实际这里dom并没有渲染,因为数据更新到dom变化是需要一些时间的。
就好比 儿子等着吃饭 爸爸只是说我要来 但是没有坐好一样,还是不能开饭
怎么办呢
可能了解 nextTick的已经知道怎么解决了
data(){
this.$nextTick(()=>{
this.scroll.refresh();
}) //这里setTimeout也可以
}
emmmm nextTick是要去看vue源码的 这个之后会再写一篇博客讲明的 ,不懂nextTick可以先去查一下文档
这里父组件数据变化,scroll子组件监听到之后,等待下个轮询中 即slot里dom挂载完成后 调用。。。 嗯 一般使用ok
scroll组件完成,真的是这样的吗?
注意哦
<scroll :data="somedata">
<list :somedata="somedata">
</list>
</scroll>
//list.vue中
<template>
<transition name="list">
<ul v-if="somedata.length">
<li v-for(item in somedata)>{{somedata.some}}</li>
<ul>
</transition>
</template>
...其余省略
<style>
.list-enter-avtive{
translation:all 0.4;
}
.list-enter{
height:0;
}
</style>
这个列表在进入时会有一个过渡的效果,当然他已经挂载在dom树里了 并且js也能获取到dom,但是其高度开始还仍是0
所以插件初始化时就失败了
emmmm 这可咋办啊
仔细看文档 translation中不止是css操作,还有js的钩子回掉函数
这里我们用 after-enter这个函数 这个是过渡完成后的回掉,所以此时已经过渡完毕。
<transition name="list" @after-enter="afterEnter">
...
methods:{
afterEnter(){
this.$emit('listChange');
}
}
外层组件
<scroll ref="scrollRef">
<list :somedata="somedata" @listChange="listChange">
</list>
</scroll>
...
methods:{
listChange(){
this.$scrollRef.refresh();
}
}
这样就可以了,等到父组件中的子组件(不一定是组件也可以dom)过渡完毕,就触发一个事件,调用钩子函数去调用scroll组件里的refresh()方法就可以了。
这样不久行了 不不不这里还不算完
也可能有下种情况
<scroll :data="somedata">
<div v-show="someshow">
//somedata加载一些动态内容
</div>
</scroll>
someshow
这个计算属性,在somedata
更新后其保持为false,直到用户点击或者打开时其表示变为true,这里继续 watch
data的话还时会出现上述问题
这里就需要 watch外面的标志位someshow
//父组件中
watch:{
someshow(newval){
if(newval){
this.$refs.scroll.refresh();
}
}
}
综合上述,这种场景下 不能在子组件里监听dom变化,这样时很麻烦的,需要在父组件里用数据去模拟dom变化,然后传达给子组件,就好比父子吃饭,爸爸坐好告诉儿子,开饭,不能仅靠着儿子听到爸爸讲话,就能断定爸爸已经坐好
题外话, vue这个框架主张的数据驱动,即一切以数据为主,各种监听数据变化,可是这里的‘儿子’如何实现监听父亲是否‘坐好’这个东西呢,这里都是通过一切取巧的方式取解决这个问题,显得比较麻烦,子组件怎么去监听dom变化呢?
这里不一定是子组件,比如一个组件的高度 长度,变化,如何映射到数据上去呢? 由数据映射dom很简单 模板渲染函数一写 一渲染就实现了,但是 dom映射数据呢? 我这里能想到的方法只有事件触发数据变化(这里的数据不是简单的click啊什么 而是大小,位置,颜色变化等等) 有待深究