Vue子组件懒加载,页内导航,平滑过渡

Vue子组件懒加载,页内导航,平滑过渡

需求与实现思路

想实现类似 elementUI tabs 那种效果, 但是是在单个容器内部,且支持懒加载,点击右侧(点击时才加载对应组件)的nav item 可以跳转到对应的组件区域。(并且在加载后自动跳转到对应区域)
在这里插入图片描述

懒加载实现方式

本例使用条件渲染控制方式,缺点:

  • 需要额外配置 v-if 的绑定

  • 似乎不是真正意义上的懒加载(似乎)(只是心里过不去而已,毕竟多了个v-if

平滑过渡实现方式

mukashi mukashi 在很久很久以前,瞟过一眼张鑫旭大佬的文章,今天碰到类似的需求,突然想到这个效果,就去找了下,还好它还在:

CSS scroll-vehavior:smooth; 实现平滑过渡 👈 你可以狠狠的点击这里进行查看

懒的看这里:[要点]

  • 父容器 overflow:hidden
  • 父容器 scroll-vehavior:smooth;
  • 使用 label for input or a herf to target 都行。(不过 a 链接的写hash还是算了吧)
  • 子组件中的 input 一定要 height:100% 不然一个视口内会出现其它元素。。。
  • clip 属性剪裁绝对定位元素。

该属性缺点:

  • 有本事给我支持 贝塞尔曲线啊!
  • 不想提到的兼容性

兼容性

Go to Can I use 除了 safari 不太给面子,其它的还行

本次用于B端需求,浏览器谁还会用IE?大不了没有这个效果的时候僵硬的跳转一下。

index.vue

<template>

<template>
    <div class="smart-cashier-wrapper">
      <div class="smart-container">
        <payment-side-manage >
          <input id="payment-side-manage" slot="holder">
        </payment-side-manage>
        <mode-of-payment v-if="isShow.modeOfPayment" ref="modeOfPayment">
          <input id="mode-of-payment" slot="holder">
        </mode-of-payment>
        <cashier-config v-if="isShow.cashierConfig" ref="cashierConfig">
          <input id="cashier-config" slot="holder">
        </cashier-config>

      </div>
      <div class="smart-nav">
        <label class="smart-to" for="payment-side-manage">支付端管理</label>
        <label class="smart-to" for="mode-of-payment" @click.once="handleChildComponentCreated('modeOfPayment')">支付方式管理</label>
        <label class="smart-to" for="cashier-config" @click.once="handleChildComponentCreated('cashierConfig')">收银台配置</label>
      </div>
    </div>
</template>
  1. 使用了具名插槽,是因为想将 labelinput的关系更加亲密
  2. once 修饰符确保只加载一次
  3. 第一个子元素默认加载。所以不给 v-if

<script>

<script>
  
    import CashierConfig from "./child/cashierConfig";
    import ModeOfPayment from "./child/modeOfPayment";
    import PaymentSideManage from "./child/paymentSideManage";
    export default {
        name: "smartCashier",
      components: {PaymentSideManage, ModeOfPayment, CashierConfig},
      data(){
          return {
            isShow:{
              "cashierConfig":false,
              "modeOfPayment":false
            }
          }
      },
      methods:{
        handleChildComponentCreated(refName){
          this.isShow[refName] = true

          this.$nextTick(()=>{
            /* 不管用
            this.$refs[refName].$el.click()
            */
            /* 不管用
            let newEvent = document.createEvent('HTMLEvents');
            newEvent.initEvent('click', false,false);
            this.$refs[refName].$el.dispatchEvent(newEvent)
            */
              
            /*scrollIntoView 牛逼!!*/
            this.$refs[refName].$el.scrollIntoView()
          })
        }
      }
    }
</script>
  1. ref而不是直接拿 DOM
  2. $nextTick 微任务搞起,是因为直接执行你就会败北

<style>

<style scoped lang="scss">
.smart-cashier-wrapper{
  width: calc(100% - 200px);
  height: 100vh;
  display: flex;
  flex-wrap: nowrap;
  flex-direction: row;
  justify-content: flex-start;
  align-items: flex-start;
  position: relative;

  *border:1px solid red;

  .smart-container{
    flex-grow: 1;
    height: 100%;
    max-height: 100vh;
    border:1px solid red;
    overflow: hidden;
    scroll-behavior: smooth;


  }
  .smart-nav{
    width: 200px;
    height: 100vh;
    border:1px solid red;
    position: fixed;
    right: 0;
    .smart-to{
      display: block;
      width: 100%;
      height: 50px;
      color: black;
      text-align: center;
      line-height: 50px;
      font-weight: bold;
    }
  }
}
</style>

中间有很多这种*border:1px solid red; 是方便我调试的时候能看到元素到底在哪里,也可以像下面这样玩。不用的时候就加上*

.componentName-wraper{
    *{
        border:1px solid red;
	}
}

child.vue

<template>
    <div class="modeOfPayment-wrapper">
      <slot name="holder"></slot>
    </div>
</template>

<script>
    export default {
        name: "modeOfPayment",
      created() {
          console.log("支付方式 was created")
      }
    }
</script>

<style scoped lang="scss">
.modeOfPayment-wrapper{
  width: 100%;
  height: 100%;
  border:1px solid red;
  overflow: hidden;
  position: relative;
  > input{
    position: absolute;
    top: 0;
    height: 100%;
    width: 1px;
    border:0;
    padding: 0;
    margin: 0;
    clip: rect(0 0 0 0);
  }
}
</style>

在生命周期 hook 中加入调试输出,可以看到加载效果。

踩坑总结

  • slot v-slot 具名插槽的使用 受到版本号影响

  • 受到事件是否可信的 标识isTrusted 的影响(发现脚本触发的点击事件无效后)

    • Element.click() 救不了我
    • dispatchEvent() 救不了我
  • scrollIntoView 牛逼 !!!

  • https://developer.mozilla.org/zh-CN/docs/Web/API/Event/isTrusted 去认识一下

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值