👨‍💻‍记录使用Vue2开发项目过程中遇到的各种难点、API问题、BUG、及教程整理(不定时更新)

2,486 阅读3分钟

前言

17年初刚工作时就听说过Vue,但那时候国内还很少公司在用,那时还是jQuery的天下,然后国内用Vue的公司越来越多,我是17年底18年初开始上手学习Vue

目前市场上常用的三大Vue桌面端UI组件库:Element、iView、Ant Design Vue,内置的组件,基本能满足大部分项目所需的业务功能需求。

此篇文章在此记录整理,本人从18年初开始上手使用Vue以后,开发产品的过程中,针对项目中业务需求所遇到的各种难点、API问题、BUG、及教程整理(不定时更新)😁

正文

1、keep-alive组件缓存:

 <!--这里是需要缓存的-->
<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>

<!--这里不会被缓存的-->
<router-view v-if="!$route.meta.keepAlive"></router-view>

keep-alive缓存组件有两个生命周期钩子函数:activated(组件激活时调用)、deactivated(组件停用时调用),对应created、destroyed。 针对业务上的一些全局公共组件,比如在一个被缓存的组件引入此公共组件,同时要绑定了一个事件,

mounted() {
    this.$nextTick(() => {
        window.addEventListener('click', function, false);
    })
},

那么退出组件时要注销该事件,

destroyed: function() {
    window.removeEventListener('click', function, false);//离开的时候注销事件
},

同时也要在deactivated里执行一次注销事件,因为被缓存的组件不会被销毁。

2、Vue构建的单页应用,假设情景是从列表页跳转到详情页,如果业务要求必须打开新窗口,即用target="_blank"。此时如果两个窗口页面之间都有用到vuex state数据的话,比如共享同一个id数组,那么列表页修改了state数据后,详情页就不能实时更新state数据。此时只能使用localStorage来实现效果了。

可以用vuex-persistedstate实现vuex持久化,底层逻辑就是把vuex数据动态更新为localStorage

3、vue-meta,更优雅的管理头部标签

动态设置meta:keywords,description和title标签,有利于SEO,可以和prerender-spa-plugin预渲染搭配使用。

参考:

vue-meta.nuxtjs.org/faq/#concat…

www.zhuyuwei.cn/2018/vue-me…

4、Vue SPA项目SEO优化之预渲染Prerender-spa-plugin

github.com/chrisvfritz…

相关bug:

报错1️⃣:

123.jpeg

此报错解决方法参考:github.com/chrisvfritz…

//在此处添加args参数
renderer: new Renderer({
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
})

报错2️⃣:

111.jpeg

此报错解决方法参考:github.com/chrisvfritz…

//此参数是build完成后自动打开chromium浏览器,方便调试,默认是true(不自动打开chromium)false是自动打开,但如果服务器是无界面的,比如centOS,ubantu,则会报错上面信息,删除headless参数即可。
renderer: new Renderer({
    headless: false,
})

预渲染build时的跨域配置

server: {
    // Normally a free port is autodetected, but feel free to set this if needed.
    port: 9292,
    proxy:{
        '/api': {
            target: 'https://www.xxxxxxx.com',
            changeOrigin: true, //是否跨域
            pathRewrite: {
                '^/api': 'api' //需要rewrite重写的,
            }
        }
    }
},

5、关于video插件

以下为 video.js 实现的公共组件videoPlayer.vue:

<template>
    <section class="video-player-box">
        <!--video-player标签的class必须设置成“video-player vjs-custom-skin”,引入的样式才能起作用。-->
        <video-player class="video-player vjs-custom-skin"
            ref="videoPlayer"
            :playsinline="true"
            :options="playerOptions"
            @play="onPlayerPlay($event)"
            @pause="onPlayerPause($event)"
            @ended="onPlayerEnded($event)"
            @loadeddata="onPlayerLoadeddata($event)"
            @waiting="onPlayerWaiting($event)"
            @playing="onPlayerPlaying($event)"
            @timeupdate="onPlayerTimeupdate($event)"
            @canplay="onPlayerCanplay($event)"
            @canplaythrough="onPlayerCanplaythrough($event)"
            @ready="playerReadied"
            @statechanged="playerStateChanged($event)">
        </video-player>
    </section>
</template>

<script>
    require('video.js/dist/video-js.css');
    require('vue-video-player/src/custom-theme.css');
    import { videoPlayer } from 'vue-video-player';
    
    export default {
        props: {
            //播放视频的URL
            videoURL: {
                type: String,
                default: ''
            },
            //在视频开始播放之前显示的图像的URL
            posterURL: {
                type: String,
                default: ''
            },
            //是否暂停播放
            isPause: {
                type: Boolean,
                default: false
            },
        },
        data() {
            return {
                playerOptions : {
                    width: document.documentElement.clientWidth,//设置视频播放器的显示宽度(以像素为单位)
                    playbackRates: [0.7, 1.0, 1.5, 2.0], //播放快进速度
                    autoplay: false, //自动播放视频
                    muted: false, // 关闭视频的声音通道(静音)。
                    loop: false, // 循环播放。
                    // 向浏览器建议在<video>加载元素后视频数据是否应该开始下载。支持的值是:
                    // 'auto':立即开始加载视频(如果浏览器支持)。某些移动设备不会预加载视频以保护其用户的带宽/数据使用,这往往是最常见和推荐的值,因为它允许浏览器选择最佳行为。
                    // 'metadata':仅加载视频的元数据,其中包括视频的持续时间和尺寸等信息。有时,元数据将通过下载几帧视频来加载。
                    // 'none':不要预加载任何数据。浏览器将等待用户点击“播放”开始下载。
                    preload: 'none',
                    language: 'zh-CN',
                    aspectRatio: '16:9', // 将播放器置于流体模式,并在计算播放器的动态大小时使用该值。该值应表示比率 - 由冒号(例如"16:9"或"4:3")分隔的两个数字。
                    fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
                    sources: [{
                        type: 'video/mp4',
                        src: 'https://imf-video-export.oss-cn-hangzhou.aliyuncs.com/video/d6f51f1f-6e49-4685-9256-758066b39f11.mp4' //要嵌入的视频源的源URL,必填项,缺失此属性会造成播放时底部controlBar控件都不工作
                    }],
                    playsinline: true,//支持小窗内联播放,IOS手机上的小窗口播放,安卓微信浏览器是X5内核,此属性不支持
                    // 视频格式测试:
                    // type: "video/webm", // ok,用ogg也可打开
                    // type: "video/ogg", // ok,用webm也可打开
                    // type: "video/3gp", //ok
                    // type: "video/mp4", // ok
                    // type: "video/avi", //打不开
                    // type: "video/flv", // 打不开
                    // type: "video/mkv", // 打不开
                    // type: "video/mov", // 打不开
                    // type: "video/mpg", // 打不开
                    // type: "video/swf", // 打不开
                    // type: "video/ts", // 打不开
                    // type: "video/wmv", // 打不开
                    // type: "video/vob", // 没转化
                    // type: "video/mxf", // 转化出错
                    // type: "video/rm", // 转化出错
                    poster: this.posterURL, //在视频开始播放之前显示的图像的URL。这通常是视频的一个框架或自定义标题屏幕。一旦用户点击“播放”,图像就会消失。
                    notSupportedMessage: '此视频暂无法播放,请稍后再试!', //允许覆盖Video.js无法播放媒体源时显示的默认消息
                    controlBar: {
                        timeDivider: true,//当前时间和持续时间之间的分隔符。如果在设计中不需要,可以隐藏。
                        durationDisplay: true,//显示时间
                        remainingTimeDisplay: false,
                        fullscreenToggle: true  //全屏按钮
                    }
                },

            }
        },
        computed: {
            player () {
                return this.$refs.videoPlayer.player
            }
        },
        created() {
        },
        mounted() {
        },
        destroyed() {
        },
        methods: {
            //监听播放
            onPlayerPlay (player) {
                console.log(player);
                // this.$refs.videoPlayer.player.play();
            },
            //监听暂停
            onPlayerPause (player) {
                console.log(player);
                // this.$refs.videoPlayer.player.pause();
            },
            //监听播放状态改变
            playerStateChanged (player) {
                // console.log(player);
            },
            //监听媒体是否已到达结尾,播放完
            onPlayerEnded (player) {
                // console.log(player);
            },
            //DOM元素上的readyState更改导致播放停止。
            onPlayerWaiting (player) {
                // console.log(player);
            },
            //媒体不再被禁止播放,并且已开始播放。
            onPlayerPlaying (player) {
                // console.log(player);
            },
            //当播放器在当前播放位置下载数据时触发
            onPlayerLoadeddata (player) {
                // console.log(player);
            },
            //当前播放位置发生变化时触发。
            onPlayerTimeupdate (player) {
                // console.log(player);
            },
            //媒体的readyState为HAVE_FUTURE_DATA或更高
            onPlayerCanplay(player) {
                // console.log('player Canplay!', player)
            },
            //媒体的readyState为HAVE_ENOUGH_DATA或更高。这意味着可以在不缓冲的情况下播放整个媒体文件。
            onPlayerCanplaythrough(player) {
                // console.log('player Canplaythrough!', player)
            },
            //将侦听器绑定到组件的就绪状态。与事件监听器的不同之处在于,如果ready事件已经发生,它将立即触发该函数。。
            playerReadied(player) {
                // seek to 10s
                console.log('example player 1 readied', player);
                // player.currentTime(0)
                // console.log('example 01: the player is readied', player)
            }


        },
        watch: {
            //更改视频URL
            videoURL(val) {
                if(val !== ''){
                    this.$refs.videoPlayer.player.src(val);
                }
            },
            //在视频开始播放之前显示的图像的URL
            posterURL(val) {
                if(val !== ''){
                    this.$refs.videoPlayer.player.poster(val);
                }
            },
            //是否暂停播放
            isPause(val) {
                console.log('isPause', val);
                if(val){
                    this.$refs.videoPlayer.player.pause();
                }
            },

        },
        components: {
            videoPlayer
        }
    }
</script>

<style lang="less" type="text/less">
    //修改播放按钮的样式
    .video-player-box{
        .video-js{
            .vjs-big-play-button{
                width: 2em;
                height: 2em;
                line-height: 2em;
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                -webkit-transform: translate(-50%, -50%);
                -moz-transform: translate(-50%, -50%);
                -ms-transform: translate(-50%, -50%);
                border-radius: 50%;
                margin: 0 !important;
                margin-top: 0 !important;
            }
        }
    }
</style>

大概就是这些配置即可完成基本的播放、暂停、视频列表切换播放(切换视频的逻辑在父组件写)等功能。但是移动端页面在微信浏览器打开会有个问题,点击视频播放按钮会默认全屏并且黑屏,video标签不能设置这两个属性,x5-video-player-type='h5' x5-video-player-fullscreen='true',或者设置为空即可。

但是video.js插件的官方文档没找到相关的option属性配置,所以后来放弃了这个插件,改为使用原生video标签来实现需求,可参考:

<template>
    <section class="video-player-box">
        //controlsList="nodownload" 此属性是取消浏览器原生video标签的下载功能。
        <video width="100%" ref="videoPlayer" controls controlsList="nodownload" playsinline="playsinline" webkit-playsinline="true" preload="none" :src="videoURL" :poster="posterURL"></video>
    </section>
</template>

<script>
    export default {
        props: {
            //播放视频的URL
            videoURL: {
                type: String,
                default: ''
            },
            //在视频开始播放之前显示的图像的URL
            posterURL: {
                type: String,
                default: ''
            },
            //是否暂停播放
            isPause: {
                type: Boolean,
                default: false
            },
        },
        data() {
            return {
            }
        },
        computed: {
        },
        created() {
        },
        mounted() {
        },
        destroyed() {
        },
        methods: {
        },
        watch: {
            //更改视频URL
            videoURL(val) {
                if(val !== ''){
                    this.$refs.videoPlayer.src = val;
                }
            },
            //在视频开始播放之前显示的图像的URL
            posterURL(val) {
                if(val !== ''){
                    this.$refs.videoPlayer.poster = val;
                }
            },
            //是否暂停播放
            isPause(val) {
                if(val){
                    this.$refs.videoPlayer.pause();
                }
            },

        },
        components: {
        }
    }
</script>

<style lang="less" type="text/less">
</style>

这样的话,移动端页面在安卓微信浏览器里的播放效果就正常了,如果产品的播放视频功能不要求自定义样式,那么像这样用video原生标签就挺方便的,而且每个浏览器的video标签的样式都不一样,Safari的更好看些。

6、关于this.$children的使用

如果模板里都是自定义子组件时: Image_20230823184621.png

那么$children数组,就是这些自定义公共组件/子组件。 image.png

如果模板里还有其他组件时,例如Element ui的组件: image.png

那么$children数组,输出的是DOM数据层级的vue组件, image.png

此时如果遍历this.$children的话,如果需要使用自定义子组件里面的某一个属性或者方法,那么就需要做判断。

image.png

以下为第三方插件整理:

1、vue-lazyload - 图片懒加载 - npm。

链接:www.npmjs.com/package/vue…

2、Moment.js - 日期格式化 中文网。

链接:momentjs.cn/

3、Vue- 中国省市区三级联动。

链接:github.com/dwqs/vue-ar…

4、vue-photo-preview - 全屏图片预览 - npm。

链接:www.npmjs.com/package/vue…

5、vue-ripple-directive——点击水波纹。

链接:www.npmjs.com/package/vue…

6、qrcode.vue - 生成二维码图片 - npm。

链接:www.npmjs.com/package/qrc…

7、clipboard - 实现复制文本到剪贴板功能 - npm。

链接:www.npmjs.com/package/cli…

8、vue 使用clipboard实现复制功能 - CSDN博客。

链接:blog.csdn.net/guxuehua/ar…

9、vue-count-to——数字跳动动画。

链接:panjiachen.github.io/countTo/dem…

10、NProgress - 全站顶部loading进度条插件。

链接:ricostacruz.com/nprogress/

11、vue-fullpage.js - 全屏滚动插件 - npm。

链接:www.npmjs.com/package/vue…

12、介绍 · better-scroll - 滚动插件。

链接:ustbhuangyi.github.io/better-scro…

13、BetterScroll:可能是目前最好用的移动端滚动插件 - 掘金。

链接:juejin.im/post/59b777…

14、Vue导出页面为PDF格式 - CSDN博客。

链接:blog.csdn.net/pratise/art…

15、vue的无缝滚动组件。

链接:github.com/chenxuan000…

16、vue相关的开源项目 - CSDN博客。

链接:blog.csdn.net/aaa333qwe/a…

17、video插件

链接:surmon-china.github.io/vue-video-p…

18、汉字拼音转换工具

链接:pinyin.js.org/

19、Vue拖拽插件

链接:github.com/SortableJS/… 链接:www.jianshu.com/p/1c73a49c0…

20、Vuex状态持久化

链接:github.com/robinvdvleu…

21、Vue表单校验插件

链接:www.npmjs.com/package/val…