前言
官網提到組合式api和選項式api
選項式api其實就是vue2的寫法,組合式api是vue3的新寫法(組合式api可以在script中使用setup()也可以使用<script setup>,<script setup>是setup()的語法糖,語法糖的寫法在vue3.2后才支持)
參考
Vue3官網的一些文章
官網遷移教程 (Vue2和Vue3差異點)
Vue3使用setup()函數核心API寫法
Vue3種使用<script setup>語法糖核心API寫法
上手后才知道 ,Vue3 的 script setup 語法糖是真的爽
Vue3 中setup()和<script setup></script>
Vue3 <script setup>語法糖+ts+Hook實踐探索
Vue3一些API用法
實戰
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>