前端踩坑笔记

986 阅读12分钟

前言

我们在开发前端项目的时候时常会出现怪异的问题和奇怪的需求,这里我记录的踩坑笔记希望对大家有用。

问题一:H5项目ios竖屏拍照上传,图片被旋转问题

通过以下几个步骤来解决:

  1. 通过第三方插件exif-js获取到图片的方向
  2. new一个FileReader对象,加载读取上传的图片
  3. 在fileReader的onload函数中,得到的图片文件用一个Image对象接收
  4. 在image的onload函数中,利用步骤1中获取到的方向orientation,通过canvas旋转校正,重新绘制一张新图
  5. 将绘制的新图转成Blob对象,添加到FormData对象中,然后进行校正后的上传操作

问题二:H5项目iPhoneX底部适配

通过以下几个步骤来解决:

  1. viewport meta 标签增加属性viewport-fit=cover
<meta name="viewport" content="width=device-width, viewport-fit=cover, xxxx">
  1. body元素增加样式
body {
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}
  1. 如有fixed底部的元素,也增加上面样式
xxx {
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
  background-color: #fff; // 记得添加background-color,不然会出现透明镂空的情况
}

问题三:移动端1px 的边框。在高清屏下,移动端的 1px 会很粗

产生原因:首先先要了解一个概念DPR(devicePixelRatio) 设备像素比,它是默认缩放为 100%的情况下,设备像素和 CSS 逻辑像素的比值。目前主流的屏幕 DPR=2 或者 3。CSS中设置的px是逻辑像素,这就造成1px变成物理像素的2px或者3px,比如2 倍屏,设备的物理像素要实现 1 像素,所以 CSS 逻辑像素只能是 0.5px。

解决办法:通过CSS :before 选择器或CSS :after 选择器设置height:1px,同时缩放0.5倍实现。

问题四:a、li等标签中使用img后的高度多了4px

产生原因:img是行内元素,默认display: inline; 它与文本的默认行为类似,下边缘是与基线(baseline)对齐,而不是紧贴容器下边缘

解放办法:

  1. 将图像定义成img:{display:block;}
  2. 给父级设置固定高度,然后overflow:hidden
  3. 设置 font-size:0;
  4. 设置 img 的 vertical-align: bottom;
  5. 设置 img 的 margin-bottom: -4px;

问题五:text-align: justify和多行省略会出现样式冲突,具体表现在iOS上,缩略的三个点会偏左,和文字叠在一起

解决办法:不使用样式来实现多行省略,通过js获取文本行高判断是否要省略,要省略的情况直接把'...'定位覆盖最后几个文字

问题六:移动端某些机型不支持video标签的poster封面图片属性,特别是安卓

解决办法:

  • 用图片元素img来代替poster
  • 播放前显示img,隐藏video
  • 播放后显示video,隐藏img

问题七:两个垂直相邻的块级元素,当上下两个边距相遇时,起外边距会产生重叠现象,且重叠后的外边距,等于其中较大者(常见情况内层元素顶部会和外层元素重合影响样式布局)

解决办法:

  • 外层元素padding代替
  • 内层元素透明边框 border:1px solid transparent;
  • 内层元素绝对定位 postion:absolute:
  • 外层元素 overflow:hidden;
  • 内层元素 加float:left;或display:inline-block;
  • 内层元素padding:1px;

问题八:vue项目style加scoped后无法修改样式(无法修改组件库如:iview UI和Element UI 样式)

解决办法:

  • 使用深度作用选择器,可以通过vue-loader提供的新写法 vue-loader 写法如下(先安装,在使用):
<style scoped>
.a >>> .b { /* ... */ }
</style>
  • 某些预处理器(如less)可能无法>>>正确解析。在这些情况下,您可以使用/deep/组合器 - 它是别名,>>>并且完全相同。
<style scoped lang="less">
  .form {
    background-color: #fff;
    /deep/ .list{
      font-size: 18px;
    }
  }
</style>

sass注意要用 /deep/ ,而无法使用 >>> 这个符号。

问题九:微信H5用户设置字号放大或者缩小导致页面布局错误

在 iOS 下,对网页的 body 元素设置 -webkit-text-size-adjust: 100% !important;可以覆盖掉微信的样式。

body {
  -webkit-text-size-adjust: 100% !important;
  text-size-adjust: 100% !important;
  -moz-text-size-adjust: 100% !important;
}

在 Android 下,需要通过WeixinJSBridge对象将网页的字体大小设置为默认大小,并且重写设置字体大小的方法,让用户不能在该网页下设置字体大小。

(function() {
    if (typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function") {
        handleFontSize();
    } else {
        if (document.addEventListener) {
            document.addEventListener("WeixinJSBridgeReady", handleFontSize, false);
        } else if (document.attachEvent) {
            document.attachEvent("WeixinJSBridgeReady", handleFontSize);
            document.attachEvent("onWeixinJSBridgeReady", handleFontSize);
        }
    }
    function handleFontSize() {
        WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize' : 0 });
        WeixinJSBridge.on('menu:setfont', function() {
            WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize' : 0 });
        });
    }
})();

问题十:ios 手机上下滑动页面会产生卡顿,手指离开页面,页面立即停止运动。整体表现就是滑动不流畅,没有滑动惯性。

iOS 5.0 以及之后的版本,滑动有定义有两个值 auto 和 touch,默认值为 auto。 解决办法

  1. 在滚动容器上增加滚动 touch 方法
.wrapper {
  -webkit-overflow-scrolling: touch;
}
  1. 设置 overflow 设置外部 overflow 为 hidden,设置内容元素 overflow 为 auto。内部元素超出 body 即产生滚动,超出的部分 body 隐藏。
body {
  overflow-y: hidden;
}
.wrapper {
  overflow-y: auto;
}

问题十一:小程序云开发的数据调用是有限制的,小程序端每次最多只能调用20条,调用云函数也只能获取100条

解决办法:

  1. 获取总数,获取所想要调用数据的总数,count。
  2. 分段调用对总数进行划分,每次调用100(或者20,都可以)条。
  3. 拼接,将几次获取的数据拼接在一起。

问题十二:移动端弹窗滚动穿透问题

问题描述:弹出层内容滚动时,body内容会跟随滚动。

解决办法:打开弹窗的时候给body添加position:fixed属性,关闭弹窗时恢复

问题十三:上线微信H5页面资源由于浏览器缓存机制未能及时更新

解决办法:

  1. 资源地址后手动添加版本号v1等标识如:index.js?v1
  2. 使用前端项目压缩打包插件如gulp对文件添加后缀

问题十四:H5苹果机手指按住屏幕下拉,屏幕顶部会多出一块白色区域。手指按住屏幕上拉,底部多出一块白色区域。

产生原因:在 iOS 中,手指按住屏幕上下拖动,会触发 touchmove 事件。这个事件触发的对象是整个 webview 容器,容器自然会被拖动,剩下的部分会成空白。

解决办法:

document.body.addEventListener(
  'touchmove',
  function(e) {
    if (e._isScroller) return
    // 阻止默认事件
    e.preventDefault()
  },
  {
    passive: false
  }
)

问题十五:H5的ios系统fixed 失效

问题描述:软键盘唤起后,页面的 fixed 元素将失效,变成了 absolute,所以当页面超过一屏且滚动时,失效的 fixed 元素就会跟随滚动了。不仅限于 type=text 的输入框,凡是软键盘(比如时间日期选择、select 选择等等)被唤起,都会遇到同样地问题

解决办法:不让页面滚动,而是让主体部分自己滚动,主体部分高度设为 100%,overflow:scroll

<body>
  <div class='warper'>
    <div class='main'></div>
  <div>
  <div class="fix-bottom"></div>
</body>
.warper {
  position: absolute;
  width: 100%;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch; 
}
.fix-bottom {
  position: fixed;
  bottom: 0;
  width: 100%;
}

问题十六:JS的相加相减精度丢失问题

例如:0.1+0.2 结果是:0.30000000000000004 解决办法:

  • 使用插js库math.js
  • 把小数先乘以10的幂次方换成整数,计算完结果再除以相应的10的幂次方

问题十七:有时候,希望点击页面其他地方的时候,input保持聚焦状态,但浏览器默认行为是将input失焦

解决方案:在点击事件中,阻止默认行为

function onClick(e) {
 // 你的事件处理代码
 ...
 
 e.preventDefault();
 // iphone有的机型下,没有阻止掉默认行为,主动再聚焦一下
 input.focus();
}

问题十八:H5安卓某些机型聚焦情况下,点击页面其他地方,不主动失焦,某些情况下,主动隐藏键盘之后,input并没有失焦,但是用户触摸页面其他地方时,因为input还处于聚焦状态,会呼起键盘

针对性的解决方案也分两种

  1. 监听用户行为,主动失焦
const autoBlur = (e) => {
  const target = e.target
  const { tagName, className } = target
  // 点击非input区域
  if (tagName.toUpperCase() !== 'INPUT') {
    this.input.blur()
  }
}
document.body.addEventListener('touchstart', autoBlur)
  1. 监听键盘高度变化,主动失焦
const onKeyboardChange = (resize) => {
  // 有时候,比如number变成text,或者系统自动在键盘上面加一些装饰,键盘并没有隐藏,但是触发了resize
  // 测试大部分机型,所有的键盘肯定大于120高度了,所以加一个限制
  if (Math.abs(resize) < 120) return

  const show = resize > 0
  if (!show) {
    this.input.blur()
  }
}

function getClientHeight() {
    return document.documentElement.clientHeight || document.body.clientHeight;
}

// 记录原始高度
let originHeight = getClientHeight()

// 监听键盘变化
window.addEventListener('resize', () => {
  const resizeHeight = getClientHeight()

  const resize = originHeight - resizeHeight
  onKeyboardChange(resize);
  originHeight = resizeHeight;
}, false)

问题十九:移动端web网页是有300ms延迟的,往往会造成按钮点击延迟甚至是点击失效。

解决办法:

  1. fastclick可以解决在手机上点击事件的300ms延迟
  2. zepto.js的touch模块,tap事件也是为了解决在click的延迟问题

问题二十:input聚焦之后滚动到视图内,大部分情况下,系统会帮你将input滚动到视图内,少数情况下,需要我们自己设置

解决办法:

  • 直接调用scrollIntoViewIfNeeded API
  • 计算聚焦前后文档高度的变化差值,然后将body高度相应的调大,并且设置scrollTop,这种情况应该是出现在UIWebview下

问题二十一:iframe 安全隐患问题

有时候前端页面为了显示别人的网站或者一些组件的时候,就用 iframe 来引入进来,比如嵌入一些广告等等。但是有些 iframe 安全性我们无法去评估测试,有时候会携带一些第三方的插件啊,或者嵌入了一下不安全的脚本啊,这些都是值得我们去考虑的。

解决办法:

  1. 使用安全的网站进行嵌入;
  2. 在 iframe 添加一个叫 sandbox 的属性,浏览器会对 iframe 内容进行严格的控制,详细了解可以看看相关的 API 接口文档

问题二十二:第三方依赖安全隐患

现如今的项目开发,很多都喜欢用别人写好的框架,为了方便快捷,很快的就搭建起项目,自己写的代码不到 20%,过多的用第三方依赖或者插件,一方面会影响性能问题,另一方面第三方的依赖或者插件存在很多安全性问题,也会存在这样那样的漏洞,所以使用起来得谨慎。

解决办法:手动去检查那些依赖的安全性问题基本是不可能的,最好是利用一些自动化的工具进行扫描过后再用,比如 NSP(Node Security Platform),Snyk 等等。

问题二十三:v-for和v-if

在Vue中v-for的优先级高于v-if,每一次的v-if判断之前都会先v-for循环。 所以如果如果v-if的判断条件和item无关的话,这样写:

<div v-for="item in items" :key="item.id" v-if="status"></div>

并不好。

我们应当将v-if放到节点的父级来进行判断处理。

<div v-if="status==true">
    <div v-for="item in items" :key="item.id"></div>
</div>

问题二十四:移动端使用overflow-x: scroll实现 横向滚动会有卡顿现象

解决办法:

  • -webkit-overflow-scrolling: touch;
  • transform: translate3d(0, 0, 0);(原理其实就是开启硬件加速)

问题二十五:H5移动端ios下input获取焦点时,页面被放大

设置meta标签

<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

问题二十六:页面中间有可滚动区域A,外层B也可以滚动,用户触发滚动时如何控制滚动区域

A: -webkit-overflow-scrolling : touch;overflow:auto;

B:overflow:hidden;-webkit-overflow-scrolling : auto;

备注:-webkit-overflow-scrolling是非标准的,尽量少用

问题二十七:vue项目切换路由滚动位置问题

在vue的项目开发中,出现了这样的问题。在进入一个路由页面的时候,进行了页面滚动,滚动的位置会被缓存。当离开页面以后,下次再进入一个页面的时候,切换新路由,发现默认进入的位置就是之前滚动的位置。这样,也就造成了多个路由页面滚动会发生互相影响的问题,这也并不是我们所想要的。

针对这样的问题,通过去vue的官网上,Vue Router中的滚动行为上有解决方法,可以使用scrollBehavior。scrollBehavior的作用是返回滚动位置的对象信息,第一个和第二个参数接收to和from路由对象,第三个参数 savedPosition 当且仅当 popstate 导航,通过浏览器的 前进/后退 按钮触发时才可用。这样,对于所有路由导航,简单地让页面滚动到顶部。在router的index.js中,进行写就可以了。完整的代码如下所示:

scrollBehavior (to, from, savedPosition) {
  return { x: 0, y: 0 }
}

问题二十八:vue项目路由变化页面数据不刷新问题

路由变化页面数据不刷新问题场景:比如文章详情数据,依赖路由的params参数获取的(ajax写在created生命周期里面),因为路由懒加载的关系,退出页面再进入另一个文章页面并不会运行created组件生命周期,导致文章数据还是上一个文章的数据。

解决方法:watch监听路由是否变化

watch: {
  '$route' (to, from) { //监听路由是否变化
    if(this.$route.params.articleId){//是否有文章id
      //获取文章数据
    }
  }
 }

问题二十九:移动端ios input输入时白屏

这个问题貌似只有再ios9中才有 解决方法:在input的父元素上添加相对定位就行了style="postion:relative;"

问题三十:移动端单独使用type="number"时,iOS调起的并不是九宫格样式的数字键盘

如果需要调起九宫格的数字键盘需要加上 pattern="[0-9]*" 属性