首页 > 代码库 > [前端] 记录一些工作中遇到的问题,及解决方案

[前端] 记录一些工作中遇到的问题,及解决方案

最近一年,在开发实践过程中遇到了不少问题,大多都能得到解决

部分知其原理,部分只能做到解决问题,而半年前遇到的问题,或多或少都忘得差不多了

是该记录一下一些问题,防止再遇到就得再查资料了

 

1. 浏览器在开启有道划词插件的时候,使用 AjaxFileUpload 插件上传文件报错

技术分享

开启插件时,该插件会往文档中添加音频元素节点

技术分享

而AjaxFileUpload插件的上传文件处理方式是,获取返回的实体内容,直接进行eval 解析,解析失败,报错,则无法上传

技术分享

这插件在旧系统中常用到,解决办法就是不用这个插件,或者停用有道划词插件

另外,我的解决方案则是用了FormData对象来异步上传文件

 

2. Uncaught TypeError: jQuery.handleError is not a function

使用某些旧插件的时候,会出现这个错误

插件使用了handleError这个方法,而新版的jQuery以及去除了这个方法,所以这时可以弃用插件或者为JQ加回此方法

jQuery.extend({    handleError: jQuery.handleError || function( s, xhr, status, e ) {        // If a local callback was specified, fire it        if ( s.error ) {            s.error( xhr, status, e );        }        // If we have some XML response text (e.g. from an AJAX call) then log it in the console        else if(xhr.responseText) {            console.log(xhr.responseText);        }    }});

 

3. 异步方式实现导出Excel表格

用异步的方式导出数据,用Ajax貌似不行(行的告诉我怎么搞呗)

目前想到的方法就是用iframe,设置不同的src即可让后端返回相应数据

 

4. 页面使用Angular.js(1),页面中iframe中初始设置src属性的话,会导致页面重新加载一次

例如设置一个初始值,某些操作之后再更改src

<iframe src="#" class="export-iframe"></iframe>

Controller似乎会触发两次,可以看到加载的请求多触发了一次,且第二次的链接中会多了一个#

解决办法就是直接不设置这个属性

<iframe class="export-iframe"></iframe>

 

5. 父页面中有iframe,iframe里面有分页按钮,在父页面对iframe做加载之后监听iframe中点击事件的操作,初始第一页正常,但点击第二页之后事件就失效了

原代码:

技术分享

第一次成功打印出来,即触发了load事件,但点击下一页后,iframe实际上已经刷新了,但并不会再触发这个load事件

后来的解决办法是换了种监听方法,区别主要是获取iframe对象的方式变了,还不知为啥会这样?

技术分享

 

6. 在iframe中的预览pdf文件时,有时embed元素未占满整个iframe,而是正好一半,一半

技术分享

技术分享

目前还不知如何解决,把embed的宽高由100%设置成接近99%的时候,反而占满iframe的概率增多了不少..

 

7. 在iPad下,无法实现自动聚焦

这问题应该是解决不了的,是iOS自带的,方案只能是由用户触发mousedownmouseupclick之类的事件后再调用

技术分享

 

8. 有个插件叫做 magicsearch ,初期用得还好,不过之后断断续续发现了一些问题

在匹配不到数据的时候,匹配结果直接显示了error文案,看看源码,直接改掉

技术分享

第二个坑是它直接把绑定元素的事件都注销了,这样太暴力很不好

技术分享

第三个坑是它给只读的style属性赋值,这种方式在严格模式是被禁止的,而这插件正好自个又用了严格模式

坑就坑在:在Angular.JS(1)环境下使用iPad的时候才报错,PC上用Angular.JS正常,iPad下用非Angular.js正常..

解决方法也很暴力,直接去掉插件的严格模式

技术分享

技术分享

第四个坑是它用了Array.from,而这方法支持度是chrome45+,所以稍低版本的就遭殃了

 

9. bootstrap v3 的collapse折叠组件使用了click的事件监听方式,在移动端会有300ms的延迟

官方貌似在v4中修复了,用v3的话,就自个添加touchstart事件的支持,还要注意touchstart事件触发之后还会触发原监听的click事件

可按需来把它注销掉,移动端即有如丝般顺滑的collapse

// 移动端iOS click有延迟  添加折叠的touchstart事件支持        if (isiOS) {            $(document).off(‘click.bs.collapse.data-api‘, ‘[data-toggle="collapse"]‘);            $(document).on(‘touchstart.bs.collapse.data-api‘, ‘[data-toggle="collapse"]‘, function (e) {                var $this   = $(this), href                var target  = $this.attr(‘data-target‘)                    || e.preventDefault()                    || (href = http://www.mamicode.com/$this.attr(‘href‘)) && href.replace(/.*(?=#[^\s]+$)/, ‘‘) //strip for ie7                var $target = $(target)                var data    = http://www.mamicode.com/$target.data(‘bs.collapse‘)                var option  = data ? ‘toggle‘ : $this.data()                var parent  = $this.attr(‘data-parent‘)                var $parent = parent && $(parent)                if (!data || !data.transitioning) {                  if ($parent) $parent.find(‘[data-toggle=collapse][data-parent="‘ + parent + ‘"]‘).not($this).addClass(‘collapsed‘)                  $this[$target.hasClass(‘in‘) ? ‘addClass‘ : ‘removeClass‘](‘collapsed‘)                }                $target.collapse(option)            });        }

 

10. iOS10+会忽略meta 标签的user-scalable=no,没错苹果就是那么牛别

<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no">

页面要禁止用户缩放,可以使用JS来辅助处理

// 禁止缩放  iOS10+会忽略meta的user-scalable=no        document.documentElement.addEventListener(‘touchstart‘, function (event) {            if (event.touches.length > 1) {                event.preventDefault();            }        }, false);

另外要注意的是,上面只是禁用了双指的缩放,还有一种缩放叫做双击缩放,而iPad下是没有双击事件的,所以只能模拟

引用st上的一段双击事件支持

(function($){            // Determine if we on iPhone or iPad            var isiOS = false;            var agent = navigator.userAgent.toLowerCase();            if(agent.indexOf(‘iphone‘) >= 0 || agent.indexOf(‘ipad‘) >= 0){                   isiOS = true;            }            $.fn.doubletap = function(onDoubleTapCallback, onTapCallback, delay){                var eventName, action;                delay = delay == null? 500 : delay;                eventName = isiOS == true? ‘touchend‘ : ‘click‘;                $(this).bind(eventName, function(event){                    var now = new Date().getTime();                    var lastTouch = $(this).data(‘lastTouch‘) || now + 1 /** the first time this will make delta a negative number */;                    var delta = now - lastTouch;                    clearTimeout(action);                    if(delta<500 && delta>0){                        if(onDoubleTapCallback != null && typeof onDoubleTapCallback == ‘function‘){                            onDoubleTapCallback(event);                        }                    }else{                        $(this).data(‘lastTouch‘, now);                        action = setTimeout(function(evt){                            if(onTapCallback != null && typeof onTapCallback == ‘function‘){                                onTapCallback(evt);                            }                            clearTimeout(action);   // clear the timeout                        }, delay, [event]);                    }                    $(this).data(‘lastTouch‘, now);                });            };        })(jQuery);

然后就可以简单地进行调用了,双击后执行e.preventDefault()即可

$(document).doubletap(                    /** doubletap-dblclick callback */                    function(event){                        event.preventDefault();                    },                    /** touch-click callback (touch) */                    function(event){                    },                    /** doubletap-dblclick delay (default is 500 ms) */                    100                );

 

11. requestAnimationFrame的并行调用不能保证在不同帧执行

技术分享

希望的效果是在一帧一帧地执行,然而浏览器会将多个操作合并到同一帧中,检测发现

技术分享

有分帧的策略,但得在回调中再次调用requestAnimationFrame才行

技术分享

而实际操作中还需要一种并行调用就能分帧的方案,目前还没找到

然而文档中也指明了,是会放到同一帧的,所以估计这思路没戏了

技术分享

技术分享

 

12. iOS高版本中,在微信内访问网页,音频背景音乐无法自动播放

其实在高版本浏览器中,基于安全措施,已经不允许自动播放音频了,但在微信内是可以的

微信安卓环境下正常,但在高版本的iOS下就失效了,解决办法是在微信的WeixinJSBridgeReady事件中播放即可

        document.addEventListener(‘WeixinJSBridgeReady‘, function() {                ...                audio.play();                ...            }, false)

 

13. 分享微信页面到朋友圈时,没有图片logo

文档中指明了要只用绝对路径,即协议名、域名、路径等等都要写全,漏写了就没了

另外,路径要填写微信能够访问的地址,不能是内网的

技术分享

 

14. 在某些手机的微信中,分享页面成功后,会有已分享的提示信息,但有些手机却没有

所以开发页面的时候,还得自行加个已分享的回调提示,心桑..

 

15. 测试的时候发现,微信里页面的touchstart事件是不能取消的,即cancelable==false,在安卓的UC和Chrome中是为true的

技术分享

技术分享

 

16. 在smarty环境下,通过后端拿到了一个变量值放在a标签的href属性中,点击后跳转的链接不对,

即链接直接附在了当前页面url的后面,将http:// 替换成 // 却成功了,这还不知为啥..

技术分享

技术分享

 

17.  z-index有拼爹的性质,

z-index值只决定同一父元素中的同级子元素的堆叠顺序。父元素的z-index值(如果有)为子元素定义了堆叠顺序(css版堆叠“拼爹”)

要注意这个特性,另外要提及的是,z-index只有设置了非static的position值才能生效

 

18. 可编辑的元素,即设置了contenteditable为true的元素是可编辑的,它有个光标的坑

当设置元素的内容innerHTML改变时,原先的光标位置会直接 跑到前面,这个不好解决

跟光标相关的还有选中位置的值的处理

假如要实现contenteditabletrue的元素中内容的复制和粘贴功能,简单地复制粘贴就会取到错乱的HTML标签

结合getSelection、clipboardData相关的操作(还得注意这个对象在新版浏览器中以及移到了原生事件对象originalEvent下,之前是在ClipboardEvent对象下),

可以实现出来,不过并不是很完美,反正就是不好搞

// 标题组件粘贴            .on(‘paste‘, ‘.component-title‘, function(e) {                if (that.previewing) { return; }                e.preventDefault();                var $this = $(this),                    $title = $this.find(‘.title‘),                    t = e.originalEvent.clipboardData.getData(‘text/plain‘),                    s = window.getSelection(),                    oldTitle = $title.text(),                    startOffset = s.anchorOffset < s.focusOffset ? s.anchorOffset : s.focusOffset,                    endOffset = s.anchorOffset > s.focusOffset ? s.anchorOffset : s.focusOffset,                    start = oldTitle.slice(0, startOffset),                    end = oldTitle.slice(endOffset);                $title.html(start + t + end).attr(‘data-title‘, start + t + end);            })

 

19. 有一种坑、或者说是特性叫做 Font Boosting,这个特性也叫做 Text Autosizer,

现象就是字体的显示大小,与在CSS中指定的大小不一致

是 Webkit 给移动端浏览器提供的一个特性:当我们在手机上浏览网页时,很可能因为原始页面宽度较大,在手机屏幕上缩小后就看不清其中的文字了。而 Font Boosting 特性在这时会自动将其中的文字字体变大,保证在即不需要左右滑动屏幕,也不需要双击放大屏幕内容的前提下,也可以让人们方便的阅读页面中的文本。

实践中发现可以通过设置容器的max-height高度来实现,可以前去上述文章查看更多

    /* 有滚动条时 基于浏览器自身对字体的自动缩放,容器里的字体可能会变大,可定义一个属性避免 */            .job-type_list {                max-height: 999999px;            }

 

20. 这问题太久远都忘了,直接放个图吧

技术分享

21. 有个HTML5的视频插件叫做 Video.js,要实现视频海报的大小占满视频大小的话

直接设置width、height无效,设置background-size: cover 可以解决

 

22. 有个弹窗组件叫做 Layer.js,发现个问题是在layer弹出层中播放视频,视频的全屏按钮失效

技术分享

没啥办法了,最后直接暴力地解决了

技术分享

 

23. React的componentWillReceiveProps事件调用的时机还不太清晰,

虽然文档中已经写明了

技术分享

在测试过程中发现,就算父组件不传递props,子组件的这个方法也会被调用,还不知道为什么

也许是做浅比较 {} !== {}  吧 ?

 

24. React 的componentDidUpdate事件调用的时机还不太清晰,

虽说是在组件更新之后才调用,不过在一个复杂页面中测试发现,componentDidUpdate已经触发了,但却获取不到页面中的元素(看起来像是组件还没更新完成)

不知为啥,最后只能加个定时器处理了

 

25. React 的componentDidMount事件调用的时机还不太清晰,

虽说是在组件加载完成之后才调用,但在实践中的一个需求发现一个问题,不太好解决,查了蛮久还没看到合适的方案

比如要做一个弹窗组件,包含几个component,弹窗是调用子component出来,原想在调子component的时候才触发其componentDidMount事件,不料早在页面加载时所有component的componentDidMount事件就已经触发了,心桑..

 

26. jshint对redux中某些语法报错,需要做一些处理

技术分享

在文件起始处加上 /* jshint -W138 */ 即可

 

27.  排除由 input[type="file"] 点击引起的 window.onblur事件

很简单,使用document.activeElement 来处理即可

 

28.  在离开当前页面时判断是否有更改,做出提示

新版本浏览器基于安全机制,不能设置提示的样式,也不能设置提示中操作(确认和取消)的回调,也不能设置提示的文案(旧版的可以设置文案)

技术分享

技术分享

实现检测提示的方法很简单,例如

// 离开当前页面之前,判断是否有更改,做出提示        window.onbeforeunload = function (e) {            // 内容有改变且不是提交试卷之后的触发            if (this.state.changed && !this.state.saved) {                return ‘提示:当前内容有修改‘;            }        }.bind(this);

 

29. chrome59以上的已经不支持页面引入ftp文件了

技术分享

 

30. 有个编辑器叫做 wangEditor,也有一些坑

wangEditor默认的吸顶 滚动会影响页面上position: fixed的元素 可依据文档中配置为false

技术分享

word文档中复制带换行的内容到编辑器中会有乱码,如

技术分享

调试找到了解决办法,改了源码,给作者提了个pr就好了

技术分享

 

31. requirejs可以使用urlArgs参数自定义文件是否缓存

技术分享

 

32. checkbox和radio的样式基本是很难自定义的,一种解决方式是用其他方式模拟出来

比如用-webkit-appearance: menulist 模拟下拉框,用 圆角的span模拟radio

技术分享   技术分享

而下拉框的样式在手机上是调用原生内核的(浏览器的或WebView的),为了保证一致的效果(在测试过程中发现华为机型经常出现不一致的问题),可以统一用ul来模拟安卓下的下拉框弹层选择,在iPhone下保持其原生即可

技术分享

 

33. 有个插件叫做 jx-xlsx,可以用来给前端读取excel文件里的内容

 

34. 有个编辑器叫 Ueditor,也有一些坑

它会在全局设置ul 和 li 的list-style为none,导致改出现的列表样式消失了

还有一些与奇葩需求结合的坑,忘得差不多了

 

35. “微软雅黑” 和 “Microsoft YaHei”是有区别的,tell me why ~

 

36. 有时已经开启了Gzip压缩,但从timeline上看似乎是全量下载了,且看

技术分享

因暂重现不了,先用一幅图表示,TTFB 是几百ms左右,但Content Download却有十几秒

这种情况还不知为啥,但过一段时间又好了

 

对 TTFB 的理解还不够清晰,在测试中发现,页面加载资源缓慢

而页面基本不需要后端操作,所以后端的耗时应该不是主要的,也部署了CDN节点,所以首个报文头部传输太慢应该也不是主要的

后来发现,对页面中资源的请求又乱了,从timeline瀑布流中发现资源并不是按照页面代码顺序由上往下请求,比如<img 标签中的src资源和css文件中的background-image属性中的src资源加载的顺序,资源并行加载的数量不清晰

一堆的不清晰之中,尝试尽可能地在减小请求数与减小资源大小直接做平衡,

尽可能地让关键的资源在最先的并行顺序中加载,页面整体加载感觉就快多了

难点TTFB还与资源的加载时机有关?还得多查查

 

37. 表格中有大量数据时,很容易就会出现性能问题

表格ReflowRepaint代价都很高,在滚动、对表格项操作的时候,经常就卡顿了

优化方案得按实际需求来看

首先可以尝试:尽可能地只处理视窗可见的表格项即可,这样一来性能就可以翻个几十倍

然后尝试:尽可能避免不必要的Reflow和Repaint,CSSTriggers关于样式的,以及关于JS的DOM属性

然后尝试:尽可能地缓存,不必要的计算就不计算,十万项,每项节约0.01ms,那都能减少1s的卡顿

然后:优化DOM选择器等等

 

38: 移动端的动画常常会碰到卡顿问题,多半是掉帧太严重了

关于帧的知识点,还得多去理解requestAnimation、GPU、JS的事件循环机制、setTimeout/setInterval 、浏览器绘制原理等等

基本原则是大多数情况下用setTimeout,上战场时尽量避免setInterval,别忘了requestAnimation这个好助手,合理分配Composite Layer

还得多实践才能发现更多坑

 

39. 页面上可播放的视频大多需要是mp4格式的,且其格式需是H.264,否则某些情况下会碰到有声音没画面的现象

 

40. Angular.js(1)中经常会碰到 In Progress 的错误问题

估计是经验还不够吧,经常操作后就调用$scope.$apply()

目前的解决方式就是把操作放到$timeout

 

41. 在数据量大的时候,Angular.js(1)中的input只要放到了$scope相关域之中,就一卡一卡的

还不知为何,目前直接换成了用JQ操作的方式,甚是顺畅

 

42. 在数据量大的时候,Angular.js(1)重新更新视图(ng-repeat)会很卡,目前还没比较好的方案

而在更新数据操作的前一步,展示一个loading效果,竟会卡上好几秒,然后loadIng才出来就立马结束

可能是线程太繁忙GUI无法绘制?尝试将操作放到下一轮事件循环中或使用requestAnimationFrame,loading能按照预期显示出来,但视图却更新不成功,心桑..

 

43. Angular.js(1)的ng-repeat中过滤空的数据,在 讨论 中看到有好几种写法

技术分享

但是都失效..

 

44. mouseenter和mouseleave事件冒泡产生的问题,为了实现鼠标划过tr标红,划出tr取消标红

而由于冒泡的问题,划过的td时候就触发了父tr的mouseleave事件,所以加句

e.stopPropagation();

 

45. 使用webpack编译的过程中发现,文件耦合略为严重

假设webpack要编译15个页面文件,因为需要提取一个common.js文件,只改一个字,15个页面文件引用的common.js就得改

基于资源加戳,则这些页面都有改动了

如果某个React组件被共用了,改动到一半的时候有线上问题要插单,那么已经做的修改就只能按文件备份了,实在是不好管理

 

46. webpack编译耗时过长,该如何优化

目前加上了chunkhash,相对好了一些,但还是有不够快,可能还需要减少打包的文件数量,再看看吧

 

47. webpack打包后自动更新页面的资源路径

目前用着两种方式

使用 html-webpack-plugin 插件,提供模版文件及目标文件,不过好像生成的路径有点问题,基本还得自己再调整一下

技术分享

直接读取文件修改占位,提供模版文件和目标文件,Node.js

this.plugin(‘done‘, function(stats) {                let asset = stats.toJson().assets;                // console.log(asset);                let commonItem = asset.filter(function(item) {                    return item.chunkNames[0] === ‘common‘;                });                asset.forEach(function(item) {                    if (!files[item.chunkNames[0]] || item.chunkNames[0] === ‘common‘) {                        return;                    }                    fs.readFile(files[item.chunkNames[0]].src, ‘utf-8‘, function(err, doc) {                        if (err) {                            throw err;                        }                        doc = doc                            .replace(‘%‘ + item.chunkNames[0] + ‘.js%‘, item.name)                            .replace(‘%common.js%‘, commonItem[0].name);                        fs.writeFileSync(files[item.chunkNames[0]].dest, doc);                    });                });            });

 

48.  在PC上和模拟器上内容是垂直居中的,但在真机上内容却偏上了一丢丢..

 

49. 表格的表头、首行或首列固定等

表格数据多时,需要有个滚动时把某一信息行列固定的效果,方案有两种

->直接设置该行列的position

这是最直接的,在一般表格中可以使用,但数据量很多的时候,或者表头复杂(比如colspan=4等)的时候就不建议使用了,计算复杂且耗性能

->把需要固定的元素复制过来成新的表格,在需要的时候整个一起操作

这中方式可以很好地处理复杂表格的问题,且计算方式也容易一点

 

表格固定最大的难点在于保证固定项和内容项的宽高一致,在完全自适应内容的情况下是非常非常难做到的(在复杂表头的时候)

所以可以考虑做一些宽高的限制(比如width或设置max-width也可以)

其实主要就是在开始时遍历每一项所计算的宽高,赋值到固定表头的属性中,用colgroup辅助的效果会好一些,如

            <colgroup>                        <col width="12%"></col>                        <col width="8%"></col>                        <col width="8%"></col>                        <col width="30%"></col>                        <col width="10%"></col>                        <col width="8%"></col>                        <col width="8%"></col>                        <col width="15%"></col>                    </colgroup>        

 

另外,记得关注表头固定产生的性能问题

 

50. 待续..

 

[前端] 记录一些工作中遇到的问题,及解决方案