当前位置: 首页 > article >正文

Vue3学习(六)Vue3 + ts几种写法

前言

官网提到组合式api和选项式api

选项式api其实就是vue2的写法,组合式api是vue3的新写法(组合式api可以在script中使用setup()也可以使用<script setup>,<script setup>是setup()的语法糖,语法糖的写法在vue3.2后才支持)

参考

Vue3官网的一些文章

Vue3语法官网教程

官网迁移教程 (Vue2和Vue3差异点)

组合式api setup()用法

单文件组件 <sctipt setup>用法

Vue3使用setup()函数核心API写法

vue3中使用TypeScript

Vue3结合TS项目开发实践总结

Vue3写法总结

Vue3种使用<script setup>语法糖核心API写法

上手后才知道 ,Vue3 的 script setup 语法糖是真的爽

Vue3 中setup()和<script setup></script>

Vue3 <script setup>语法糖+ts+Hook实践探索

Vue3一些API用法

vue3 ref函数用法

Vue3中ref和reactive的区别

Vue3第一篇之ref和reactive详解扩展

实战

Vue3使用setup()函数

复制代码

<script lang="ts">
import { defineComponent, onMounted, ref, watch, nextTick, computed, onBeforeUnmount } from '@vue/composition-api';
import { useVisibility } from '@live/hooks/src/useVisibility';
import { toWorkVideoPage, getCookie, parseQuery, toLiveRoomByUserId, openProfile, osName, query } from '@live/actions';
import { throttle, isInLiveRoom, isLowDeviceBiz } from '../../utils/init';
import { BOOKSTATUS, ENROLLSTATUS, EVENTSTATUS } from '../../schemas';
import loadingsvg from './img/loading.svg';
import type { PropType } from '@vue/composition-api';
import type { TTabPhotoList, TEventList, TTabs } from '../../schemas';
// import LoadingIcon from '@/components/LoadingIcon/index.vue';
import { showToast } from '@/utils/init';
import { sendShow, sendClick } from '@/logs/index';
import TabSwiper from '@/components/TabSwiper/index.vue';
// import LottieIcon from '@/components/LottieIcon/index.vue';

export default defineComponent({
  components: {
    Swiper,
    Sticky,
    // LottieIcon,
    TabSwiper,
    // LoadingIcon,
    LottieIcon: () => import(/* webpackChunkName:"lottie-icon" */ '@/components/LottieIcon/index.vue'),
  },
  props: {
    tabPhotoList: {
      type: Array as PropType<TTabPhotoList[]>,
      default: () => [],
    },
    currentTabIndex: {
      type: Number,
      default: 0,
    },
    bookStatusLoading: {
      type: Boolean,
      default: false,
    },
  },
  emits: [
    'updateCurrentTabIndex', 'handleEnroll', 'handleBook',
  ],
  setup(props, { emit }) {

    const swiperPanelRef = ref();
    const currentSwiperPanelIndex = ref(0);
    const setTimeoutId = ref();
    // 当前页面是否可见,settimeout中需要判断是否播放视频
    const isVisibility = ref(true);
    // 当前video是否可见,只有video可见 && 页面可见才播放视频
    const isVisibilityVideo = ref(true);

    const swiperPannelItemRef = ref();
    const laterRender = ref(false);

    setTimeout(() => {
      laterRender.value = true;
    });

    watch(
      [() => props.currentTabIndex],
      () => {
        if (currentSwiperPanelIndex.value === 0) {
          sendShow(getPanelShowLog(0));
        }
      },
      {
        immediate: false,
      },
    );

    // 自动播放第一个视频
    onMounted(() => {
      sendShow(getPanelShowLog(0));
      handleVideoPlay();
      // 离开可视区不播放视频
      const io = new IntersectionObserver(res => {
        if (res[0].intersectionRatio > 0) {
          isVisibilityVideo.value = true;
          handleVideoPlay();
        } else {
          isVisibilityVideo.value = false;
          isVisibility.value = true;
          videoStop();
        }
      });
      io.observe(document.querySelector('.swiper-panel-main')!);
    });

    onBeforeUnmount(() => {
      videoStop();
    });

    useVisibility({
      visibleHandler: () => {
        console.log('----visibleHandler-00-0---');
        isVisibility.value = true;

        // 此处安卓端未生效,代码执行到,但是视频没有重新播放
        // if (osName !== 'android') { // fix 安卓偶先播放状态下漏出封面图
        // pc,ios,android
        handleVideoPlay(1000, false);
        // }
      },
      hiddenHandler: () => {
        isVisibility.value = false;
        console.log('----hiddenHandler-00-0---');

        // 此处安卓端未生效,代码执行到,但是视频没有因此暂停
        // if (osName !== 'android') {
        videoStop(false);
        // }
      },
    });

    // const videoVisibilityCallBack =

    const eventListByIndex = computed<TEventList[]>(() => {
      return (props.tabPhotoList[props.currentTabIndex] as TTabPhotoList).eventList || [];
    });
    const tabsByTabPhotoList = computed<TTabs[]>(() => {
      return props?.tabPhotoList?.map((item: TTabPhotoList) => {
        return {
          tabTitle: item?.tabTitle,
          tabCode: item?.tabCode,
          selectedTitle: item?.selectedTitle,
        };
      });
    });

    const onSwiperTabsChange = (index: number) => {
      // currentSwiperTabsIndex.value = index;
      emit('updateCurrentTabIndex', index);
      currentSwiperPanelIndex.value = 0;
      swiperPanelRef?.value?.to(0);
      handleVideoPlay();
    };

    const getPanelShowLog = (index: number) => {
      const event: any = (props as any).tabPhotoList[props.currentTabIndex].eventList[index];

      return {
        action: 'BLIND_DATE_VENUE_VIDEO_CARD',
        params: {
          author_id: event.authorInfo.userInfo.user_id,
          id: event.eventId,
          status: event.eventStatus,
          tab_name: props.tabPhotoList[props.currentTabIndex].tabTitle,
          video_id: event.photo.photoId,
        },
      };
    };

    const onSwiperPanelChange = (index, lastIndex) => {
      sendShow(getPanelShowLog(index));
      currentSwiperPanelIndex.value = index;
      handleVideoPlay();

    };

    const videoStop = (isInitPoster = true) => {
      try {
        if (isInLiveRoom()) {
          return;
        }

        // 暂停隐藏封面
        if (isInitPoster) {
          initPoster();
        }
        const dom = document.querySelectorAll('.swiper-panel-item video');

        dom.forEach((item: HTMLVideoElement) => {
          item?.pause();
          // item?.load();
          console.log('stop', item.className);
        });
      } catch (e) {
        console.log('error', 'videostop', e);
      }

    };

    /**
     * 播放视频处理:ref不太满足播放控制,采用document.querySelectorAll直接获取dom
     * 1.涉及到过渡动画,影响getBoundingClientRect计算,所以await nextTick();
     * 2.因为支持轮播,可能存在两个activitydom都处于选中状态,因此需要用for循环找到一个距离屏幕中心最近的item
     * inShowPoster: 安卓端点击视频跳转再次回到h5时候,封面会闪一下(不会暂停1s后播放),次case不隐藏封面
     */
    const handleVideoPlay = (timeOut = 1000, isInitPoster = true) => {

      if (isInLiveRoom()) {
        return;
      }

      // fix 快速切换导致业务卡顿
      clearTimeout(setTimeoutId.value);
      videoStop(isInitPoster);

      setTimeoutId.value = setTimeout(() => {
        try {
          videoStop(isInitPoster);
          // console.error(' settimeout transform------', document.querySelector('.spu-swiper-inner').style.transform);

          if (isVisibility.value && isVisibilityVideo.value ) {
            const targetDom = getCurrentVideo();

            // play调用失败或者多次调用触发异常(The operation was aborted)
            targetDom?.play()?.catch(e => {
              console.log(e);
            });
          }
        } catch (e) {
          console.log('error', e);
        }
      }, timeOut);
    };

    const getCurrentVideo = () => {
      const activeDom = document.querySelectorAll('.swiper-panel-item.is-active video');
      const target = window.innerWidth / 2;
      let targetDom = activeDom?.[0] as HTMLVideoElement;
      let distance = Number.MAX_VALUE;

      for (const item of activeDom) {
        const left = item.getBoundingClientRect().left;

        if (Math.abs(left - target) < distance) {
          distance = Math.abs(left - target);
          targetDom = item as HTMLVideoElement;
        }
      }

      return targetDom;
    };

    const initPoster = () => {
      //
      const domPoster = document.querySelectorAll<HTMLElement>('.item-video-living.videoPoster');

      domPoster.forEach(item => {
        item && (item.style.opacity = '1');
      });
    };

    const toWorkVideoPageFn = (eventId: number, photoId: string, userId: string, eventStatus: number) => {
      if (isInLiveRoom()) {
        return;
      }

      sendClick({
        action: 'BLIND_DATE_VENUE_VIDEO_CARD',
        params: {
          author_id: userId,
          id: eventId,
          status: eventStatus,
          tab_name: props.tabPhotoList[props.currentTabIndex].tabTitle,
          video_id: photoId,
        },
      });
      // videoStop();
      toWorkVideoPage(photoId, '');
    };

    const handleBook = throttle((isCancel:boolean, eventId: number, userId: number, photoId: string) => {
      console.log('handleBook', isCancel, eventId, userId, photoId);

      if (!props.bookStatusLoading) { // 网络请求阶段bookStatusLoading为true
        emit('handleBook', isCancel, eventId, userId, photoId);
      }
    }, 1000);

    const handleEnroll = throttle((isCancel:boolean, eventId: number, userId: number, photoId: string) => {
      console.log('handleEnroll', isCancel, eventId, userId, photoId);
      emit('handleEnroll', isCancel, eventId, userId, photoId);
    }, 1000);

    const handleOnline = (userId: number) => {
      // videoStop();
      const { authorId } = query;

      if ( String(authorId) === String(userId)) {
        showToast('已经在当前直播间');

        return;
      }

      if (userId) {
        toLiveRoomByUserId(String(userId), {
          liveSource: 'BLIND_DATE_CNY_PAGE_BUTTON',
          sourceType: 269,
        });
      }
    };

    const openProfileFn = (userId: number) => {
      // videoStop();
      openProfile(String(userId));
    };

    const onVideoWaiting = (id: string) => {

      // loading
      const domLoading = document.querySelectorAll<HTMLElement>(`.${id}`);

      domLoading.forEach(item => {
        item && (item.style.opacity = '1');
      });
      console.log('onVideoWaiting', id);
    };

    // 性能考虑,不采用display,用opacity
    const onVideoPlaying = (id: string) => {
      try {
        // 关闭loading
        const domLoading = document.querySelectorAll<HTMLElement>(`.${id}`);

        domLoading.forEach(item => {
          item && (item.style.opacity = '0');
        });
        // 关闭poster
        const domPoster = document.querySelectorAll<HTMLElement>(`.${id}_poster`);

        domPoster.forEach(item => {
          item && (item.style.opacity = '0');
        });

        const video = getCurrentVideo();
        video && (video.muted = false);

      } catch (e) {
        console.log('onVideoPlaying', e);
      }
    };

    const onVideoOtherEvent = throttle((name: string, id: string) => {
      if (name === 'timeupdate') {
        const dom = getCurrentVideo();

        if (dom?.readyState < 4 ) {
          onVideoWaiting(id);
        } else {
          onVideoPlaying(id);
        }
      }

    }, 1000);

    // 不是低端机 && 不在直播间
    const isShowVideo = () => {
      return !isLowDeviceBiz() && !isInLiveRoom();
    };

    const oneventvideo = (name, id) => {
      // const dom = getCurrentVideo();
      // console.log('name', name, id, dom.readyState);
      if (name === 'canplaythrough') {
        try {
        // 关闭loading
          const domLoading = document.querySelectorAll<HTMLElement>(`.${id}`);

          domLoading.forEach(item => {
            item && (item.style.opacity = '0');
          });

        } catch (e) {
          console.log('onVideoPlaying', e);
        }
      }
    };

    // 当前dom节点改变:loop模式下,边界场景下视觉上未切换,实际已切换,此时会出现有声音,无画面情况,此case需要重新播放视频
    const onDomChangePanel = (index, lastIndex) => {
      handleVideoPlay();
    };

    const getTextInfo = (title: string) => {
      return title.indexOf('「') === 0;
    };

    return {
      swiperPanelRef,
      currentSwiperPanelIndex,
      onSwiperTabsChange,
      onSwiperPanelChange,
      toWorkVideoPageFn,
      eventListByIndex,
      tabsByTabPhotoList,
      handleBook,
      handleEnroll,
      handleOnline,
      openProfileFn,
      isInLiveRoom,
      BOOKSTATUS,
      ENROLLSTATUS,
      EVENTSTATUS,
      onVideoWaiting,
      onVideoPlaying,
      loadingsvg,
      isShowVideo,
      swiperPannelItemRef,
      oneventvideo,
      onVideoOtherEvent,
      onDomChangePanel,
      getTextInfo,
      laterRender,
    };
  },
});
</script>

http://www.kler.cn/news/328187.html

相关文章:

  • 深入工作流调度的内核
  • 等保测评:企业数字安全的坚实盾牌
  • [Docker学习笔记]利用Dockerfile创建镜像
  • 无人机之编队控制篇
  • 速盾:cdn是怎么加速视频的?
  • C++第3课——保留小数点、比较运算符、逻辑运算符、布尔类型以及if-else分支语句(含视频讲解)
  • JAVA连接HDFS操作
  • docker(1) --- win11环境配置
  • React Native使用高德地图
  • vscode 的terminal 输出打印行数限制设置
  • 深度学习之贝叶斯分类器
  • camunda + oracle 启动报错 解决方法
  • Nginx的命令行控制
  • NLP:BERT的介绍并使用该模型计算文本相似度
  • VS2013 运行Qt生成的.exe报错
  • 【系统架构设计师】专题:需求工程总结
  • Qwen2-VL论文阅读笔记
  • 开发环境搭建之VScode的安装及使用
  • 性能微基准测试JMH
  • 数据结构-4.栈与队列
  • Transformer 算法模型详解
  • 9.30Python基础-元组(补充)、字典、集合
  • linux配置git
  • 2024年10月HarmonyOS应用开发者高级认证全新题库
  • DC00024基于ssm实验室预约管理系统java web项目web教师预约jsp预约管理系统
  • 【mysql】理解一条sql的执行流程
  • 电气工程师面试必备:全面解析常见面试问题及答案
  • Python面试题精选及解析--第二篇
  • 深度解析:Python蓝桥杯青少组精英赛道与高端题型概览
  • Java 安全认证和 Hadoop UGI 原理解析