移动端适配及PC端适配心得总结体会(二) (可能比较全

前篇☞ 移动端适配及PC端适配心得总结体会(一) (可能比较全javascript

web适配产生的问题

1.浏览器mate指定内核

QQ浏览器 meta元素检测 识别内核 规则介绍:css

识别为chrome内核
  • doctype 为标准
  • meta 标签元素

例子:html

<!DOCTYPE html>
<html>
  <head>
    <!-- 下面3个meta中任选一个,便可正确识别 -->
    <meta name="renderer" content="webkit" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="X-UA-Compatible" content="chrome=1" />
    <title>chrome core</title>
  </head>
  <body>
  meta webkit
  </body>
</html>
复制代码
识别为IE内核
  • doctype 为非标准
  • meta 元素检测

例子:前端

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <!-- 下面3个meta中任选一个,便可正确识别 -->
    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    <meta name="renderer" content="ie-comp" />
    <meta name="renderer" content="ie-stand" />
    <title>ie core</title>
  </head>
  <body>
  meta ie
  </body>
</html>
复制代码

参考:qq浏览器文档vue

2. 1px边框的问题

img

出现的缘由:java

按照iPhone6的尺寸,一张750px宽的设计图,这个750px其实就是iPhone6的设备像素,在测量设计图时量到的1px实际上是1设备像素,而当咱们设置布局视口等于理想视口等于375px,而且因为iPhone6的DPR为2,写css时的1px对应的是2设备像素,因此看起来会粗一点。ios

解决办法git

1.border-image

基于media查询判断不一样的设备像素比给定不一样的border-imagegithub

.border_1px{
          border-bottom: 1px solid #000;
        }
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .border_1px{
                border-bottom: none;
                border-width: 0 0 1px 0;
                border-image: url(../img/1pxline.png) 0 0 2 0 stretch;
            }
        }
复制代码
2. background-image

border-image相似,准备一张符合条件的边框背景图,模拟在背景上。web

.border_1px{
          border-bottom: 1px solid #000;
        }
@media only screen and (-webkit-min-device-pixel-ratio:2{
.border_1px{
        background: url(../img/1pxline.png) repeat-x left bottom;
        background-size: 100% 1px;
            }
        }
复制代码

上面两种都须要单独准备图片,并且圆角不是很好处理,可是能够应对大部分场景。

3.伪类 + transform

基于media查询判断不一样的设备像素比对线条进行缩放:

.border_1px:before{
          content: '';
          position: absolute;
          top: 0;
          height: 1px;
          width: 100%;
          background-color: #000;
          transform-origin: 50% 0%;
        }
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .border_1px:before{
                transform: scaleY(0.5);
            }
        }
        @media only screen and (-webkit-min-device-pixel-ratio:3){
            .border_1px:before{
                transform: scaleY(0.33);
            }
        }
复制代码

这种方式能够知足各类场景,若是须要知足圆角,只须要给伪类也加上border-radius便可。

4.使用svg(插件帮助postcss-write-svg)

上面咱们border-imagebackground-image均可以模拟1px边框,可是使用的都是位图,还须要外部引入。

借助PostCSSpostcss-write-svg咱们能直接使用border-imagebackground-image建立svg1px边框:

@svg border_1px { 
  height: 2px; 
  @rect { 
    fill: var(--color, black); 
    width: 100%; 
    height: 50%; 
    } 
  } 
.example { border: 1px solid transparent; border-image: svg(border_1px param(--color #00b1ff)) 2 2 stretch; }
复制代码

编译后:

.example { border: 1px solid transparent; border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch; }
复制代码

3.适配iPhoneX

咱们须要将顶部和底部合理的摆放在安全区域内,iOS11新增了两个CSS函数env、constant,用于设定安全区域与边界的距离。

函数内部能够是四个常量:

  • safe-area-inset-left:安全区域距离左边边界距离
  • safe-area-inset-right:安全区域距离右边边界距离
  • safe-area-inset-top:安全区域距离顶部边界距离
  • safe-area-inset-bottom:安全区域距离底部边界距离

注意:咱们必须指定viweport-fit后才能使用这两个函数:

<meta name="viewport" content="width=device-width, viewport-fit=cover">
复制代码

constantiOS < 11.2的版本中生效,enviOS >= 11.2的版本中生效,这意味着咱们每每要同时设置他们,将页面限制在安全区域内:

body {
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}
复制代码

当使用底部固定导航栏时,咱们要为他们设置padding值:

{
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}
复制代码

具体细节

aotu.io/notes/2017/…

4.关于横屏

js识别

window.addEventListener("resize", ()=>{
    if (window.orientation === 180 || window.orientation === 0) { 
      // 正常方向或屏幕旋转180度
        console.log('竖屏');
    };
    if (window.orientation === 90 || window.orientation === -90 ){ 
       // 屏幕顺时钟旋转90度或屏幕逆时针旋转90度
        console.log('横屏');
    }  
});
复制代码

css识别

@media screen and (orientation: portrait) {
  /*竖屏...*/
} 
@media screen and (orientation: landscape) {
  /*横屏...*/
}
复制代码

5.图片模糊问题

1 产生缘由

咱们平时使用的图片大多数都属于位图(png、jpg..),位图由一个个像素点构成的,每一个像素都具备特定的位置和颜色值:

img

理论上,位图的每一个像素对应在屏幕上使用一个物理像素来渲染,才能达到最佳的显示效果。

而在dpr > 1的屏幕上,位图的一个像素可能由多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值,因此相同的图片在dpr > 1的屏幕上就会模糊:

img

2 解决方案

为了保证图片质量,咱们应该尽量让一个屏幕像素来渲染一个图片像素,因此,针对不一样DPR的屏幕,咱们须要展现不一样分辨率的图片。

如:在dpr=2的屏幕上展现两倍图(@2x),在dpr=3的屏幕上展现三倍图(@3x)

img

3 media查询

使用media查询判断不一样的设备像素比来显示不一样精度的图片:

.avatar{
            background-image: url(conardLi_1x.png);
        }
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .avatar{
                background-image: url(conardLi_2x.png);
            }
        }
        @media only screen and (-webkit-min-device-pixel-ratio:3){
            .avatar{
                background-image: url(conardLi_3x.png);
            }
        }
复制代码

只适用于背景图

4 image-set

使用image-set

.avatar {
    background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );
}
复制代码

只适用于背景图

5 srcset

使用img标签的srcset属性,浏览器会自动根据像素密度匹配最佳显示图片:

<img src="conardLi_1x.png" srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">
复制代码
6 JavaScript拼接图片url

使用window.devicePixelRatio获取设备像素比,遍历全部图片,替换图片地址:

const dpr = window.devicePixelRatio;
const images =  document.querySelectorAll('img');
images.forEach((img)=>{
  img.src.replace(".", `@${dpr}x.`);
})
复制代码
7 使用svg

SVG的全称是可缩放矢量图,不一样于位图的基于像素,SVG 则是属于对图像的形状描述,因此它本质上是文本文件,体积较小,且无论放大多少倍都不会失真。

除了咱们手动在代码中绘制svg,咱们还能够像使用位图同样使用svg图片:

<img src="conardLi.svg">
<img src="data:image/svg+xml;base64,[data]"> .avatar { background: url(conardLi.svg); } 复制代码

参考:

移动端适配

头条移动端适配

h5相关

什么是h5

H5 技术是一系列移动端 web 前端技术的集合

img

H5 技术自己是用于移动端的 web 网页。因为 App 自己有个 webview 的容器,在容器里能够运行 Web 前端相关代码,由此 H5 和原生 App 结合又衍生出来了 Hybrid App 技术

Hybrid App 技术大体原理

img

移动端响应式布局

解决方案一:rem + pxToRem

原理
  1. 监听屏幕视窗的宽度,经过必定比例换算赋值给htmlfont-size。此时,根字体大小就会随屏幕宽度而变化。
  2. px 转换成 rem, 常规方案有两种,一种是利用sass/less中的自定义函数 pxToRem,写px时,利用pxToRem函数转换成 rem。另一种是直接写px,编译过程利用插件所有转成rem。这样 dom 中元素的大小,就会随屏幕宽度变化而变化了。
实现
  1. 动态更新根字体大小
const MAX_FONT_SIZE = 420
// 定义最大的屏幕宽度
document.addEventListener('DOMContentLoaded', () => {
  const html = document.querySelector('html')
  let fontSize = window.innerWidth / 10
  fontSize = fontSize > MAX_FONT_SIZE ? MAX_FONT_SIZE : fontSize
  html.style.fontSize = fontSize + 'px'
})

复制代码
  1. pxrem
pxToRem 方案一
$rootFontSize: 375 / 10;
// 定义 px 转化为 rem 的函数
@function px2rem ($px) {
    @return $px / $rootFontSize + rem;
}
.demo {
    width: px2rem(100);
    height: px2rem(100);
}
复制代码
pxToRem方案二

vue-cli3 中装 postcss-pxtorem 插件就能够了,其余平台也是大体差很少的思路。

const autoprefixer = require('autoprefixer')
const pxtorem = require('postcss-pxtorem')
module.exports = { 
  // ...
  css: {
    sourceMap: true,
    loaderOptions: {
      postcss: {
        plugins: [
          autoprefixer(),
          pxtorem({
            rootValue: 37.5,
            propList: ['*']
          })
        ]
      }
    }
  }
}
复制代码

继续探索postcss-pxtorem插件源码,查看它实现的原理。

function createPxReplace (rootValue, unitPrecision, minPixelValue) {
    return function (m, $1) {
        if (!$1) return m;
        var pixels = parseFloat($1);
        if (pixels < minPixelValue) return m;
        var fixedVal = toFixed((pixels / rootValue), unitPrecision);
        return (fixedVal === 0) ? '0' : fixedVal + 'rem';
    };
}
复制代码

px变换成 rem 主要是这个函数,固然里面有不少可配置的参数, 核心原理和咱们方案一差很少。方便在于,不须要每次写px都要加上一个函数,代码也清晰不少。

是否是全部元素 px 都要转换成 rem呢?那可不必定哦,border 中的 px 不该该转 rem,涉及到另一个 1px 的问题,上一篇文章详细论述过,避免 pxrem,将 border 中的 px 大写成 PX/Px/pX

解决方案二:vh + vw

原理

vw 相对于视窗宽度的单位,随宽度变化而变化。

实现

与 rem 相似作法,直接使用 postcss-px-to-viewport 插件进行配置, 配置方式也是和 postcss-pxtorem 大同小异。插件的原理也相同

其余解决方案

方案 缺陷
1 百分比 高度没法百分比
2 媒体查询 + metaviewport 不一样设备宽度不一样,缩放比没法彻底肯定
3 flex 仍是没法解决宽度超出问题

上面方案均存在致命缺陷,不推荐使用它完成移动端布局计算。

flexrem 结合使用更佳

vue快速配置移动端模板

参考

开源库解决方案

1.vant 组件库

vant 组件库中,默认采用 px 作计量单位,若是须要使用 rem,直接使用插件完美适配。

对于 vw 方案,vant 也是能够经过插件将 px 转成 vw,对于 vw 可能会存在一些坑点。

2.ant-design-mobile 组件库

ant-design-mobile 组件库仍然使用 px 单位

@hd: 1px; // 基本单位

// 字体尺寸
// ---
@font-size-icontext: 10 * @hd;
@font-size-caption-sm: 12 * @hd;
@font-size-base: 14 * @hd;
@font-size-subhead: 15 * @hd;
@font-size-caption: 16 * @hd;
@font-size-heading: 17 * @hd;

// 圆角
// ---
@radius-xs: 2 * @hd;
@radius-sm: 3 * @hd;
@radius-md: 5 * @hd;
@radius-lg: 7 * @hd;
@radius-circle: 50%;

// 边框尺寸
// ---
@border-width-sm: 1PX;
@border-width-md: 1PX;
@border-width-lg: 2 * @hd;
复制代码

h5遇到的问题汇总

img

1.ios滑动不流畅

表现

上下滑动页面会产生卡顿,手指离开页面,页面当即中止运动。总体表现就是滑动不流畅,没有滑动惯性。

产生缘由

为何 iOS 的 webview 中 滑动不流畅,它是如何定义的?

原来在 iOS 5.0 以及以后的版本,滑动有定义有两个值 autotouch,默认值为 auto

-webkit-overflow-scrolling: touch; /* 当手指从触摸屏上移开,会保持一段时间的滚动 */
-webkit-overflow-scrolling: auto; /* 当手指从触摸屏上移开,滚动会当即中止 */
复制代码

解决方案

1.在滚动容器上增长滚动 touch 方法

-webkit-overflow-scrolling 值设置为 touch

.wrapper {
    -webkit-overflow-scrolling: touch;
}
复制代码

设置滚动条隐藏: .container ::-webkit-scrollbar {display: none;}

可能会致使使用position:fixed; 固定定位的元素,随着页面一块儿滚动

2.设置 overflow

设置外部 overflowhidden,设置内容元素 overflowauto。内部元素超出 body 即产生滚动,超出的部分 body 隐藏。

body {
    overflow-y: hidden;
}
.wrapper {
    overflow-y: auto;
}
复制代码

二者结合使用更佳!


2.iOS 上拉边界下拉出现白色空白

表现

手指按住屏幕下拉,屏幕顶部会多出一块白色区域。手指按住屏幕上拉,底部多出一块白色区域。

产生缘由

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

解决方案
1. 监听事件禁止滑动

移动端触摸事件有三个,分别定义为

1. touchstart :手指放在一个DOM元素上。
2. touchmove :手指拖曳一个DOM元素。
3. touchend :手指从一个DOM元素上移开。
复制代码

显然咱们须要控制的是 touchmove 事件

touchmove 事件的速度是能够实现定义的,取决于硬件性能和其余实现细节

preventDefault 方法,阻止同一触点上全部默认行为,好比滚动。

由此咱们找到解决方案,经过监听 touchmove,让须要滑动的地方滑动,不须要滑动的地方禁止滑动。

值得注意的是咱们要过滤掉具备滚动容器的元素。

实现以下:

document.body.addEventListener('touchmove', function(e) {
    if(e._isScroller) return;
    // 阻止默认事件
    e.preventDefault();
}, {
    passive: false
});
复制代码
2. 滚动妥协填充空白,装饰成其余功能

在不少时候,咱们能够不去解决这个问题,换一直思路。根据场景,咱们能够将下拉做为一个功能性的操做

好比: 下拉后刷新页面


3.页面放大或缩小不肯定性行为

表现

双击或者双指张开手指页面元素,页面会放大或缩小。

产生缘由

HTML 自己会产生放大或缩小的行为,好比在 PC 浏览器上,能够自由控制页面的放大缩小。可是在移动端,咱们是不须要这个行为的。因此,咱们须要禁止该不肯定性行为,来提高用户体验。

原理与解决方案

HTML meta 元标签标准中有个 中 viewport 属性,用来控制页面的缩放,通常用于移动端。以下图 MDN 中介绍

<meta name="viewport" content="width=device-width, initial-scale=1.0">
复制代码

所以咱们能够设置 maximum-scaleminimum-scaleuser-scalable=no 用来避免这个问题

<meta name=viewport
  content="width=device-width, initial-scale=1.0, minimum-scale=1.0 maximum-scale=1.0, user-scalable=no">
复制代码

阻止页面放大(meta不起做用时)

window.addEventListener(
    "touchmove",
    function (event) {
      if (event.scale !== 1) {
        event.preventDefault();
      }
    },
    { passive: false }
  );
复制代码

4.click 点击事件延时与穿透

表现

监听元素 click 事件,点击元素触发时间延迟约 300ms

点击蒙层,蒙层消失后,下层元素点击触发。

产生缘由
为何会产生 click 延时?

iOS 中的 safari,为了实现双击缩放操做,在单击 300ms 以后,若是未进行第二次点击,则执行 click 单击操做。也就是说来判断用户行为是否为双击产生的。可是,在 App 中,不管是否须要双击缩放这种行为,click 单击都会产生 300ms 延迟。

为何会产生 click 点击穿透?

双层元素叠加时,在上层元素上绑定 touch 事件,下层元素绑定 click 事件。因为 click 发生在 touch 以后,点击上层元素,元素消失,下层元素会触发 click 事件,由此产生了点击穿透的效果。

原理与解决方案
解决方案一:使用 touchstart 替换 click

前面已经介绍了,移动设备不只支持点击,还支持几个触摸事件。 那么咱们如今基本思路就是用 touch 事件代替click 事件。

click 替换成 touchstart 不只解决了 click 事件都延时问题,还解决了穿透问题。由于穿透问题是在 touchclick 混用时产生。

在原生中使用

el.addEventListener("touchstart", () => { console.log("ok"); }, false);
复制代码

在 vue 中使用

<button @touchstart="handleTouchstart()">点击</button>
复制代码

开源解决方案中,也是既提供了 click 事件,又提供了touchstart 事件。如 vant 中的 button 组件

img

那么,是否能够将 click 事件所有替换成 touchstart 呢?为何开源框架还会给出 click 事件呢?

咱们想象一种情景,同时须要点击和滑动的场景下。若是将 click 替换成 touchstart 会怎样?

事件触发顺序: touchstart, touchmove, touchend, click

很容易想象,在我须要touchmove滑动时候,优先触发了touchstart的点击事件,是否是已经产生了冲突呢?

因此呢,在具备滚动的状况下,仍是建议使用 click 处理。

在接下来的fastclick开源库中也作了以下处理。

主要目的就是,在使用 touchstart 合成 click 事件时,保证其不在滚动的父元素之下。

解决方案二: 使用 fastclick 库

使用 npm/yarn 安装后使用

import FastClick from 'fastclick';
FastClick.attach(document.body, options);
复制代码

一样,使用fastclick库后,click 延时和穿透问题都没了

按照个人惯例,只要涉及开源库,那么咱们必定要去了解它实现的原理。主要是将现有的原生事件集合封装合成一个兼容性较强的事件集合。

fastclick源码 核心代码不长, 1000 行不到。有兴趣能够了解一下!


5.软键盘将页面顶起来、收起未回落问题

表现

Android 手机中,点击 input 框时,键盘弹出,将页面顶起来,致使页面样式错乱。

移开焦点时,键盘收起,键盘区域空白,未回落。

产生缘由

咱们在app 布局中会有个固定的底部。安卓一些版本中,输入弹窗出来,会将解压 absolutefixed 定位的元素。致使可视区域变小,布局错乱。

原理与解决方案
方案1:

监听input失焦事件,失焦即回落

/iphone|ipod|ipad/i.test(navigator.appVersion) &&
  document.addEventListener(
    "blur",
    (event) => {
      // 当页面没出现滚动条时才执行,由于有滚动条时,不会出现这问题
      // input textarea 标签才执行,由于 a 等标签也会触发 blur 事件
      if (
        document.documentElement.offsetHeight <= document.documentElement.clientHeight &&
        ["input", "textarea"].includes(event.target.localName)
      ) {
        document.body.scrollIntoView(); // 回顶部
      }
    },
    true
  );
复制代码
方案2:

软键盘将页面顶起来的解决方案,主要是经过监听页面高度变化,强制恢复成弹出前的高度。

//这里应该改为style,height属性

// 记录原有的视口高度
const originalHeight = document.body.clientHeight || document.documentElement.clientHeight;
window.onresize = function(){
  var resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
  if(resizeHeight < originalHeight ){
    // 恢复内容区域高度
    // const container = document.getElementById("container")
    // 例如 container.style.height = originalHeight;
  }
}
复制代码

键盘不能回落问题出如今 iOS 12+ 和 wechat 6.7.4+ 中,而在微信 H5 开发中是比较常见的 Bug。

兼容原理,1.判断版本类型 2.更改滚动的可视区域

const isWechat = window.navigator.userAgent.match(/MicroMessenger\/([\d\.]+)/i);
if (!isWechat) return;
const wechatVersion = wechatInfo[1];
const version = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
 
 // 若是设备类型为iOS 12+ 和wechat 6.7.4+,恢复成原来的视口
if (+wechatVersion.replace(/\./g, '') >= 674 && +version[1] >= 12) {
  window.scrollTo(0, Math.max(document.body.clientHeight, document.documentElement.clientHeight));
}
复制代码
复制代码

window.scrollTo(x-coord, y-coord),其中window.scrollTo(0, clientHeight)恢复成原来的视口


6.iPhone X系列安全区域适配问题

表现

头部刘海两侧区域或者底部区域,出现刘海遮挡文字,或者呈现黑底或白底空白区域。

产生缘由

iPhone X 以及它以上的系列,都采用刘海屏设计全面屏手势。头部、底部、侧边都须要作特殊处理。才能适配 iPhone X 的特殊状况。

解决方案

设置安全区域,填充危险区域,危险区域不作操做和内容展现。

危险区域指头部不规则区域,底部横条区域,左右触发区域。

img

具体操做为:viewport-fit ,meta 标签设置为 cover,获取全部区域填充。 判断设备是否属于 iPhone X,给头部底部增长适配层

viewport-fit 有 3 个值分别为:

  • auto:此值不影响初始布局视图端口,而且整个web页面都是可查看的。
  • contain: 视图端口按比例缩放,以适合显示内嵌的最大矩形。
  • cover:视图端口被缩放以填充设备显示。强烈建议使用 safe area inset 变量,以确保重要内容不会出如今显示以外。

设置 viewport-fit 为 cover

<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, viewport-fit=cover">
复制代码

增长适配层

使用 safe area inset 变量

/* 适配 iPhone X 顶部填充*/
@supports (top: env(safe-area-inset-top)){
  body,
  .header{
      padding-top: constant(safe-area-inset-top, 40px);
      padding-top: env(safe-area-inset-top, 40px);
      padding-top: var(safe-area-inset-top, 40px);
  }
}
/* 判断iPhoneX 将 footer 的 padding-bottom 填充到最底部 */
@supports (bottom: env(safe-area-inset-bottom)){
    body,
    .footer{
        padding-bottom: constant(safe-area-inset-bottom, 20px);
        padding-bottom: env(safe-area-inset-bottom, 20px);
        padding-top: var(safe-area-inset-bottom, 20px);
    }
}
复制代码

safe-area-inset-top, safe-area-inset-right, safe-area-inset-bottom, safe-area-inset-left safe-area-inset-*由四个定义了视口边缘内矩形的 top, right, bottomleft 的环境变量组成,这样能够安全地放入内容,而不会有被非矩形的显示切断的风险。

对于矩形视口,例如普通的笔记本电脑显示器,其值等于零。

对于非矩形显示器(如圆形表盘,iPhoneX 屏幕,在用户代理设置的四个值造成的矩形内,全部内容都可见。

其中 env() 用法为 env( <custom-ident> , <declaration-value>? ),第一个参数为自定义的区域,第二个为备用值。

其中 var() 用法为 var(<custom-property-name> , <declaration-value>?),做用是在 env() 不生效的状况下,给出一个备用值。

constant()css 2017-2018 年为草稿阶段,是否已被标准化未知。而其余iOS 浏览器版本中是否有此函数未知,做为兼容处理而添加进去。

详情请查看文章末尾的参考资料。


7.页面生成为图片和二维码问题

表现

在工做中有须要将页面生成图片或者二维码的需求。可能咱们第一想到的,交给后端来生成更简单。可是这样咱们须要把页面代码所有传给后端,网络性能消耗太大。

解决方案
生成二维码

使用 QRCode 生成二维码

import QRCode from 'qrcode';
// 使用 async 生成图片
const options = {};
const url = window.location.href;
async url => {
  try {
    console.log(await QRCode.toDataURL(url, options))
  } catch (err) {
    console.error(err);
  }
}
复制代码

await QRCode.toDataURL(url, options) 赋值给 图片 url 便可

生成图片

主要是使用 htmlToCanvas 生成 canvas 画布

import html2canvas from 'html2canvas';
html2canvas(document.body).then(function(canvas) {
    document.body.appendChild(canvas);
});
复制代码

可是不仅仅在此处就完了,因为是 canvas 的缘由。移动端生成出来的图片比较模糊。

咱们使用一个新的 canvas 方法多倍生成,放入一倍容器里面,达到更加清晰的效果,经过超连接下载图片 下载文件简单实现,更完整的实现方式以后更新

const scaleSize = 2;
const newCanvas = document.createElement("canvas");
const target = document.querySelector('div');
const width = parseInt(window.getComputedStyle(target).width);
const height = parseInt(window.getComputedStyle(target).height);
newCanvas.width = width * scaleSize;
newCanvas.height = widthh * scaleSize;
newCanvas.style.width = width + "px";
newCanvas.style.height =width + "px";
const context = newCanvas.getContext("2d");
context.scale(scaleSize, scaleSize);
html2canvas(document.querySelector('.demo'), { canvas: newCanvas }).then(function(canvas) {
  // 简单的经过超连接设置下载功能
  document.querySelector(".btn").setAttribute('href', canvas.toDataURL());
}
复制代码

根据须要设置 scaleSize 大小


8.微信公众号分享问题

表现

在微信公众号 H5 开发中,页面内部点击分享按钮调用 SDK,方法不生效。

解决方案

添加一层蒙层,作分享引导。

由于页面内部点击分享按钮没法直接调用,而分享功能须要点击右上角更多来操做。


9.H5 调用 SDK 相关解决方案

原生与 H5 的通讯

解决方案

使用 DSBridge 同时支持 iOS 与 Android

文档见参考资料

SDK小组 提供方法
  1. 注册方法 bridge.register
bridge.register('enterApp', function() {
  broadcast.emit('ENTER_APP')
})
复制代码
  1. 回调方法 bridge.call
export const getSDKVersion = () => bridge.call('BLT.getSDKVersion')
复制代码
事件监听与触发法
const broadcast = {
  on: function(name, fn, pluralable) {
    this._on(name, fn, pluralable, false)
  },
  once: function(name, fn, pluralable) {
    this._on(name, fn, pluralable, true)
  },
  _on: function(name, fn, pluralable, once) {
    let eventData = broadcast.data
    let fnObj = { fn: fn, once: once }
    if (pluralable && Object.prototype.hasOwnProperty.call(eventData, 'name')) {
      eventData[name].push(fnObj)
    } else {
      eventData[name] = [fnObj]
    }
    return this
  },
  emit: function(name, data, thisArg) {
    let fn, fnList, i, len
    thisArg = thisArg || null
    fnList = broadcast.data[name] || []
    for (i = 0, len = fnList.length; i < len; i++) {
      fn = fnList[i].fn
      fn.apply(thisArg, [data, name])
      if (fnList[i].once) {
        fnList.splice(i, 1)
        i--
        len--
      }
    }
    return this
  },
  data: {}
}
export default broadcast

复制代码
踩坑注意

方法调用前,必定要判断 SDK 是否提供该方法 若是 Android 提供该方法,iOS 上调用就会出现一个方法调用失败等弹窗。 怎么解决呢?

提供一个判断是否 Android、iOS。根据设备进行判断

export const hasNativeMethod = (name) =>
  return bridge.hasNativeMethod('BYJ.' + name)
}

export const getSDKVersion = function() {
  if (hasNativeMethod('getSDKVersion')) {
    bridge.call('BYJ.getSDKVersion')
  }
}
复制代码

同一功能须要iOS,Android方法名相同,这样更好处理哦


10.H5 调试相关方案策略

表现

调试代码通常就是为了查看数据定位 bug。分为两种场景,一种是开发和测试时调试,一种是生产环境上调试。

为何有生产环境上调试呢?有些时候测试环境上无法复现这个 bug,测试环境和生产环境不一致,此时就须要紧急生产调试。

在 PC 端开发时,咱们能够直接掉出控制台,使用浏览器提供的工具操做devtools或者查看日志。可是在 App 内部咱们怎么作呢?

原理与解决方案
1. vconsole 控制台插件

使用方法也很简单

import Vconsole from 'vconsole'
new Vconsole()
复制代码

img

有兴趣看看它实现的基本原理,咱们关注的点应该在 vsconsole 如何打印出咱们全部 log 的 腾讯开源vconsole

上述方法仅用于开发和测试。生产环境中不容许出现,因此,使用时须要对环境进行判断。

import Vconsole from 'vconsole'
if (process.env.NODE_ENV !== 'production') {
    new Vconsole()
}
复制代码
2. 代理 + spy-debugger

操做稍微有点麻烦,不过我会详细写出,大体分为 4 个步骤

  1. 安装插件(全局安装)
sudo npm install spy-debugger -g
复制代码
  1. 手机与电脑置于同一 wifi 下,手机设置代理

设置手机的 HTTP 代理,代理 IP 地址设置为 PC 的 IP 地址,端口为spy-debugger的启动端口

spy-debugger 默认端口:9888

Android :设置 - WLAN - 长按选中网络 - 修改网络 - 高级 - 代理设置 - 手动

IOS :设置 - Wi-Fi - 选中网络, 点击感叹号, HTTP 代理手动

  1. 手机打开浏览器或者 app 中 H5 页面
  2. 打开桌面日志网站进行调试,点击 npm 控制台监听地址。查看抓包和 H5 页面结构

这种方式能够调试生成环境的页面,不须要修改代码,能够应付大多数调试需求

参考:小锁君少

12 移动端100vh的问题汇总

问题描述:

在滚屏手机端要求全屏滚动(使用swiper),由于移动端高度不定,因此采用了100vh布局,在模拟器里很是和谐,结果在发如今移动端的 Chrome 和 微信/safari 浏览器中,由于浏览器栏和一些导航栏 标题栏 致使不同的呈现

呈现1: swiper插件的高设置为innerHeight ,致使微信浏览器 在滚动的时候 会出现断层

呈现2: 100vh 其实超出了屏幕高度,致使一些内容被挡住

出现的缘由:

最好避免100vh,而是依赖javascript来设置高度,以得到完整的视口体验。

核心问题是移动浏览器(Chrome和Safari)有一个“帮助”功能,地址栏有时可见,有时隐藏,改变了视口的可见大小,

这些浏览器没有将100vh的高度调整为视口高度变化时屏幕的可见部分,而是将100vh设置为隐藏地址栏的浏览器高度。结果是,当地址栏可见时,屏幕的底部部分将被切断,从而破坏了100vh的初衷。

呈现3:

在咱们使用iOS Safari的浏览器时,上划页面的时候工具栏和地址栏会隐藏起来。

当页面向上滑动的时候,工具栏会隐藏,此时的window.innerHeight会发生变化,可是css中的vh是不会变的,100vh = 隐藏工具栏时的window.innerHeight。(在IphoneXR中,显示工具栏的时候window.innerHeight = 719px,工具栏隐藏时候window.innerHeight = 833px)

若是你的页面的原本设计就是能够滑动的长页面是没有什么影响的,可是若是你的页面准备作成只有一个视口大小的单页面应用,那就会出现问题。 由于就算你设置width: 100%,页面仍是会出现滚动,而且向上滚动达到必定阈值的时候,工具栏会隐藏,布局彻底混乱。而且且在横屏和竖屏下,工具栏的高度也是不一样的。

此外若是咱们为元素设置absolute或fixed属性,想要将它们固定在底部位置的话,Safari也不能如咱们所愿。如上文所讲,实际上你的页面有一部分被底部的工具栏挡住了,因此咱们设置在底部位置的元素也会被工具栏挡住。

解决的一些办法:

1.对于由于nav 被挡住的东西使用calc动态算高度(没尝试)
min-height: calc(100vh - 0.9rem) //0.9rem是挡住的高度
复制代码
2.使用js 动态设置高度

将高度设置为window.innerHeight将正确地将高度设置为窗口的可见部分。若是地址栏是可见的,那么window.innerHeight是全屏的高度。若是地址栏是隐藏的,那么window.innerHeight将是屏幕可见部分的高度,

3.在vue项目里使用

${app}/src/app.vue

mounted() {
    // First we get the viewport height and we multiple it by 1% to get a value for a vh unit
    let vh = window.innerHeight * 0.01
    // Then we set the value in the --vh custom property to the root of the document
    document.documentElement.style.setProperty('--vh', `${vh}px`)
    // We listen to the resize event
    window.addEventListener('resize', () => {
      // We execute the same script as before
      let vh = window.innerHeight * 0.01
      document.documentElement.style.setProperty('--vh', `${vh}px`)
    })
  },
复制代码

${app}/views/foo.vue

<style lang="scss" scoped>
.container {
  height: 100vh; /* 在以前设置为100vh,能够兼容某些不支持自定义属性的浏览器。*/
  height: calc(var(--vh, 1vh) * 100 - 46px);
</style>
复制代码
4.使用 height:100%

给body下的每一层级都设置高度100%

* {
  padding: 0;
  margin: 0;
  border: 0;
  outline: 0;
  box-sizing: border-box;
}
html {
  width: 100%;
  height: 100%;
  border: 5px solid red;
  overflow: hidden;
  //没试验 明天实验一下
  height:-webkit-fill-available;
  
}
复制代码
5.在苹果浏览器里,动态解决高度,使用css

工具栏高度75px,calc的计算属性在低版本的safari中并不兼容 可使用方法4

给body下的每一层级都设置高度100%

calc(100vh - 75px)
复制代码
6.关于父容器没有高度, 子height: 100%,而致使的内容塌陷问题

场景描述:

<head>
        <style>
            .app { width: 100%;height: 100%;display: flex;flex: 1;flex-direction: column; }
            header { width: 100%;height: 75px; }
            main { flex: 1; }
            iframe { width: 100%;height: 100%;}
        </style>
    </head>
    <body>
        <div class="app">
            <header></header>
            <main>
                <iframe v-if="true"></iframe>           
              <div v-else></div>
            </main>
        </div>

复制代码

具备百分比高度的元素的父级必须具备已定义的高度,而且必须具备height属性.不然,具备百分比高度的元素必须默认为height:auto(内容高度)

chrome会自动弥补这个问题,可是Safari会认为这是一个缺陷,iframe的父级没有给定的高度,直接致使iframe高度塌陷(随内容高度)。因此在拉伸浏览器窗口的时候,iframe内容实际高度产生了变化,致使该问题的出现。

解决方案:

  1. 给定父级指定高度
main { flex: 1;height: calc(100% - 75px); }
height: calc(100vh - calc(100vh - 100%))
复制代码
  1. 直接给iframe子级指定高度
iframe { width: 100%; height: calc(100vh - 75px); }
复制代码

这里不用100% - 75px,是由于100%依赖于父级指定高度,而vh是直接已浏览器但是窗口高度为基准的。

7.使用-webkit-fill-available

(safari没用)

的CSS

body {
  min-height: 100vh;
  /* mobile viewport bug fix */
  min-height: -webkit-fill-available;
}
html {
  height: -webkit-fill-available;
}
body {
  display: flex; 
  flex-direction: column;
  margin: 0;
  min-height: 100vh;
}

main {
  flex: 1;
}
复制代码
8.完美解决办法
<template>
  <div class="module"> <div class="module__item">20%</div> <div class="module__item">40%</div> <div class="module__item">60%</div> <div class="module__item">80%</div> <div class="module__item">100%</div> </div>
</template>

<script> export default { mounted() { // First we get the viewport height and we multiple it by 1% to get a value for a vh unit const vh = window.innerHeight * 0.01 // Then we set the value in the --vh custom property to the root of the document document.documentElement.style.setProperty('--vh', `${vh}px`) // We listen to the resize event window.addEventListener('resize', () => { // We execute the same script as before const vh = window.innerHeight * 0.01 document.documentElement.style.setProperty('--vh', `${vh}px`) }) } } </script>

<style> body { background-color: #333; } .module { height: 100vh; /* Use vh as a fallback for browsers that do not support Custom Properties */ height: calc(var(--vh, 1vh) * 100); margin: 0 auto; max-width: 30%; } .module__item { align-items: center; display: flex; height: 20%; justify-content: center; } .module__item:nth-child(odd) { background-color: #fff; color: #f73859; } .module__item:nth-child(even) { background-color: #f73859; color: #f1d08a; } </style>

复制代码

13 针对safari浏览器的问题汇总

1.input标签在safari苹果浏览器中的高度默认

在作浏览器兼容的时候,发现input标签在safari苹果浏览器中的高度永远都是默认的,这时候解决的办法是加上line-height属性就能够设置;

若是Safari浏览器的input高度设置无论用,必定要设置line-height,而后去除iOS固有UI样式:-webkit-appearance: none;

14 flex布局的问题(扩展)

在容器中的每一个单元块被称之为 flex item

主轴空间:main size

交叉轴空间:cross size

1.flex容器

div{ display: flex | inline-flex; }
复制代码

当时设置 flex 布局以后,子元素的 float、clear、vertical-align 的属性将会失效,

flex:1 所在div 高度自适应屏幕的剩余高度

2.给flex容器设置的6个属性

1. flex-direction: 决定主轴的方向(即项目的排列方向)
.container {
    flex-direction: row(默认) | row-reverse | column | column-reverse;
}
复制代码

2. flex-wrap: 决定容器内项目是否可换行

.container {
    flex-wrap: nowrap(默认) | wrap(项目主轴总尺寸超出容器时换行,第一行在上方) | wrap-reverse;(反向)
}
复制代码
  1. flex-flow: flex-direction 和 flex-wrap 的简写形式 (没有用)

4. justify-content:定义了项目在主轴的对齐方式。

.container {
    justify-content: 
      flex-start(默认值 左对齐)
    | flex-end(右对齐)
    | center 
    | space-between(两端对齐,只有两行的时候,分别在首尾)
    | space-around(每一个项目两侧的间隔相等,项目之间的间隔比项目与边缘的间隔大一倍)
  	| space-evenly 子元素会均匀分布在容器内,同时额外的空间将会被子元素的两侧所分享
}
复制代码

记住 justify-content 只会在 盒子有剩余空间能够分配时 发挥做用。

5. align-items: 定义了项目在交叉轴上的对齐方式

.container {
    align-items:
      stretch(默认值):若是项目未设置高度或者设为 auto,将占满整个容器的高度。
      flex-start (顶点对齐)
      flex-end	(尾点对齐)
      center
      baseline (项目的第一行文字的基线对齐)
}
复制代码

6. align-content: 定义了多根轴线的对齐方式,若是项目只有一根轴线,那么该属性将不起做用

.container {
    align-content: flex-start | flex-end | center | space-between | space-around | stretch(默认值);
}
复制代码

当你 flex-wrap 设置为 nowrap 的时候,容器仅存在一根轴线,由于项目不会换行,就不会产生多条轴线。

当你 flex-wrap 设置为 wrap 的时候,容器可能会出现多条轴线,这时候你就须要去设置多条轴线之间的对齐方式了。

对齐方式同justify-content 两端/中间/等距对齐等

3.Flex 项目属性

1. order: 定义项目在容器中的排列顺序,数值越小,排列越靠前,默认值为 0

设置了 order,使之可以排到最前面。

2. flex-basis: 定义了在分配多余空间以前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间

默认值:auto,即项目原本的大小, 这时候 item 的宽高取决于 width 或 height 的值。

当主轴为水平方向的时候,当设置了 flex-basis,项目的宽度设置值会失效,flex-basis 须要跟 flex-grow 和 flex-shrink 配合使用才能发挥效果。

  • 当 flex-basis 值为 0 % 时,是把该项目视为零尺寸的,故即便声明该尺寸为 140px,也并无什么用。
  • 当 flex-basis 值为 auto 时,则跟根据尺寸的设定值(假如为 100px),则这 100px 不会归入剩余空间。

3. flex-grow: 定义项目的放大比例

默认值为 0,即若是存在剩余空间,也不放大

当全部的项目都以 flex-basis 的值进行排列后,仍有剩余空间,那么这时候 flex-grow 就会发挥做用了。

若是全部项目的 flex-grow 属性都为 1,则它们将等分剩余空间。(若是有的话)

若是一个项目的 flex-grow 属性为 2,其余项目都为 1,则前者占据的剩余空间将比其余项多一倍。

固然若是当全部项目以 flex-basis 的值排列完后发现空间不够了,且 flex-wrap:nowrap 时,此时 flex-grow 则不起做用了,这时候就须要接下来的这个属性。

4. flex-shrink: 定义了项目的缩小比例

默认值: 1,即若是空间不足,该项目将缩小,负值对该属性无效。

若是一个项目的 flex-shrink 属性为 0,其余项目都为 1,则空间不足时,前者不缩小。

5. flex: flex-grow, flex-shrink 和 flex-basis的简写

.item{
    flex:  <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
    0 1 auto(默认)
} 
复制代码

分别以下:

1.当 flex 取值为一个非负数字,则该数字为 flex-grow 值,flex-shrink 取 1,flex-basis 取 0%

.item {flex: 1;} (等分剩余空间)
//等价于
.item {
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: 0%;
}
复制代码

2.当 flex 取值为 0 时,对应的三个值分别为 0 1 0%

.item {flex: 0;}(存在剩余空间,也不放大)
//等价于
.item {
    flex-grow: 0;
    flex-shrink: 1;
    flex-basis: 0%;
}
复制代码

最后求各位大佬多多关注~~~ 我会一直努力更新的!! 作一个合格的搬运工(笑
这篇文章参考了不少其余大佬的优秀文章~ 有些参考连接可能没有指出来 若有遗漏 必定补上❤

(*^▽^*)下个篇章打算分享一下最近面试出现的高频考点和答案 ~!

相关文章
相关标签/搜索