vue移動端項目經驗(二)


視頻插件vue-video-player的使用及注意事項

官方文檔
video.js:https://docs.videojs.com/docs/api/player.html
vue-video-player:https://github.com/surmon-china/vue-video-player

1、包的安裝
npm install vue-video-player --save

2、包的引入
import VideoPlayer from 'vue-video-player'
require('video.js/dist/video-js.css')  //若此行報錯無法找到,則改為require('vue-video-player/node_modules/video.js/dist/video-js.css')
require('vue-video-player/src/custom-theme.css')
Vue.use(VideoPlayer)

3、插件使用
html部分
<div style="overflow:hidden">  //移動端當屏幕寬度較小時,此視頻控件調節音量時會使得屏幕出現橫向滾動條,故在父級盒子使用overflow:hidden來阻止滾動條出現,以免影響用戶體驗。
    <video-player  class="video-player vjs-custom-skin"
        ref="videoPlayer" 
        :playsinline="true" 
        :options="playerOptions"   //相關配置
        @play="onPlayerPlay($event)"//監聽開始狀態(非必需),這是事件,按需使用
        @pause="onPlayerPause($event)"//監聽暫停狀態(非必需),這是事件,按需使用
    >
    </video-player>
</div>


js部分
data(){
    return{

        playerOptions : {
            playbackRates: [0.5, 1.0, 1.5, 2.0], //播放速度
            autoplay: false, //如果true,瀏覽器准備好時開始回放。
            muted: false, // 默認情況下將會消除任何音頻。
            loop: false, // 導致視頻一結束就重新開始。
            preload: 'auto', // 建議瀏覽器在<video>加載元素后是否應該開始下載視頻數據。auto瀏覽器選擇最佳行為,立即開始加載視頻(如果瀏覽器支持)
            language: 'zh-CN',
            aspectRatio: '16:9', // 將播放器置於流暢模式,並在計算播放器的動態大小時使用該值。值應該代表一個比例 - 用冒號分隔的兩個數字(例如"16:9"或"4:3")
            fluid: true, // 當true時,Video.js player將擁有流體大小。換句話說,它將按比例縮放以適應其容器。
            sources: [{
                type: "",//這里的種類支持很多種:基本視頻格式、直播、流媒體等,具體可以參看git網址項目
                src: "" //url地址
            }],
            poster: "../../static/images/test.jpg", //你的封面地址
            // width: document.documentElement.clientWidth, //播放器寬度
            notSupportedMessage: '此視頻暫無法播放,請稍后再試', //允許覆蓋Video.js無法播放媒體源時顯示的默認信息。
            controlBar: {
                  timeDivider: true,  //當前時間和持續時間的分隔符
                  durationDisplay: true,  //顯示持續時間
                  remainingTimeDisplay: false,  //是否顯示剩余時間功能
                  fullscreenToggle: true  //全屏按鈕
            }
        }

    }
},

//掛載視頻組件(非必須)不寫這一步也可以實現播放,添加這個是為了自定義按鈕使用
computed: {
    player() {
        return this.$refs.videoPlayer.player//自定義播放
    }
},

視頻插件xgplayer(西瓜播放器)的使用及注意事項

更多詳情見官方文檔:http://h5player.bytedance.com/

貼代碼:
1、安裝包:npm install xgplayer
2、代碼部分:
<template>
       <div id="mse"></div>   //1、標簽,必須要有
</template>

<script>
    import * as axios from 'axios'
    import common from '../../kits/common.js'
    import Player from 'xgplayer';   //2、引入視頻組件
    export default{
        name:'Demo',
        data(){
            return{
                playerConfig:{   //3、動態設置相關值
                    id: 'mse',
                    normalUrl:'',  //普清
                    midUrl:'',   //高清
                    hightUrl: '', //超清
                    poster: '',   //封面
                }
            }
        },
        created(){
            this.getVideoInfo()
        },
        methods:{
            playerInit(){   //4、設置視頻配置(注意:playerInit應放在異步函數里或mounted之后,不可在created里直接加載,否則不生效)
                let player = new Player({   //初始化player實例對象
                    id:'mse',
                    url: this.playerConfig.highUrl,   //視頻鏈接
                    poster:this.playerConfig.poster,    //封面圖片
                    fluid: true,   //流式布局
                    volume: 0.6,   //初始音量
                    whitelist: ['iPhone','Android'],   //白名單
                    playsinline: true,   //內聯模式
                    videoInit: true,
                    'x5-video-player-type': 'h5',
                });
                player.emit('resourceReady', [{name: '超清', url: this.playerConfig.highUrl}, {name: '高清', url: this.playerConfig.highUrl},{name: '標清', url: this.playerConfig.highUrl},]);
            }
        },
        getVideoInfo(){
            axios.get(`${common.videoApi}/getVideo`).then(res=>{
                this.playerConfig.highUrl=res.data.data.url1    //獲取超清視頻鏈接
                this.playerConfig.midUrl=res.data.data.url2    //獲取高清視頻鏈接
                this.playerConfig.poster=res.data.data.vimage   //獲取視頻封面
                this.playerInit()   //為什么在這里調用playerInit函數而不在mounted生命周期中調用?因為接口請求是異步的,在mounted中接口調用還未返回數據,故playerConfig中的數據仍然為空。而視頻組件依賴於playerConfig中的數據
            })
        }
    }
    
</script>

【【【直播】】】:直播與視頻播放差不多,就以下幾點不同
1、引入不同:
import 'xgplayer';   //先引入xgplayer
import HlsJsPlayer from 'xgplayer-hls.js';  //再引入組件
2、初始化不同:
let player=new HlsJsPlayer({    //初始化實例對象HlsJsPlayer
    id:'hlsmse',
    url: this.hlsPlayerConfig.url,   
    poster:this.hlsPlayerConfig.poster,    
})


【【【注意事項:西瓜播放器xgplayer我用的時候有個Bug,就是在當前頁面,xgplayer的視頻鏈接url每重新賦值一次,xgplayer都會在原有的<video>標簽上再生成一個新的<video>標簽,並去覆蓋原有的標簽,通過這種方式實現當前視頻的更新,但這也導致一個問題,因為原有的<video>標簽不會被清空。就是說會出現多個video標簽在同一個頁面上的同一個位置。不僅影響性能,還會導致視頻組件占位沖突。目前我想到的解決辦法:在url重新賦值后,利用provide / inject 組合 刷新當前頁面,以達到重新加載xgplayer組件的目的。(如何刷新,見下方)】】】
【【【09.10.24,想到了以上bug第二種解決辦法:在重新初始化播放器之前,利用js先刪除舊的已經渲染過的<div id="mse"></div>,緊接在再創建一個新的<div id="mse"></div>即可,代碼如下
    <div class="videoBody" id="videoBody">
           <div v-show="islive==false" id="mse" class="mse"></div>
           <div v-show="islive!==true" id="hlsmse" class="hlsmse"></div>
           <div id="msedemo"></div>
    </div>

重新創建mse:
   document.getElementById('videoBody').removeChild(document.getElementById('mse'))
   document.getElementById('videoBody').insertBefore(document.createElement('div'),document.getElementById('hlsmse')) 
   document.getElementById('hlsmse').previousElementSibling.setAttribute('id','mse') 

重新創建hlsmse:
   document.getElementById('videoBody').removeChild(document.getElementById('hlsmse'))
   document.getElementById('videoBody').insertBefore(document.createElement('div'),document.getElementById('msedemo')) 
   document.getElementById('msedemo').previousElementSibling.setAttribute('id','hlsmse') 
】】】


【【【注冊事件,如監聽播放暫停事件】】】:
playerInit(){   //xgplayer配置
    let player = new Player({
        id:'mse',
        url: this.playerConfig.url,   
    });
    player.on('play',function(){    //注冊並監聽播放事件
        consol.log('播放了')
    })
    player.on('pause',function(){   //注冊並監聽暫停事件
        console.log('暫停了')
    })
},
具體可查看官網:http://h5player.bytedance.com/api/#%E4%BA%8B%E4%BB%B6
【【【上次播放到這里】】】:
1、在playInit的new Player里添加:
progressDot: [{'time': this.playerConfig.lastTime,'text':'您上次看到了這里'}],
2、在playInit函數最后添加以下代碼
if(this.playerConfig.currentTime>0){
    player.currentTime=this.playerConfig.lastTime   //將上次播放的時間配置到當前播放器的播放時間
}
this.postTime=setInterval(()=>{
    if(player.hasStart){
        axios.post(`${common.videoapi}/amstc/addVideoTime?vid=${this.vid}&uid=${this.uid}&currenttime=${parseInt(player.currentTime)}`)   //每過20秒將當前播放時間上傳至服務器,下次進入此頁面獲取上次播放時間。
    }
},20000)

3、在頁面銷毀后,清除定時器
destroyed(){
    clearInterval(this.postTime)
},
注意,此功能適用於pc端,移動端安卓ios會有兼容問題

【【【安卓手機微信端里,組件出現黑屏情況,視頻加載不出來,並報錯The play() request was interrupted by a new load request】】】:
提示:在安卓端的微信內嵌的瀏覽器里,視頻組件是可以實現自動播放的,在其他地方瀏覽器則不可播放,除非初始時是靜音狀態。因為瀏覽器視頻播放協議的原因,video在用戶交互之前,一般不允許自動播放。
原因看這:http://link.sov5.cn/l/LKioRMRBQX
解決:
let player=new HlsJsPlayer({
    id:'hlsmse',
    autoplay:false,  //這里取消自動播放
    url: this.hlsPlayerConfig.url,   
    poster:this.posterImg,    
    fluid: true,   
    volume: 0.6,   
    useHls: true,   
    whitelist: ['iPhone','Android'],  
    playsinline: true,   
    ignores: ['time','volume'],
    'x5-video-player-type': 'h5',
})

+ player.start(this.hlsPlayerConfig.url)   //調用此函數即可,調用此函數在安卓微信端會自動播放

vue如何刷新當前頁面

轉自:https://www.cnblogs.com/mmzuo-798/p/9356253.html

重新刷新當前頁面,首先想到的就是路由重新導入當前頁面,比如這樣:
this.$router.push({name:'goodsList',params:{vid:vid}})
但是:我們會發現用vue-router在當前頁面再重定向路由到頁面本身,就算改變id,頁面也是不進行刷新的,根本沒有任何作用~所以這個方法out

下面提供兩種方法:
1、強制刷新法,頁面會類似於ctrl+f5的那種刷新,會有一段時間的空白出現,用戶體驗很不好,不建議使用
location. reload()
this.$router.go(0)

2、provide / inject 組合 方式是我試過最實用的
① 首先,要修改下你的app.vue,如圖

② 通過聲明reload方法,控制router-view的顯示或隱藏,從而控制頁面的再次加載,這邊定義了
isRouterAlive //true or false 來控制,然后在需要當前頁面刷新的頁面中注入App.vue組件提供(provide)的 reload 依賴,然后直接用this.reload來調用就行

vue利用watch監聽路由變化,可配合上面刷新方法使用

watch: {
      $route() {
        console.log("路由發生變化了")
        this.goodId=this.$route.params.vid  //2、獲取新id重新請求數據
        axios.get(`${common.goodsapi}/getgoods?id=${this.goodId}`).then(res=>{.....})
        this.reload()  //3、監聽到路由變化后刷新頁面,同時完成頁面數據更新
      }
    },
methods:{
    goToGoodsList(){
        this.$router.push({name:'goodsList',params:{vid:vid}})    //1、點擊后路由跳轉仍在本頁面,路由僅所攜帶的id發生改變。
    }
}

回到頂部

document.documentElement.scrollTo(0,0)

轉換時間格式

        transCommentDate(dateNum){
            const date=new Date(dateNum)
            let Y=date.getFullYear()
            let M=date.getMonth()+1
            M=M<10?('0'+M):M
            let D=date.getDate()
            D=D<10?('0'+D):D
            let h=date.getHours()
            h=h<10?('0'+h):h
            let m=date.getMinutes()
            m=m<10?('0'+m):m
            let s=date.getSeconds()
            s=s<10?('0'+s):s

            return `${Y}-${M}-${D} ${h}:${m}:${s}`
        },

切換輸入框時的獲取焦點問題

需求:有兩個input框(評論和回復),在點擊“回個話”按鈕后,評論input框切換成回復input框,同時回復input框獲取焦點

評論框:<input class="comment" id="comment" v-if="!isReply" />   回復框:<input class="reply" id="reply" v-slse-if="isReply"/>

思路:當點擊“回個話”按鈕后,執行函數使this.isReply=true,回復框顯示。再使用document.getElementById("reply").focus()使其獲取焦點。

問題:以上思路執行后會報錯,錯誤為document.getElementById("reply")找不到。為什么呢,因為回復框原本是不存在的,只有在頁面mounted之后,回復框dom才會加載出來,而document.getElementById("reply")是在頁面加載之前就執行了,故找不到。

【【【解決辦法:將兩個輸入框的id設置成一樣的(如id="inputLabel"),再使用document.getElementById("inputLabel")即可。】】】

利用websocket實現直播實時評論

直接貼代碼
<template>
    <div>
        <div>
            <ul>
                <li v-for="(item,index) in messages" :key="index">{{item.ccontent}}</li>   //渲染從socket獲取到的評論數據
            </ul>
        </div>
        <input v-model="commentValue" />
        <div @click="sendComment">發表評論</div>  //發送評論數據到socket
    <div>
</template>

<script>
    import * as axios from 'axios'
    import common from '../../sets/common.js'
    import {getCookie} from '../../sets/cookie.js'  //引入cookie
    data(){
        return{
            usertoken:null,   //用戶信息
            wspath:'',   //websocket的path
            socket:null,   //socket實例對象
            messages:[],   //從socket獲取的評論數據
            commentValue:'',   //用戶輸入的評論內容
        }
    }
    created(){
        this.usertoken = getCookie('Token')  
        this.chatInit()  //初始化socket
    },
    methods:{
        //1、實例化socket
        chatInit(){
            if(typeof(WebSocket) === 'undefined'){
                this.$toast({
                    message:'您的瀏覽器不支持socket',
                    duration:2000,  
                })
            }else{
                // 實例化socket
                if(this.usertoken){
                  this.wspath = `${common.socketlink}${this.roomid}?token=${this.usertoken}`    //socketlink由后端提供給我,roomid從直播接口獲取,token自己去設置
                } else {
                  this.wspath = `${common.socketlink}${this.roomid}`
                }
                //初始化socket實例對象
                this.socket = new WebSocket(this.wspath)   
                // 監聽socket連接
                this.socket.onopen = ()=>{
                    console.log('socket連接成功')
                }
                // 監聽socket錯誤信息
                this.socket.onerror = ()=>{
                    console.log('socket連接錯誤')
                }
                //監聽socket關閉
                this.socket.onclose=()=>{
                    console.log('socket連接關閉')
                }
                // 監聽socket消息
                this.socket.onmessage = this.getComment    //這里面調用函數時不能帶(),若this.getComment()會報錯
            }
        },
        //2、監聽socket消息
        getComment(msg){
            this.messages=this.messages.concat(JSON.parse(msg.data))
        },
        //3、發送評論
        sendComment(){
                this.socket.send(`{"ccontent":"${this.commentValue}"}`)    //這里注意上傳時前面參數如ccontent要與后端的一致,不然socket會檢測上傳錯誤,從而自動關閉連接
                this.commentValue=''
                document.getElementById('commentScrollHidden').scrollTop=document.getElementById('scrollBottom').offsetHeight   //發送評論后評論區回到底部
            }
        },
    }
</script>

實現簡單的微信分享功能(若想使用js-sdk實現分享功能請移步 https://www.cnblogs.com/huihuihero/p/12132952.html)

①先動態設置每個頁面的title(方法見下方),
②有些頁面需要自定義標題的也可以通過 document.title= 設置(方法見下方)。
③最后就是ios微信瀏覽器兼容問題,解決了即可(方法見下方)

ios微信瀏覽器分享問題

【問題】:ios端微信內分享的鏈接url不改變,總是同一個頁面

【原因】:
IOS:微信IOS設備,所記錄的url是進入頁的url,所以無論分享哪個頁面,總是會分享成初始進入頁的url
Android:微信安卓設備,所記錄的是當前頁的url,所以分享出去的也會是當前頁的url

【詳細解釋】:Vue的路由默認的mode是hash,這個模式帶來的bug不是一般的多,在hash模式下url地址會出現#,這個#號會使window.location.href()方法失效,因此location.href方式這里最好不要用。並且ios微信支付的調起也會因為#的存在而導致jsap調起失敗,但是Android並沒有問題。因此mode的模式最好是history,這種模式下微信支付調起完全沒問題。接下來就是微信分享的問題,在history模式下android還是能夠正常分享,但是ios系統在這種模式下在微信瀏覽器下的url地址就是一開始進入頁面的url,不會改變,無論路由如何的切換(android則不存在這種情況)。

【解決思路】:思路1、用js-sdk,詳見微信文檔。思路2、判斷若是微信端,則在進入頁面后將頁面的路由拼接給url,已達到重新定義url的效果

【解決方法】:在main.js中添加以下代碼(利用Vue Router的afterEach方法)
router.afterEach((to, from) => {
	const u = navigator.userAgent.toLowerCase()
        //下面這句是做判斷,是否是ios。當然你也可以做一些其他判斷,如這里我通過to.path=='/demo/demoReply'將一些頁面排除在外
	if(u.indexOf("like mac os x") < 0 || u.match(/MicroMessenger/i) != 'micromessenger' || u.match(/WebP/i) == "webp"||to.path=='/pArticle/pArcReply'||to.path=='/demo/demoReply') return
	if (to.path !== global.location.pathname) {
	  location.assign(to.fullPath)    //因為vue路由的hash模式,這里最好不要用 location.href= 或 location.replace() ,以避免url中#帶來的問題
	}
})
【提個醒】:1、使用以上方法時,遇到了個問題,一是路由傳參使用params時,當參數超過2個就會出現bug,然后以query方式傳參解決了,但參數再多時,query方式也不好用了,最終只好把這些頁面在上述代碼中通過判斷方式排除了,暫未知曉原因。
2、這個只能在線上用可以,線下時,需要先注釋掉,不能使用。

微信分享自定義標題

在當前頁面設置title即可,分享出去會自帶頁面title
通過document.title="中國進入5G時代"方式設置
getVideoInfo(){
    axios.get(`${common.videoapi}/getvideoinfo`).then(res=>{
        document.title=res.data.data.name
    })
}

vue根據路由動態改變頁面標題

在main.js中添加以下代碼
router.beforeEach((to, from, next) => {
    /* 路由發生變化修改頁面title */
    if (to.meta.title) {
        document.title = to.meta.title;
    }
    next();
})

在router.js路由頁面
{
        path:'/demo/demoReply',
	component:demoReply,
	name:'DemoReply',
	meta:{
		title:'評論回復'   //設置標題
	}
},

改變v-html內的指定標簽的樣式,這里以img為例,設置img最大寬度100%,高度自適應。(注:一定要設置高度自適應)

方式1:js設置
updated(){
    let elem=document.getElementById('demo').getElementsByTagName('img')    //獲取指定標簽里的img
    let elem2=Array.from(elem)   //因為getElementsByTagName()返回的是一個偽數組,所以這里使用Array.from轉換為數組
        for(let item of elem2){
            item.style.maxWidth="100%"
            item.style.height="auto"
        }
    },


方式2:css穿透模式下設置
/deep/  htmlcontent{
    img{
        max-width:100%!important;
        height:auto!important;
    }
}

vue2.0項目上線屏蔽console.log()

在build/webpack.prod.conf.js文件里的UglifyJsPlugin里加上這樣兩行代碼即可
new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false,
          drop_debugger: true,    //屏蔽debugger
          drop_console: true,    //屏蔽console
        }
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),

減少,壓縮項目打包后體積

在 config/index.js 中修改參數,
找到 productionSourceMap: true,改為 false 即可。

時間轉換

transDate(dateNum){
            const date=new Date(dateNum)
            let Y=date.getFullYear()
            let M=date.getMonth()+1
            M=M<10?('0'+M):M
            let D=date.getDate()
            D=D<10?('0'+D):D
            let h=date.getHours()
            h=h<10?('0'+h):h
            let m=date.getMinutes()
            m=m<10?('0'+m):m
            let s=date.getSeconds()
            s=s<10?('0'+s):s

            return `${Y}-${M}-${D} ${h}:${m}:${s}`
        },

解決格林尼治時間在ios端和android端的兼容問題

格林尼治時間格式:2019-03-05T09:02:24.000+0000

android端:
new Date("2019-03-05T09:02:24.000+0000")  可正常轉換。
new Date("2019-03-05T09:02:24.000+0000".substr(0,19))  轉換出來的時間會早8個小時

ios端:
new Date("2019-03-05T09:02:24.000+0000")  輸出為NaN,不可用。
new Date("2019-03-05T09:02:24.000+0000".substr(0,19))  可正常轉換

故通過判斷是否是ios端,再選擇轉換方式。具體代碼如下:
transDate(dateNum){
        let u = navigator.userAgent;
        if (!!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) {   //ios端
            const date=new Date(dateNum.substr(0,19))   //截取前19位后再轉換
            let Y=date.getFullYear()
            let M=date.getMonth()+1
            M=M<10?('0'+M):M
            let D=date.getDate()
            D=D<10?('0'+D):D
            let h=date.getHours()
            h=h<10?('0'+h):h
            let m=date.getMinutes()
            m=m<10?('0'+m):m
            let s=date.getSeconds()
            s=s<10?('0'+s):s
            return `${Y}-${M}-${D} ${h}:${m}:${s}`
        }else{                                             //非ios端
            const date=new Date(dateNum)   //不截取,正常轉換
            let Y=date.getFullYear()
            let M=date.getMonth()+1
            M=M<10?('0'+M):M
            let D=date.getDate()
            D=D<10?('0'+D):D
            let h=date.getHours()
            h=h<10?('0'+h):h
            let m=date.getMinutes()
            m=m<10?('0'+m):m
            let s=date.getSeconds()
            s=s<10?('0'+s):s
            return `${Y}-${M}-${D} ${h}:${m}:${s}`
        }
            
},

判斷手機操作系統是ios端還是android端

checkPort(){
    let u = navigator.userAgent, app = navigator.appVersion;
    let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android終端
    let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios終端
    if (isAndroid) {
       //這個是安卓操作系統
    }
    if (isIOS) {
  //這個是ios操作系統
    }
}


免責聲明!

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



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