bug记录

703 阅读21分钟

1 import和require差别

  • import:开发环境导入了模块,但是却没有使用到,实际上也是进行了导入,但是生产环境如果没有用到这个模块则不会进行导入。
  • require: 不管生产和开发都会进行导入

2 文字溢出现象

2.1 现象

元素设置了宽高,输入的内容就是字符串,如果正常输入,一切问题都没有,但是当测试人员一顿乱按时,会发现有一串字符超出元素的边界。

2.2 原因

一大串英文字母被默认为一个单词,因此不会正常换行。

2.3 解决办法

在样式中加入word-wrap:break-word

3.js代码不执行问题(使用vue时)

3.1现象

当做H5新闻资讯的时候,大约有十几篇文章,文章都是写死的,并没有后台,当时有人把这十几篇文章当做一个数组,统统保存在data数据中,根据传递的url参数不同来显示不同的文章,测试的时候,在本地可以显示,但是一到线上就不显示了,调试的时候当把大量数据删除以后,线上可以显示。

3.2 原因

当data里面的数据过多的时候,则会停止工作,不执行。

3.3 解决办法

把所有数据分开来写,然后单独进行管理。

4 请求体缺失问题

4.1 现象

同事做vue项目的时候,请求报错显示是请求体缺失,我检查代码逻辑,发现并无异常,然后上网搜也没有收到相应的内容和解决办法。

4.2 原因

然后检查axios代码,发现其代码里面写的是内容有误,axios({url: url,method: 'post',params: params}),原因是由于post请求数据只能放在data里面,params请求数据适用于get请求,并不适用于post请求。

4.3 解决办法

axios({url: url,method: 'post',data: params})

5 使用canvas-nest.js插件

5.1 现象

在vue项目中,想给页面设置粒子背景,然后使用到它,开始的时候,我把它卸载created中让其执行,但是页面一加载并没有立马显示背景图,而是大部分粒子从上到下一点点开始显示,然后我在mounted中执行了,可以正常显示,但是,后来由于代码需求,将原来一部分进行了优化,这次吸取了教训,还是在mounted中执行,还是出现上次的情况。

5.2 原因

经过仔细分析,发现在若是想要在页面一加载就立马显示所有的例子,就必须给设置背景的那个元素设置高度,否则都是从最上面开始的。

5.3 解决办法

因为我是将这个背景设置在body中,所以必须给body设置一个高度。

6 table中这是th宽度不生效

6.1 现象

在使用table表格标签的时候,当table的宽度小于其父类的容器时,设置宽度好使,但是当table的宽度大于其弗雷的宽度时,则不管用。

6.2 原因

table标签的有默认的自适应,无论宽度设置多少,都不会生效。

6.3 解决办法

在th或td的里面加入div标签,然后将表格每列的宽度设置为div的宽度。

7 css3中calc属性不生效问题

例如:

div{
    // 注意calc中加,减的运算符号左右必须有空格,才会生效,如果:calc(100vh -60px),calc(100vh-60px)不生效。
    min-height: calc(100vh - 60px);
}

8 项目部署中出现403报错

8.1 现象

项目部署中,使用nginx的部署,项目要求需要配置上下文根,即在域名后面新增一个字段,例如http://core.he-live.com/devMng/#/,然后打包的项目必须放在home/work/combatmap-manage里面,然后出现的问题就是项目部署以后出现403错误。

8.2 原因

经过不断尝试,将这个打包文件放在其他目录上可以正常运行,但是放在work目录下面就会出现403禁止访问。

8.3 解决办法

由于work文件夹里面没有权限,需要开通权限,就可以了。

9 vue项目部署

9.1 部署vue项目到测试环境,只需要配置nginx配置文件即可。(不需要修改其他文件)

location / {
root /home/test/;//将vue打包以后的dist文件里面的static和index.html存放路径
index index;
}

9.2 线上部署,将vue项目部署到Tomcat服务器上

(1)环境需求 服务器上安装Tomcat环境8.5版本。Tomcat统一安装到服务器/home/work/combatmap/(本人安装的目录)。 (2)打包dist文件

修改config目录下的index.js,将build对应的assetsPublicPath: '/'改为assetsPublicPath:'./',不然打包部署到tomcat上面访问是一片空白。

新增build文件夹下面util.js的publicPath,否则会出现elementUI图标不显示的问题。

if (options.extract) { return ExtractTextPlugin.extract({ use: loaders, fallback: 'vue-style-loader', publicPath: '../../', //这是新增的字段 }) }

(3)将dist下面的两个文件(static和index.html)直接复制到tomcat的webapps文件夹。

(4)修改tomcat配置文件server.xml文件

--修改tomcat端口

--修改host标签,如下图,红框新加内容。

注意:docBase的路径就是Tomcat的webapps下面。

(5)启动Tomcat服务,访问Tomcat服务器地址ip:端口,能访问首页 (6)配置nginx服务器上的nginx,添加下图转发配置,注意下图红框中的端口为2.a) 中的tomcat端口。 (7)重启nginx服务。访问nginx对应的ip:端口,能出项目首页。

9.3在原有项目基础上加上下文根(即http://abd.com/test)

(注:196服务器为总nginx服务器,198服务器为前端tomcat部署项目) 比如我们需要加的上下文根为mapmanage则只需要在path中加入mapmanage,前面有无‘/’都可以。

(1)测试1

196服务器上配置如下

location / {
    proxy_pass http://123.456.198:80
}

当在地址栏中输入http://123.456.196:80以后,404报错。 当在地址栏中输入http://123.456.196:80/mapmanage,会直接跳到http://123.456.198:80/mapmanage/#/login。 (2)测试2

196服务器上配置如下

location / {
    proxy_pass http://123.456.198:80/
}

当在地址栏中输入http://123.456.196:80以后,404报错。 当在地址栏中输入http://123.456.196:80/mapmanage,会直接跳到http://123.456.198:80/mapmanage/#/login。

(3)测试3

196服务器上配置如下

location / {
    proxy_pass http://123.456.198:80/mapmanage
}

当在地址栏中输入http://123.456.196:80以后,会直接跳到http://123.456.198:80/mapmanage/#/login 当在地址栏中输入http://123.456.196:80/mapmanage,会直接跳到404页面

(4)测试4

196服务器上配置如下

location / {
    proxy_pass http://123.456.198:80/mapmanage/
}

当在地址栏中输入http://123.456.196:80以后,会直接跳到http://123.456.198:80/#/login 当在地址栏中输入http://123.456.196:80/mapmanage,会直接跳到404页面

(5)测试5

196服务器上配置如下

location /mapmanage {
    proxy_pass http://123.456.198:80/mapmanage/
}

当在地址栏中输入http://123.456.196:80以后,nginx欢迎页面。 当在地址栏中输入http://123.456.196:80/mapmanage,显示空白页。 当在地址栏中输入http://123.456.196:80/mapmanage/,会跳到http://123.456.196:80/mapmanage/#/login。

(6)测试6

196服务器上配置如下

location /mapmanage/ {
    proxy_pass http://123.456.198:80/mapmanage/
}

当在地址栏中输入http://123.456.196:80以后,nginx欢迎页面。 当在地址栏中输入http://123.456.196:80/mapmanage,还是在地址栏中输入http://123.456.196:80/mapmanage/,都会跳到http://123.456.196:80/mapmanage/#/login。

至此无论是访问196IP地址/mapmanage还是198IP地址/mapmanage/都会访问198tomcat服务器上的地址。

10 移动端调用拨号键盘

<a href="tel:13828172679">13622178579</a>

11 vux使用bug

安装vux,vue-loader,vux-loader@14.2.2,yaml-loader,less,less-loader依赖的时候要使用npm安装,不要使用cnpm安装,在vuecli3中的vue.config.js上配置如下图

configureWebpack: config => {
    require('vux-loader').merge(config, {
        options: {},
        plugins: [
            {
                name: 'vux-ui'
            },
            {
                name: 'duplicate-style'
            }
        ]
    })
}

12 input输入框设置maxlength无效问题

当type = 'number'的时候设置maxlength无效,但当去掉type的时候,则会生效

13 当判断为null或者‘’的时候,避免使用length,应该直接使用这个变量名作为判断条件

例如:

if(name.length > 0){} // 尽量避免这种方法使用,因为如果name为null则会报错,页面出不来。
if(name) {} // 应该使用这个

14 两栏布局时,越界的伪元素不显示或者显示横向出现滚动条的问题。

设计要求:两栏布局,左面和右面有可能又很多的列表,可滑动,并且当点击某个列表的时候,会有个三角形指示。

图14.1 两栏布局默认显示效果

14.1 现象

(1) 当点击列表的时候,如下图所示,会出现横向滚动条。

图14.2 当左面列表被点击时效果图

(2)代码:

<style>
    *{
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        list-style: none;
    }
    .father{
        position: fixed;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
    }
    /* 主要内容 */
    .content{
        display: flex;
        flex-wrap: wrap;
        width: 100%;
        height: calc(100% - 2.5rem);
    }

    /* 左面 */
    .content .left{
        width: 8.5rem;
        height: 100%;
        overflow-y: auto;
    }
    .content .left ul{
        width: 8.5rem;
    }
    .content .left > ul > li{
        background: #fff;
        width: 100%;
        height: 2.5rem;
        line-height: 2.5rem;
        text-align: center;
        border-bottom: 1px solid #ccc;
    }
    /* 右面 */
    .content .right {
        width: calc(100% - 8.5rem);
        background: gold;
        height: 100%;
        overflow-y: auto;
        text-align: center;
    }
    .content .right > div{
        height: 1000px;
    }
    .active{
        background-color: red!important;
        color: #fff;
        position: relative;
    }
    .active::after{
        content: '';
        display: block;
        position: absolute;
        border: 5px solid;
        border-color: transparent transparent transparent blue;
        right: 0;
        top: 50%;
        transform: translate(100%, -50%)
    }
    /* 底部 */
    .footer{
        display: flex;
        height: 2.5rem;
        z-index: 1;
        position: absolute;
        width: 100%;
        background-color: red;
    }
    .footer span{
        width: 50%;
        border: 1px solid blue;
        line-height: 2.5rem;
        text-align: center;
    }
</style>
<body>
    <div class="father">
        <div class="content">
            <div class="left">
                <ul>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> 大家好 </li>
                    <li> hahaha </li>
                </ul>
            </div>
            <div class="right">
                <div>
                    右面内容
                </div>
            </div>
        </div>
        <div class="footer">
            <span>取消</span>
            <span>确定</span>
        </div>
    </div>
    <script>
        var ali = document.getElementsByTagName('li');
        var len = ali.length;
        for(let i=0; i < len; i ++){
            ali[i].index = i;
            ali[i].onclick=function() {
                for(let j= 0; j < len; j++){
                    ali[j].className = '';
                }
                ali[i].className = 'active'
            }
        }
    </script>
</body>

14.2 解决办法

(1)方法1:将可以将左面内容增加一个委员的宽度,这样,伪元素既可以显示,又不会出现导航条。代码如下:

    /* 左面 */
    .content .left{
        width: 9rem;  /*新加内容*/
        height: 100%;
        overflow-y: auto;
        overflow-x: hidden; /*新加内容*/
        background: gold; /*新加内容*/
    }
    .content .right {
        width: calc(100% - 9rem);/*新加内容*/
        background: gold;
        height: 100%;
        overflow-y: auto;
        text-align: center;
    }

(2) 方法2,如下图:

    .content .left{
        width: 8.5rem;
        height: 100%;
        overflow-y: auto;
        overflow-x: hidden; /*新加内容*/
    }
    .content .left ul{
        width: 8.5rem;
        position: absolute; /*新加内容*/
    }

原理:当overflow: hidden元素在绝对定位元素和其包含块之间的时候,绝对定位元素不会被剪裁,也就是设置overflow为hidden的元素本身没有定位,并且子元素绝对定位,则子元素不会被裁减。

例如: 以下两种绝对定位元素不会被剪裁:

    <div style="overflow: hidden;">
  <img src="big.jpg" style="position: absolute;">
</div>
<div style="position: relative;">
  <div style="overflow: hidden;">
    <img src="big.jpg" style="position: absolute;">
  </div>    
</div>

以下两种绝对定位元素会被剪裁:

<div style="overflow: hidden; position: relative;">
  <img src="big.jpg" style="position: absolute;">
</div>
<div style="overflow: hidden;">
  <div style="position: relative;">
    <img src="big.jpg" style="position: absolute;">
  </div>    
</div>

15 移动页面在苹果(ios)手机滑动卡的问题

15.1 现象

同一套代码,在安卓手机上正常,但是在苹果手机上滑动很卡。

15.2 原因

当使用overflow-y:scorll/auto属性的时候,内容超出容器溢出滚动的效果很迟顿,特别是在IOS系统里。

15.3 解决办法

这时候可以使用-webkit-overflow-scrolling:touch这个属性,让滚动条产生滚动回弹的效果,就像ios原生的滚动条一样流畅。

  • 说明:-webkit-overflow-scrolling:控制元素在移动设备上是否使用滚动回弹效果。

    • auto:使用普通滚动, 当手指从触摸屏上移开,滚动会立即停止。
    • touch:使用具有回弹效果的滚动,当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果,继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。
  • 需要注意的事项:

通过动态添加内容撑开容器,结果根本不能滑动。在safari上,点击其他区域,再在滚动区域滑动,滚动条无法滚动。 在safari上,使用了-webkit-overflow-scrolling:touch之后,页面偶尔会卡住不动。

  • 解决问题的方法:

第一步:将使用-webkit-overflow-scrolling的元素的定位取消或手动改成position:static;

第二步:将使用-webkit-overflow-scrolling的元素添加一个子元素,设置子元素的高度为height:101%,或者为了精确一些写成height:calc(100vh + 1px),注意这个加号前后都要有空格.

16 个别手机输入框和label标签窜位。

16.1 现象

在移动端开发h5页面的时候,华为magic手机出现输入框和在label下面,而不是在同一行,其他手机都是正常,输入框使用的是float,label的文字内容就是被一个span标签包裹起来。例如:

    <div>
        <span>推荐人开户行支行:</span>
        <input type="text">
    </div>

这个推荐人开户行支行字段由于是新增的,并且比之前的字段都要长,本地测试一点问题都没有,并且大部分手机都没有问题,但是荣耀magic却出现了这个问题。

16.2 原因

华为magic的手机屏幕虽然大,但是由于分辨率的问题,导致,个别元素标签超过一行的长度,所以导致span和input不在一行。

16.3 解决方法

有两种,第一是缩短span的宽度,即去掉一个字,或是将input标签宽度缩小。

17 苹果手机的输入框弹出键盘后,页面下方有空白

17.1 现象

在使用苹果手机填写报单页面的时候,当点击输入框,输入信息完成之后,关闭输入框,发现手机底部有输入框留下的灰色区域,但是报单页面的部分信息被隐藏了,导致个别字段不能填写,并且提交不了报单。

17.2 原因

h5填写报单的页面的根元素都是设置div宽高都是100%,当键盘显示的时候,会挤压window的高度,使其变下,所以键盘消失以后会留下空白。

17.3 解决方法

    fixScroll() {
        let windowFocusHeight = window.innerHeight
        if (this.windowHeight == windowFocusHeight) {
          return
        }
        let currentPosition;
        let speed = 1; //页面滚动距离
        currentPosition = document.documentElement.scrollTop || document.body.scrollTop;
        currentPosition -= speed;
        window.scrollTo(0, currentPosition); //页面向上滚动
        currentPosition += speed; //speed变量
        window.scrollTo(0, currentPosition); //页面向下滚动
    },
    mounted() {
        var a = document.getElementsByTagName('input');
        this.windowHeight = window.innerHeight
        for (let i = 0; i < a.length; i++) {
           a[i].addEventListener('blur', this.fixScroll);
        } 
    }

18 骨架屏

当使用page-skeleton-webpack-plugin的时候,能自动生成骨架屏shell文件,但是打包以后却显示不出来。 步骤参照

segmentfault.com/a/119000002…

github.com/ElemeFE/pag…

感觉没有问题,一步一步走,弄了好几次,没有都会生成shell文件,但是打包以后并不会进行显示。后来,尝试了一下在<div id="app"></div>变成<div id="app"><!-- shell --></div>重新打包以后就可以出现了。

19 let和var的区别

之前知道的是let和var的作用域不一样,例如一个for循环,要是用var定义,则可以在for循环的外部访问,但要是如果用let定义,则在外面访问不了,知道的仅仅局限于此,知道遇见下面的:

    // 第一种结果都是一样的,也就是oLi.length
    var oLi= document.querySelectorAll('li')
    for (var i = 0,len = oLi.length; i < len; i++){
        oLi[i].onclick = function(){
            console.log(i)
        }
    }
    // 第二种结果却是递增的,也就是从0到oLi.length
    var oLi= document.querySelectorAll('li')
    for (let i = 0,len = oLi.length; i < len; i++){
        oLi[i].onclick = function(){
            console.log(i)
        }
    }

20 使用vue.addRoutes需要注意的事项

当使用vue做权限控制的时候,需要动态的改变路由,所以此时需要使用这个。

20.1 需要使用自定$addRoutes

自定义$addRoutes方法,如果直接使用router.addRoutes定义的话,仅仅只是加路由,原有路由还不会变。所以会出现“Duplicate named routes definition:”的错误提示。

并且如果一个用户在页面不刷新的前提下,重复登录,或者退出以后由另外一个用户进行登录,则上个用户的权限还是会保留下来。

    router.$addRoutes = (params) => {
      router.matcher = new VueRouter().matcher;
      router.addRoutes(params)
    }

20.2 router.options.routes和router.$addRoutes配合使用。

  • 使用router.options.routes原因

创建项目的时候,我们会把路由信息routes保存在vuex中,当用户进行登录的时候,我们获取当前用户的权限,来动态改变vuex中的routes, 虽然此时routes数值确实改变了,我们初始化也是向下面这么写的:

    const router = new VueRouter({
      routes: store.state.routes
    })

但是此时的router.options.routes确没有变化,所以此时需要将生成的路由重新赋值给它:router.options.routes = newRoutes;

  • 使用router.$addRoutes原因

(1)如果仅仅使用上面的router.options.routes, router中路由虽然会改变,但是页面加载时会出现空白,路由并没有加入到页面上。 所以需要使用这个router.$addRoutes(res);

(2)但是如果仅仅使用这个,而不需要使用router.options.routes。页面虽然会正常显示,一切看起来都正常,但是实际上router.options.routes的数值还是原来的值。

所以最好是将二者都加上

20.3 return next({...to, replace: true});

替代之前的路由,防止留下历史记录。

21 父组件传值子组件后立刻调用子组件方法

21.1 现象

父组件传递给子组件一个value, 如果在父组件中改变value,然后直接调用子组件中的方法,则会发现,方法调用完之后,子组件里面的值才发生改变。

const Child = {
    template: `<div ref="child">子组件</div>`,
    props: ['value'],
    methods: {
        checkVal() {
            console.log(this.value)
        }
    }
}
 Vue.component('Father', {
     data() {
        value = 1;
     },
     components: {
        Child
     }
     mounted() {
        this.value = 2;
        this.$refs.child.checkVal();
     },
     template: `<div>父组件<Child :value="value"/></div>`
 })

21.2 解决办法

  • 将所改变的数值作为参数传递给子组件的函数this.$refs.child.checkVal(2)
  • 在子组件中使用watch进行监听数据的变化。

22 同一个文件不能上传第二次问题

22.1 现象

文件上传时,第一次点击文件,可以进行上传,上传成功以后,如果此时继续上传该文件,则不会发生任何变化,就像没有点击时一样。

22.2 原因

同一个文件上传时,并没有触发change事件。

22.3 解决办法

上传成功以后,清空文件的value:this.$refs.file.value=''

23 URL传值时会转换数据类型

23.1 现象

一个页面的id(Number类型)通过传值给另外一个页面,由于这个id是下拉框中的一个选项,但是传递到另外一个页面的时候,却没有显示当前下拉框的选项,但是数值明明都已经传递过来了,唯独不显示。

23.2 原因

url传值会自动把数据变为字符串类型。

23.3 解决办法

获取数据以后进行类型的转化即可。

24 padding会影响边框为1px的元素显示情况。

24.1 现象

在使用elementUI 中的dataPicker组件时,由于修改了部分样式以后,发现日期控件的边框虽然都设置为1px,但是边框却会粗细不一(如图24.1 a), 但如果要是把边框设置为2px则都是一样的宽度(如图24.2 b);

图24.1 a

图24.1 b

24.2 解决办法

首先确认日期控件的边框有没有生效,在控制台审查元素的时候,将border选项选中和取消选中,确实能发现边框的显示和隐藏,但既然都是同样的1px为啥会显示上图中的样子,边框粗细不一。然后将这个元素下面的样式,一个个取消选中,终于发现,当把代码里面.el-range-editor.el-input__inner元素的padding的样式去掉以后,显示正常,原因可能时该元素是(inline-block一族,例如diaplay设置为inline-flex等这些)如下图。

图24.2

25 css实现禁止点击

25.1 用处场合

在使用elementUI中的表格进行排序时,本人设置了2中状态:升序和降序,当点击表头上面的文字时,正常发起请求(统一由后端进行排序),样式显示也完全正常(如图),但是点击文字旁边的图标时,却有第三种状态null, 所以为了避免这个问题,才引发了此次需求。

图 25.1 表格排序

25.2 解决办法。

获取图标按钮,加入下面的样式配置。

    .sort-caret{
    // 加入这个的意义在于,禁止点击表格的上下排序按钮,因为如果没有这么设置的话,会出现三种状态:升序,降序,默认。但是目前项目是没有默认的。
    pointer-events: none !important;
    cursor: default;
  }

26 ios系统下new Date()

这是同事做项目的时候,发现的一个bug, 前端new Date('2019-01-01')时,在ios系统上传到后台值为空,但是安卓手机则是正常的,苹果手机只能使用new Date('2020/01/01')来创建日期。

27 excel文件下载问题

27.1 现象

excel文件下载时,出现wps可以打开,但是excel2007或者excel2013却打不开。

27.2 背景

excel 文件下载是需要token验证的,所以不能通过后台返回一个地址链接形式,直接进行下载。所以需要后台以二进制文件流的形式返回给前端,并且文件格式支持xls和xlsx。

27.3 原因

后台通过postman发起请求,下载完成的文件,无论是在excel还是在wps都可以进行打开,经过仔细对比postman和前端对返回文件流的对比,唯一的区别就是,前端修改了文件名。

前端修改文件名的同时,设置了文件格式为xlsx类型,正是由于这一步导致的问题,于是将文件格式改为xls形式,就可以了。

经过查看后台代码,发现是以xls格式返回的文件流,虽然不管哪种格式,后台响应头都为Content-Type: application/vnd.ms-excel;charset=UTF-8; 但是后台将文件转变为文件流时,要么以xls,要么以xlsx进行写入。所以前端文件的后缀名要进行匹配,并不是可以随意更换二者的后缀名。

27.4 解决办法

前台文件的后缀名要与后台写入的文件流格式相匹配即可。

28 vue项目报错(avoid using non-primitive value as key)

vue项目中v-for将key设置为item(注:item是一个对象),设置key的时候,不要使用对象类型,要使用原始数据类型。

29 /deep/解决style中设置scoped后样式不生效。

29.1 现象

vue项目中引入第三方的组件,想在父组件中修改这个子组件的样式问题,但却修改不成功。

29.2 原因

如果一个子组件设置了scoped的属性,元素添加了一个class=‘myClass’,但是最终呈现出来的class并不是这个名字,而是在当前的元素上增加了一个属性:data-v-e0542f64, 最终样式表变为myClass[data-v-e0542f64]

29.3 解决办法

设置样式的时候,在 .myClass{}前加上/deep/, 即:/deep/ .myClass {}

30 vue+websocket项目,scrollIntoView需要未达到指定位置。

30.1 现象

在使用socket.io实现实时通讯过程中,都会有个需求,那就是需要将消息框中的滚动条滚动到最新的消息对应的位置,因此选择使用el.scrollIntoView(),消息列表中最后面加入一个空div,但是每次滚动的时候都相差一个聊天内容。

30.2 原因

vue渲染机制是异步的,当你更新数据时,新增一条消息,但是如果此时立马使用scrollIntoView就会返回上一次div的位置。

30.3 解决办法

在this.$nextTick()中调用scrollIntoView()

31 webpack+vue中使用three.js加载图片不生效

在使用three.js中的new textureLoader().load('/img/a.jpg')时不生效,最后尝试使用require方式才实现。new textureLoader().load(require('/img/a.jpg'))

32 iframe引入pdf线上不显示问题

32.1 现象

紧急需求,要求项目(西安恒交所)首页显示公告,公告的内容是pdf文件,本地使用ifram测试时,可以完全显示,但是一到产线就显示不了。

32.2 原因

本该显示pdf文件的,却显示一片空白,并且上面还提示:“访问被拒绝”,后来打开控制台发现下图报错信息。然后发现原来是nginx 配置了X-Frame-Options: 'sameorigin';

32.3 解决办法

让运维同学帮忙修改,发现将X-Frame-Options:'ALLOW-FROM 域名'并没有生效,后来将其去除,取而代之的是使用add_header Content-Security-Policy "frame-src 域名;"

33 权限管理中多个用户在不同tab页登录冲突问题。

33.1 现象

在第一个标签页中进行登录用户1,然后此时在另一个标签页在登录用户2,则此时的localStorage中的用户1信息就会被用户2信息覆盖掉。

33.2 原因

由于存储用户信息的是一个写死的字段“userInfo”,所以当后一个用户登录的时候,肯定会把之前的用户信息覆盖掉。

33.3 解决办法

由于本项目中的用户的用户名是唯一的,所以统一使用用户的用户名作为key来进行保存,然后当页面刷新的时候,获取localstorage最后一个用户即可。

34 elementUI中的下拉框溢出现象。

34.1 现象

如下图所示,当点击下拉框的选项时,此时继续滚动滚动条,下拉框中的选项会溢出弹框边界。

图34.1 下拉框溢出

34.2 原因

由于elementUI中设置了定位。

34.3 解决办法

通过网上查找资料,将下拉框的属性popper-append-to-body设置为false,并不能解决,后来查找其他资料,得到如下代码,可以为下面代码单独创建一个文件,然后混入到app.vue中即可。具体原理为:由于当我们弹框页面其他部分,下拉框会自动隐藏,所以利用这一特性,我们增加mousedowe,和mousewheel监听事件,如果有鼠标滚动时或者鼠标按住下拉框的滚动调时,我们就触发document的点击事件,并且由于冒泡的原理,下拉框会自动关闭。

    let lock = true;
    let el = null;
    const MousedownEvent = new Event('mousedown', {bubbles:true});
    const MouseupEvent = new Event('mouseup', {bubbles:true});
    const fakeClickOutSide = () => {
      document.dispatchEvent(MousedownEvent);
      document.dispatchEvent(MouseupEvent);
      lock = true; // console.log('dispatchEvent');
    };
    const mousedownHandle = e => {
      let classList = e.target.classList;
      if(classList.contains('el-select__caret') || classList.contains('el-input__inner')) {
        lock = false;
        return;
      }
      if(lock) return;
      fakeClickOutSide();
    };
    const mousewheelHandle = e => {
      if(lock || e.target.classList.contains('el-select-dropdown__item') || e.target.parentNode.classList.contains('el-select-dropdown__item')) return;
      fakeClickOutSide();
    };
    const eventListener = (type) => {
      el[type + 'EventListener']('mousedown', mousedownHandle);
      window[type + 'EventListener']('mousewheel', mousewheelHandle);
      window[type + 'EventListener']('DOMMouseScroll', mousewheelHandle); // fireFox 3.5+ 
    }
    export default {
      mounted() {
        el = this.$root.$el;
        el.addFakeClickOutSideEventCount = el.addFakeClickOutSideEventCount || 0;
        (! el.addFakeClickOutSideEventCount) && this.$nextTick(() => {
          eventListener('add');
        });
        el.addFakeClickOutSideEventCount += 1;
      },
      destroyed() {
        eventListener('remove');
        el.addFakeClickOutSideEventCount -= 1;
      },
    }

35 vue中的filters函数内获取不到this

解决办法使用computed或者,将需要的其他参数传入filter函数中。

36 axios请求体缺失

axios如果是post请求,但是不需要传任何字段,则必须传一个空对象,否则会报错:请求提缺失。

37 vue项目打包提示信息

37.1 出现conficting order相关警告

如下图所示,项目打包的时候,出现下面提示信息。

图37.1 提示信息图

刚刚看到这些提示信息,一头雾水,测试了打包好的文件,发现功能正常,但是,本人比较钻牛角尖,不允许有一点警告或者提示信息,通过仔细观察,图片中的红色标起来的地方:pagination组件和select组件的。感觉应该是这两个文件的问题,在接着往后看,看最下面的红色圈起来的地方:原来是他们希望chunk要按照顺序引入,即所有的组件里面最好都要按照相同的顺序来引入这两个组件,比如,在其中一个组件先引入select,再引入pagination组件,另外一个组件最好不要先引入pagination,再引入select。

至此,只需要改变一下引入顺序,问题解决。

37.2 caniuse-lite is outdated

出现如下图的提示,按照提示进行操作yarn upgrade即可,然后重新打包。

38 通过js获取ip(WebRTC API)

38.1 背景

由于战报项目需要账号和公司电脑ip进行绑定,即只有当账号和ip一致时,才能登录成功,开始计划是由后台获取,但是后台获取不了,只能获取到外网的ip,获取不到内网的ip, 所以只能由前端实现,但是前端获取ip本来就属于侵犯隐私,有些浏览器禁止获取真实ip,都会将正式ip进行转换。

38.2 WebRTC(Web Real-Time Communications)

是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和(或)音频流或者其他任意数据的传输。WebRTC包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建点对点(Peer-to-Peer)的数据分享和电话会议成为可能。

它于2011年6月1日开源并在Google、Mozilla、Opera支持下被纳入万维网联盟的W3C推荐标准。

webRTC 是HTML 5 的一个扩展,允许去获取当前客户端的IP地址,可以查看当前网址:net.ipcalf.com/。

但如果使用 chrome 浏览器打开,此时可能会看到一串类似于:

e87e041d-15e1-4662-adad-7a6601fca9fb.local

的机器码,这是因为chrome 默认是隐藏掉 内网IP地址的,可以通过修改 chrome 浏览器的配置更改此行为:

  • 在chrome 浏览器地址栏中输入:chrome://flags/
  • 搜索 #enable-webrtc-hide-local-ips-with-mdns 该配置 并将属性改为 disabled

38.3 代码实现

    function getUserIP() {
      var RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window
        .mozRTCPeerConnection;
        let that = this
      if (RTCPeerConnection)(() => {
        var rtc = new RTCPeerConnection()
        rtc.createDataChannel(''); //创建一个可以发送任意数据的数据通道
        rtc.createOffer(offerDesc => { //创建并存储一个sdp数据
          rtc.setLocalDescription(offerDesc)
        }, e => {
          console.log(e)
        })
        rtc.onicecandidate = (evt) => { //监听candidate事件
          if (evt.candidate) {
            // var ip_regex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/
            // console.log(evt.candidate)
            // var ip_addr = ip_regex.exec(evt.candidate.candidate)[1];
            var ip_addr = evt.candidate.address
            that.ip = ip_addr
            console.log(that.ip)
          }
        }
      })()
    },