前言
我们在开发前端项目的时候时常会出现怪异的问题和奇怪的需求,这里我记录的踩坑笔记希望对大家有用。
问题一:H5项目ios竖屏拍照上传,图片被旋转问题
通过以下几个步骤来解决:
- 通过第三方插件exif-js获取到图片的方向
- new一个FileReader对象,加载读取上传的图片
- 在fileReader的onload函数中,得到的图片文件用一个Image对象接收
- 在image的onload函数中,利用步骤1中获取到的方向orientation,通过canvas旋转校正,重新绘制一张新图
- 将绘制的新图转成Blob对象,添加到FormData对象中,然后进行校正后的上传操作
问题二:H5项目iPhoneX底部适配
通过以下几个步骤来解决:
- viewport meta 标签增加属性viewport-fit=cover
<meta name="viewport" content="width=device-width, viewport-fit=cover, xxxx">
- body元素增加样式
body {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
- 如有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)对齐,而不是紧贴容器下边缘
解放办法:
- 将图像定义成img:{display:block;}
- 给父级设置固定高度,然后overflow:hidden
- 设置 font-size:0;
- 设置 img 的 vertical-align: bottom;
- 设置 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。 解决办法
- 在滚动容器上增加滚动 touch 方法
.wrapper {
-webkit-overflow-scrolling: touch;
}
- 设置 overflow 设置外部 overflow 为 hidden,设置内容元素 overflow 为 auto。内部元素超出 body 即产生滚动,超出的部分 body 隐藏。
body {
overflow-y: hidden;
}
.wrapper {
overflow-y: auto;
}
问题十一:小程序云开发的数据调用是有限制的,小程序端每次最多只能调用20条,调用云函数也只能获取100条
解决办法:
- 获取总数,获取所想要调用数据的总数,count。
- 分段调用对总数进行划分,每次调用100(或者20,都可以)条。
- 拼接,将几次获取的数据拼接在一起。
问题十二:移动端弹窗滚动穿透问题
问题描述:弹出层内容滚动时,body内容会跟随滚动。
解决办法:打开弹窗的时候给body添加position:fixed属性,关闭弹窗时恢复
问题十三:上线微信H5页面资源由于浏览器缓存机制未能及时更新
解决办法:
- 资源地址后手动添加版本号v1等标识如:index.js?v1
- 使用前端项目压缩打包插件如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还处于聚焦状态,会呼起键盘
针对性的解决方案也分两种
- 监听用户行为,主动失焦
const autoBlur = (e) => {
const target = e.target
const { tagName, className } = target
// 点击非input区域
if (tagName.toUpperCase() !== 'INPUT') {
this.input.blur()
}
}
document.body.addEventListener('touchstart', autoBlur)
- 监听键盘高度变化,主动失焦
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延迟的,往往会造成按钮点击延迟甚至是点击失效。
解决办法:
- fastclick可以解决在手机上点击事件的300ms延迟
- zepto.js的touch模块,tap事件也是为了解决在click的延迟问题
问题二十:input聚焦之后滚动到视图内,大部分情况下,系统会帮你将input滚动到视图内,少数情况下,需要我们自己设置
解决办法:
- 直接调用scrollIntoViewIfNeeded API
- 计算聚焦前后文档高度的变化差值,然后将body高度相应的调大,并且设置scrollTop,这种情况应该是出现在UIWebview下
问题二十一:iframe 安全隐患问题
有时候前端页面为了显示别人的网站或者一些组件的时候,就用 iframe 来引入进来,比如嵌入一些广告等等。但是有些 iframe 安全性我们无法去评估测试,有时候会携带一些第三方的插件啊,或者嵌入了一下不安全的脚本啊,这些都是值得我们去考虑的。
解决办法:
- 使用安全的网站进行嵌入;
- 在 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]*" 属性