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>

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM