slot和$nextTick不解之缘

slot与nextTick与translation与v-show不解之缘

为什么会写这个东西呢,主要是再项目遇到了一个贼怪的坑。 
先来说明一下项目需求,我有一个滑动scroll组件,就是类似这样 wrapper是传给滑动库的一个元素,solt是插槽分发

 
  1. <template>
  2. <div ref="wrapper">
  3. <slot></slot>
  4. </div>
  5. </template>

slot是什么呢? emmm slot怎么用 官方文档有 其实vue中数据是通过props传给子组件的,那么dom怎么办 要传一个 template模板数据嘛,在vue中是可以用slot分发dom的 
那slot到底是父组件的还是子组件的呢? 
当然是父组件了,类比props,也是把父组件的某个数据传给子组件的。同理slot也是这样,儿子要吃饭,爸爸先占个座,等爸爸来了 就开饭,一个道理。。。 
可是儿子怎么知道爸爸的动向呢?

现在来继续讲需求 
我在父亲组件里

 
  1. <template>
  2. <scroll :data="somedata">
  3. <ul v-if="somedata.length">
  4. <li v-for(item in somedata)>{{somedata.some}}</li>
  5. <ul>
  6. </scroll>
  7. </template>
  8. ...其余省略

这里的somedata是计算属性或者异步获取的,标签里slot分发的dom也就是v-for异步加载的了,当然他得有数据 
这里的需求就是 我父组件的dom数据somedat变化之后需要将scroll里插件库调用refresh方法去更新一下高度或者宽度了。

ok开始解决 这多简单我在子组件里watch一下传进来的somedata变化不久好了

 
  1. //scroll.vue中
  2. ...省略
  3. props:{
  4. data:{
  5. type:Array,
  6. defalut:()=>[]
  7. }
  8. }
  9. watch:{
  10. data(){
  11. this.scroll.refresh();
  12. }
  13. }

这不就行了。。。 试试告诉我们并没有那么简单... 
实际这里dom并没有渲染,因为数据更新到dom变化是需要一些时间的。 
就好比 儿子等着吃饭 爸爸只是说我要来 但是没有坐好一样,还是不能开饭 
怎么办呢 
可能了解 nextTick的已经知道怎么解决了

 
  1. data(){
  2. this.$nextTick(()=>{
  3. this.scroll.refresh();
  4. }) //这里setTimeout也可以
  5. }

emmmm nextTick是要去看vue源码的 这个之后会再写一篇博客讲明的 ,不懂nextTick可以先去查一下文档

这里父组件数据变化,scroll子组件监听到之后,等待下个轮询中 即slot里dom挂载完成后 调用。。。 嗯 一般使用ok 
scroll组件完成,真的是这样的吗? 
注意哦

 
  1. <scroll :data="somedata">
  2. <list :somedata="somedata">
  3. </list>
  4. </scroll>
 
  1. //list.vue中
  2. <template>
  3. <transition name="list">
  4. <ul v-if="somedata.length">
  5. <li v-for(item in somedata)>{{somedata.some}}</li>
  6. <ul>
  7. </transition>
  8. </template>
  9. ...其余省略
  10. <style>
  11. .list-enter-avtive{
  12. translation:all 0.4;
  13. }
  14. .list-enter{
  15. height:0;
  16. }
  17. </style>

这个列表在进入时会有一个过渡的效果,当然他已经挂载在dom树里了 并且js也能获取到dom,但是其高度开始还仍是0 
所以插件初始化时就失败了

emmmm 这可咋办啊

仔细看文档 translation中不止是css操作,还有js的钩子回掉函数 
这里我们用 after-enter这个函数 这个是过渡完成后的回掉,所以此时已经过渡完毕。

 
  1. <transition name="list" @after-enter="afterEnter">
  2. ...
  3. methods:{
  4. afterEnter(){
  5. this.$emit('listChange');
  6. }
  7. }

外层组件

 
  1. <scroll ref="scrollRef">
  2. <list :somedata="somedata" @listChange="listChange">
  3. </list>
  4. </scroll>
  5. ...
  6. methods:{
  7. listChange(){
  8. this.$scrollRef.refresh();
  9. }
  10. }

这样就可以了,等到父组件中的子组件(不一定是组件也可以dom)过渡完毕,就触发一个事件,调用钩子函数去调用scroll组件里的refresh()方法就可以了。

这样不久行了 不不不这里还不算完 
也可能有下种情况

 
  1. <scroll :data="somedata">
  2. <div v-show="someshow">
  3. //somedata加载一些动态内容
  4. </div>
  5. </scroll>

someshow这个计算属性,在somedata更新后其保持为false,直到用户点击或者打开时其表示变为true,这里继续 watchdata的话还时会出现上述问题

这里就需要 watch外面的标志位someshow

 
  1. //父组件中
  2. watch:{
  3. someshow(newval){
  4. if(newval){
  5. this.$refs.scroll.refresh();
  6. }
  7. }
  8. }

综合上述,这种场景下 不能在子组件里监听dom变化,这样时很麻烦的,需要在父组件里用数据去模拟dom变化,然后传达给子组件,就好比父子吃饭,爸爸坐好告诉儿子,开饭,不能仅靠着儿子听到爸爸讲话,就能断定爸爸已经坐好

题外话, vue这个框架主张的数据驱动,即一切以数据为主,各种监听数据变化,可是这里的‘儿子’如何实现监听父亲是否‘坐好’这个东西呢,这里都是通过一切取巧的方式取解决这个问题,显得比较麻烦,子组件怎么去监听dom变化呢? 
这里不一定是子组件,比如一个组件的高度 长度,变化,如何映射到数据上去呢? 由数据映射dom很简单 模板渲染函数一写 一渲染就实现了,但是 dom映射数据呢? 我这里能想到的方法只有事件触发数据变化(这里的数据不是简单的click啊什么 而是大小,位置,颜色变化等等) 有待深究

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值