前篇☞ 移动端适配及PC端适配心得总结体会(一) (可能比较全javascript
QQ浏览器 meta元素检测 识别内核 规则介绍:css
例子: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>
复制代码
例子:前端
<!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
出现的缘由:java
按照iPhone6的尺寸,一张750px宽的设计图,这个750px其实就是iPhone6的设备像素,在测量设计图时量到的1px实际上是1设备像素,而当咱们设置布局视口等于理想视口等于375px,而且因为iPhone6的DPR为2,写css时的1px对应的是2设备像素,因此看起来会粗一点。ios
解决办法git
基于media
查询判断不一样的设备像素比给定不一样的border-image
:github
.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;
}
}
复制代码
和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;
}
}
复制代码
上面两种都须要单独准备图片,并且圆角不是很好处理,可是能够应对大部分场景。
基于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
便可。
postcss-write-svg
)上面咱们border-image
和background-image
均可以模拟1px
边框,可是使用的都是位图,还须要外部引入。
借助PostCSS
的postcss-write-svg
咱们能直接使用border-image
和background-image
建立svg
的1px
边框:
@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; }
复制代码
咱们须要将顶部和底部合理的摆放在安全区域内,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">
复制代码
constant
在iOS < 11.2
的版本中生效,env
在iOS >= 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);
}
复制代码
具体细节
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) {
/*横屏...*/
}
复制代码
咱们平时使用的图片大多数都属于位图(png、jpg..
),位图由一个个像素点构成的,每一个像素都具备特定的位置和颜色值:
理论上,位图的每一个像素对应在屏幕上使用一个物理像素来渲染,才能达到最佳的显示效果。
而在dpr > 1
的屏幕上,位图的一个像素可能由多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值,因此相同的图片在dpr > 1
的屏幕上就会模糊:
为了保证图片质量,咱们应该尽量让一个屏幕像素来渲染一个图片像素,因此,针对不一样DPR
的屏幕,咱们须要展现不一样分辨率的图片。
如:在dpr=2
的屏幕上展现两倍图(@2x)
,在dpr=3
的屏幕上展现三倍图(@3x)
。
使用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);
}
}
复制代码
只适用于背景图
使用image-set
:
.avatar {
background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );
}
复制代码
只适用于背景图
使用img
标签的srcset
属性,浏览器会自动根据像素密度匹配最佳显示图片:
<img src="conardLi_1x.png" srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">
复制代码
使用window.devicePixelRatio
获取设备像素比,遍历全部图片,替换图片地址:
const dpr = window.devicePixelRatio;
const images = document.querySelectorAll('img');
images.forEach((img)=>{
img.src.replace(".", `@${dpr}x.`);
})
复制代码
SVG
的全称是可缩放矢量图,不一样于位图的基于像素,SVG
则是属于对图像的形状描述,因此它本质上是文本文件,体积较小,且无论放大多少倍都不会失真。
除了咱们手动在代码中绘制svg
,咱们还能够像使用位图同样使用svg
图片:
<img src="conardLi.svg">
<img src="data:image/svg+xml;base64,[data]"> .avatar { background: url(conardLi.svg); } 复制代码
参考:
H5 技术是一系列移动端 web 前端技术的集合
H5 技术自己是用于移动端的 web 网页。因为 App 自己有个 webview
的容器,在容器里能够运行 Web 前端相关代码,由此 H5 和原生 App 结合又衍生出来了 Hybrid App 技术。
html
的font-size
。此时,根字体大小就会随屏幕宽度而变化。px
转换成 rem
, 常规方案有两种,一种是利用sass
/less
中的自定义函数 pxToRem
,写px
时,利用pxToRem
函数转换成 rem
。另一种是直接写px
,编译过程利用插件所有转成rem
。这样 dom
中元素的大小,就会随屏幕宽度变化而变化了。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'
})
复制代码
px
转 rem
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
的问题,上一篇文章详细论述过,避免px
转rem
,将border
中的px
大写成PX/Px/pX
vw
相对于视窗宽度的单位,随宽度变化而变化。
与 rem 相似作法,直接使用 postcss-px-to-viewport 插件进行配置, 配置方式也是和 postcss-pxtorem 大同小异。插件的原理也相同
方案 | 缺陷 | |
---|---|---|
1 | 百分比 | 高度没法百分比 |
2 | 媒体查询 + meta 中 viewport |
不一样设备宽度不一样,缩放比没法彻底肯定 |
3 | flex |
仍是没法解决宽度超出问题 |
上面方案均存在致命缺陷,不推荐使用它完成移动端布局计算。
flex
与rem
结合使用更佳
vant 组件库中,默认采用 px
作计量单位,若是须要使用 rem
,直接使用插件完美适配。
对于 vw
方案,vant 也是能够经过插件将 px
转成 vw
,对于 vw
可能会存在一些坑点。
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;
复制代码
上下滑动页面会产生卡顿,手指离开页面,页面当即中止运动。总体表现就是滑动不流畅,没有滑动惯性。
为何 iOS 的 webview 中 滑动不流畅,它是如何定义的?
原来在 iOS 5.0 以及以后的版本,滑动有定义有两个值 auto
和 touch
,默认值为 auto
。
-webkit-overflow-scrolling: touch; /* 当手指从触摸屏上移开,会保持一段时间的滚动 */
-webkit-overflow-scrolling: auto; /* 当手指从触摸屏上移开,滚动会当即中止 */
复制代码
将-webkit-overflow-scrolling
值设置为 touch
.wrapper {
-webkit-overflow-scrolling: touch;
}
复制代码
设置滚动条隐藏:
.container ::-webkit-scrollbar {display: none;}
可能会致使使用position:fixed;
固定定位的元素,随着页面一块儿滚动
设置外部 overflow
为 hidden
,设置内容元素 overflow
为 auto
。内部元素超出 body 即产生滚动,超出的部分 body 隐藏。
body {
overflow-y: hidden;
}
.wrapper {
overflow-y: auto;
}
复制代码
二者结合使用更佳!
手指按住屏幕下拉,屏幕顶部会多出一块白色区域。手指按住屏幕上拉,底部多出一块白色区域。
在 iOS 中,手指按住屏幕上下拖动,会触发 touchmove
事件。这个事件触发的对象是整个 webview
容器,容器天然会被拖动,剩下的部分会成空白。
移动端触摸事件有三个,分别定义为
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
});
复制代码
在不少时候,咱们能够不去解决这个问题,换一直思路。根据场景,咱们能够将下拉做为一个功能性的操做。
好比: 下拉后刷新页面
双击或者双指张开手指页面元素,页面会放大或缩小。
HTML 自己会产生放大或缩小的行为,好比在 PC 浏览器上,能够自由控制页面的放大缩小。可是在移动端,咱们是不须要这个行为的。因此,咱们须要禁止该不肯定性行为,来提高用户体验。
HTML meta
元标签标准中有个 中 viewport
属性,用来控制页面的缩放,通常用于移动端。以下图 MDN 中介绍
<meta name="viewport" content="width=device-width, initial-scale=1.0">
复制代码
所以咱们能够设置 maximum-scale
、minimum-scale
与 user-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 }
);
复制代码
监听元素 click
事件,点击元素触发时间延迟约 300ms
。
点击蒙层,蒙层消失后,下层元素点击触发。
iOS 中的 safari,为了实现双击缩放操做,在单击 300ms 以后,若是未进行第二次点击,则执行 click
单击操做。也就是说来判断用户行为是否为双击产生的。可是,在 App 中,不管是否须要双击缩放这种行为,click
单击都会产生 300ms 延迟。
双层元素叠加时,在上层元素上绑定 touch
事件,下层元素绑定 click
事件。因为 click
发生在 touch
以后,点击上层元素,元素消失,下层元素会触发 click
事件,由此产生了点击穿透的效果。
前面已经介绍了,移动设备不只支持点击,还支持几个触摸事件。 那么咱们如今基本思路就是用 touch
事件代替click
事件。
将 click
替换成 touchstart
不只解决了 click
事件都延时问题,还解决了穿透问题。由于穿透问题是在 touch
和 click
混用时产生。
在原生中使用
el.addEventListener("touchstart", () => { console.log("ok"); }, false);
复制代码
在 vue 中使用
<button @touchstart="handleTouchstart()">点击</button>
复制代码
开源解决方案中,也是既提供了 click
事件,又提供了touchstart
事件。如 vant 中的 button
组件
那么,是否能够将 click
事件所有替换成 touchstart
呢?为何开源框架还会给出 click
事件呢?
咱们想象一种情景,同时须要点击和滑动的场景下。若是将 click
替换成 touchstart
会怎样?
事件触发顺序:
touchstart
,touchmove
,touchend
,click
。
很容易想象,在我须要touchmove
滑动时候,优先触发了touchstart
的点击事件,是否是已经产生了冲突呢?
因此呢,在具备滚动的状况下,仍是建议使用 click
处理。
在接下来的fastclick
开源库中也作了以下处理。
主要目的就是,在使用 touchstart
合成 click
事件时,保证其不在滚动的父元素之下。
使用 npm/yarn
安装后使用
import FastClick from 'fastclick';
FastClick.attach(document.body, options);
复制代码
一样,使用fastclick
库后,click
延时和穿透问题都没了
按照个人惯例,只要涉及开源库,那么咱们必定要去了解它实现的原理。主要是将现有的原生事件集合封装合成一个兼容性较强的事件集合。
fastclick源码 核心代码不长, 1000 行不到。有兴趣能够了解一下!
Android 手机中,点击 input
框时,键盘弹出,将页面顶起来,致使页面样式错乱。
移开焦点时,键盘收起,键盘区域空白,未回落。
咱们在app 布局中会有个固定的底部。安卓一些版本中,输入弹窗出来,会将解压 absolute
和 fixed
定位的元素。致使可视区域变小,布局错乱。
监听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
);
复制代码
软键盘将页面顶起来的解决方案,主要是经过监听页面高度变化,强制恢复成弹出前的高度。
//这里应该改为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)
恢复成原来的视口
头部刘海两侧区域或者底部区域,出现刘海遮挡文字,或者呈现黑底或白底空白区域。
iPhone X 以及它以上的系列,都采用刘海屏设计和全面屏手势。头部、底部、侧边都须要作特殊处理。才能适配 iPhone X 的特殊状况。
设置安全区域,填充危险区域,危险区域不作操做和内容展现。
危险区域指头部不规则区域,底部横条区域,左右触发区域。
具体操做为:viewport-fit
,meta
标签设置为 cover
,获取全部区域填充。 判断设备是否属于 iPhone X,给头部底部增长适配层
viewport-fit
有 3 个值分别为:
auto
:此值不影响初始布局视图端口,而且整个web页面都是可查看的。contain
: 视图端口按比例缩放,以适合显示内嵌的最大矩形。cover
:视图端口被缩放以填充设备显示。强烈建议使用safe area inset
变量,以确保重要内容不会出如今显示以外。
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
,bottom
和left
的环境变量组成,这样能够安全地放入内容,而不会有被非矩形的显示切断的风险。对于矩形视口,例如普通的笔记本电脑显示器,其值等于零。
对于非矩形显示器(如圆形表盘,
iPhoneX
屏幕,在用户代理设置的四个值造成的矩形内,全部内容都可见。
其中 env()
用法为 env( <custom-ident>
, <declaration-value>
? ),第一个参数为自定义的区域,第二个为备用值。
其中 var()
用法为 var(<custom-property-name> , <declaration-value>?)
,做用是在 env()
不生效的状况下,给出一个备用值。
constant()
被 css
2017-2018 年为草稿阶段,是否已被标准化未知。而其余iOS 浏览器版本中是否有此函数未知,做为兼容处理而添加进去。
详情请查看文章末尾的参考资料。
在工做中有须要将页面生成图片或者二维码的需求。可能咱们第一想到的,交给后端来生成更简单。可是这样咱们须要把页面代码所有传给后端,网络性能消耗太大。
使用 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
大小
在微信公众号 H5 开发中,页面内部点击分享按钮调用 SDK,方法不生效。
添加一层蒙层,作分享引导。
由于页面内部点击分享按钮没法直接调用,而分享功能须要点击右上角更多来操做。
原生与 H5 的通讯
使用 DSBridge
同时支持 iOS 与 Android
文档见参考资料
bridge.register
bridge.register('enterApp', function() {
broadcast.emit('ENTER_APP')
})
复制代码
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方法名相同,这样更好处理哦
调试代码通常就是为了查看数据和定位 bug。分为两种场景,一种是开发和测试时调试,一种是生产环境上调试。
为何有生产环境上调试呢?有些时候测试环境上无法复现这个 bug,测试环境和生产环境不一致,此时就须要紧急生产调试。
在 PC 端开发时,咱们能够直接掉出控制台,使用浏览器提供的工具操做devtools或者查看日志。可是在 App 内部咱们怎么作呢?
vconsole
控制台插件使用方法也很简单
import Vconsole from 'vconsole'
new Vconsole()
复制代码
有兴趣看看它实现的基本原理,咱们关注的点应该在 vsconsole 如何打印出咱们全部 log 的 腾讯开源vconsole
上述方法仅用于开发和测试。生产环境中不容许出现,因此,使用时须要对环境进行判断。
import Vconsole from 'vconsole'
if (process.env.NODE_ENV !== 'production') {
new Vconsole()
}
复制代码
操做稍微有点麻烦,不过我会详细写出,大体分为 4 个步骤
sudo npm install spy-debugger -g
复制代码
设置手机的 HTTP 代理,代理 IP 地址设置为 PC 的 IP 地址,端口为spy-debugger
的启动端口
spy-debugger 默认端口:9888
Android :设置 - WLAN - 长按选中网络 - 修改网络 - 高级 - 代理设置 - 手动
IOS :设置 - Wi-Fi - 选中网络, 点击感叹号, HTTP 代理手动
这种方式能够调试生成环境的页面,不须要修改代码,能够应付大多数调试需求
问题描述:
在滚屏手机端要求全屏滚动(使用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也不能如咱们所愿。如上文所讲,实际上你的页面有一部分被底部的工具栏挡住了,因此咱们设置在底部位置的元素也会被工具栏挡住。
解决的一些办法:
min-height: calc(100vh - 0.9rem) //0.9rem是挡住的高度
复制代码
将高度设置为window.innerHeight
将正确地将高度设置为窗口的可见部分。若是地址栏是可见的,那么window.innerHeight
是全屏的高度。若是地址栏是隐藏的,那么window.innerHeight
将是屏幕可见部分的高度,
${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>
复制代码
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;
}
复制代码
工具栏高度75px,calc的计算属性在低版本的safari中并不兼容 可使用方法4
给body下的每一层级都设置高度100%
calc(100vh - 75px)
复制代码
场景描述:
<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内容实际高度产生了变化,致使该问题的出现。
解决方案:
main { flex: 1;height: calc(100% - 75px); }
height: calc(100vh - calc(100vh - 100%))
复制代码
iframe { width: 100%; height: calc(100vh - 75px); }
复制代码
这里不用100% - 75px,是由于100%依赖于父级指定高度,而vh是直接已浏览器但是窗口高度为基准的。
(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;
}
复制代码
<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>
复制代码
在作浏览器兼容的时候,发现input标签在safari苹果浏览器中的高度永远都是默认的,这时候解决的办法是加上line-height属性就能够设置;
若是Safari浏览器的input高度设置无论用,必定要设置line-height,而后去除iOS固有UI样式:-webkit-appearance: none;
在容器中的每一个单元块被称之为 flex item
主轴空间:main size
交叉轴空间:cross size
div{ display: flex | inline-flex; }
复制代码
当时设置 flex 布局以后,子元素的 float、clear、vertical-align 的属性将会失效,
flex:1 所在div 高度自适应屏幕的剩余高度
.container {
flex-direction: row(默认) | row-reverse | column | column-reverse;
}
复制代码
2. flex-wrap: 决定容器内项目是否可换行
.container {
flex-wrap: nowrap(默认) | wrap(项目主轴总尺寸超出容器时换行,第一行在上方) | wrap-reverse;(反向)
}
复制代码
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 两端/中间/等距对齐等
1. order: 定义项目在容器中的排列顺序,数值越小,排列越靠前,默认值为 0
设置了 order,使之可以排到最前面。
2. flex-basis: 定义了在分配多余空间以前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间
默认值:auto,即项目原本的大小, 这时候 item 的宽高取决于 width 或 height 的值。
当主轴为水平方向的时候,当设置了 flex-basis,项目的宽度设置值会失效,flex-basis 须要跟 flex-grow 和 flex-shrink 配合使用才能发挥效果。
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%;
}
复制代码
最后求各位大佬多多关注
~~~ 我会一直努力更新的!! 作一个合格的搬运工(笑
这篇文章参考了不少其余大佬的优秀文章~ 有些参考连接可能没有指出来 若有遗漏 必定补上❤
(*^▽^*)
下个篇章打算分享一下最近面试出现的高频考点和答案 ~!