zoukankan      html  css  js  c++  java
  • 移动端采坑系列

    技术选型

    采用react框架开发移动端的话,一般的架构是Preact + antd-mobile + react-hammerjs + iscroll。

    Preact

    Preact是React的3kb轻量化方案,拥有同样的ES6 API,其拥有以下优点:
    1. 体积小,React V16.0有34.8kb,而Preact官网声称只有3kb
    2. 性能高,是最快的虚拟DOM框架之一
    3. 生态好,官方提供preact-compat,可以无缝使用React生态系统中的各类组件

    迁移指南

    1:安装preactpreact-compatpreact-compat会让你编译后的代码量增加2kb左右,但是它胜在支持npm仓库中的绝大多数的React模块,另外,preact-compat包在Preact 的基础上提供必要的适配,让它表现的跟react和react-dom一样。

    npm i -S preact preact-compat

    2:在webpack配置resolve.alias中,将reactreact-dom的路径指向到preact-compat

    {
      "resolve": {
        "alias": {
          "react": "preact-compat",
          "react-dom": "preact-compat"
        }
      }
    }

    页面终端适配

    flexible方案

    关于移动端的适配布局有很多解决方案,其中手机淘宝使用Flexible的布局方案,在项目中只需要引入lib-flexible库即可。

    <script src="http://g.tbcdn.cn/mtb/lib-flexible/{{version}}/??flexible_css.js,flexible.js"></script>

    flexible的实质是JS动态修改meta的viewport,其主要做了以下几件事:

    1. JS动态修改meta标签
    2. 给html元素添加data-dpr属性,并且动态修改data-dpr的值
    3. 给html元素添加font-size属性,并且动态修改font-size的值

    其核心代码如下:

    (function(){
        var metaEl = document.querySelector('meta[name="viewport"]')
        var dpr = 0
        var scale = 0
        if(!dpr && !scale){
            var isIPhone = window.navigator.appVersion.match(/[iphone|ipad]/gi)
            var devicePixelRatio = window.devicePixelRatio
            if(isIPhone) {
                dpr = devicePixelRatio
            } else {
               dpr = 1
            }
        }
        docEl.setAttribute('data-dpr', dpr)
        if (!metaEl) {
            metaEl = document.createElement('meta');
            metaEl.setAttribute('name', 'viewport');
            metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
            document.documentElement.firstElementChild.appendChild(metaEl);
      } else {
            metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
      }
    })()

    设置动态缩放视口后,在iphone6上,CSS像素的2px显示效果为1px,而在dpr=1的设备上,CSS像素的1px显示为1px,为了让字体在不同手机上显示效果一致,则需要动态修改html的font-size,且页面中使用rem作为单位。

    /** 设置根元素font-size
        * 当设备宽度为375(iPhone6)时,根元素font-size=16px
        */
    var clientWidth = win.innerWidth
        || doc.documentElement.clientWidth
        || doc.body.clientWidth
    if (!clientWidth) return
    var fz = 16 * clientWidth / 375
    document.style.fontSize = fz + 'px'

    在开发页面时,可以借助less或者sass写一个函数,可以将px数值转化为rem数值,这样就能很方便的使用px。

    viewport方案

    vw(view-width),vh(view-height)是CSS3新增的两个单位,表示视区宽度/高度,视区总宽度为100vw,总高度为100vh,随着viewport单位越来越受到总多浏览器的支持,vw就可以直接运用于是适配布局中。

    vw: 1vw等于window.innerWidth的1%
    vh: 1vh等于window.innerHeight的1%
    vmin: vmin的值是当前vw和vh中较小的值
    vmax: vmax的值是当前vw和vh中较大的值

    在该方案中,可以使用postcss-px-to-viewport插件,将css中的px直接转化为vw,vh,该插件参数配置如下:

    "postcss-px-to-viewport": { 
        viewportWidth: 750, 
        viewportHeight: 1334, 
        unitPrecision: 5, 
        viewportUnit: 'vw', 
        selectorBlackList: [], 
        minPixelValue: 1,  
        mediaQuery: false 
    }

    两种方案的对比

    1. vw/vh可以直接使用,不需要借助第三方的依赖
    2. postcss-px-to-viewport插件会将css中的px都转化成vw/vh单位,尤其是第三方的UI框架,这其实是我们所不期望的,对于不希望转换的px样式需额外添加.ignore类名
    3. 将px转换为vw/vh后,在浏览器上调试很不方便
    4. postcss-px-to-viewport并不能将内嵌样式中的px转换成vw/vh
    5. 需要额外处理1px边框的问题

    1px边框问题

    flexible

    flexible下1px边框是最容易处理的,因为flexible动态修改了viewport视口缩放比例,所以直接设置border:solid 1px #ddd;即可,但是flexible对Android机并没有做任何处理,所以在Android机上线条非常粗,且由于选用了vw/vh方案适配页面,所以不得不考虑其他方案处理1px的问题。

    border-image属性

    可以使用postcss-write-svg插件,代码如下:

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

    postcss-write-svg插件会将上面的代码编译成如下代码:

    #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;
    }

    该方案简单易用,比修改图片要简单快捷得多,具体请参考 postcss-write-svg
    在添加postcss-write-svg插件时,如果同时使用了postcss-px-to-viewport插件,一定要将postcss-write-svg插件放置在前面,且在safari浏览器中,border: 1px solid transparent; 属性会看不见线条,需要将transparent去掉。

    after伪类 + scale

    在所需元素上添加:after伪类,并进行缩放,代码如下:

    #example {
        position: relative;
    }
    // 添加1px的下边界
    #example:after {
        content: '',
        position: absolute;
        border-bottom: solid 1px #ddd;
        bottom: 0;
        left:0;
         100%;
        transform: scale(1 , 1 / 2)
    }

    这里的缩放的倍数是所在移动端1/dpr,所以这里也需要借助媒体查询来在不同dpr下缩放不同倍数:

    @media screen and ( -webkit-min-device-pixel-ratio : 2 ),       
        ( min--moz-device-pixel-ratio : 2 ),      
        ( min-resolution: 2dppx ) {
        #example:after {
            transform: scale(1 , 1 / 2)
        }
    }
    @media screen and ( -webkit-min-device-pixel-ratio : 3 ),       
        ( min--moz-device-pixel-ratio : 3 ),      
        ( min-resolution: 3dppx ) {
        #example:after {
            transform: scale(1 , 1 / 3)
        }
    }

    该方案的缺点:

    1. 很难添加四个方向的1px边框,需要嵌套多层元素才能实现
    2. 在添加下边界1px边框线条时,线条并不是在元素最下边,这是由于缩放后,最下边为空白区域,如果将bottom: -1px时,元素如果有overflow: hidden;属性,那么边框线条也就看不见了。

    点击延迟

    ,移动端在派发点击事件时,通常会出现300ms的延迟,这是由于移动端浏览器为了判断用户是否双击导致的,当用户第一次点击后,在接下来一段时间内用户未进行下一次点击,浏览器才会当做单击事件处理。

    解决方法

    禁止缩放

    <meta name="viewport" content="width=device-width user-scalable= 'no'">

    当页面存在需要放大一张图片和一段字体很小的文本时,该方法就不可取。

    指针事件

    指针事件由微软提出,现已经进入W3C规范的候选推荐标准阶段,它的主旨是对所有的输入类型,进行统一的处理,例如:只需要监听一个元素的pointerdown事件,无需分别监听其touchstart和mousedown事件。

    指针事件新增了一个CSS属性touch-action,当在body元素上设置touch-action: none;,彻底禁止了双击缩放,也就解决了点击延迟的问题,不过目前只有IE实现了指针事件。

    指针事件的 polyfill:polyfill是在非IE浏览器中模拟指针事件,不过如果只是为了解决点击延迟的问题,该方案就显得有点过了。

    FastClick

    原理: FastClick是在检测到touchend事件时,通过DOM自定义事件立即触发一个模拟click事件,并把浏览器在300ms后真正触发的click事件阻止掉。

    使用方法:

    <script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
    <script>
        if ('addEventListener' in document) {
            document.addEventListener('DOMContentLoaded', function() {
                FastClick.attach(document.body);
            }, false);
        }
    </script>

    就目前而言,FastClick非常实际地解决了300ms点击延迟的问题。

    滚动问题

    总结移动端技术选型

    目前比较好的方案,推荐使用Preact作为其基础框架,使用CSS3新增的vw/vh单位解决页面适配等问题,通过postcss插件postcss-px-to-viewport自动将css的px单位转换为vw/vh单位,1px边框采用border-image属性,借助postcss-write-svg插件减少对边框图片的操作,再使用react-hammerjs监听操作手势,使用iScroll模拟移动端滑动。

    echarts移动端的bug

    本文是采用下面的方式直接引入echarts,并没有使用echarts-for-react等第三方库。

    import echarts from 'echarts/lib/echarts'
    import 'echarts/lib/chart/line'
    import 'echarts/lib/component/tooltip'

    safari非首屏echarts图表不渲染

    在iphone手机端safari浏览器上,屏幕可见区域的echarts图表正常渲染,滚动区域下面的DOM元素是正常渲染,echarts图表不渲染,但是当手动点击图表区域后,echarts又正常渲染出来。如下图所示:

    图片描述

    解决方法:在echarts图表的父元素及祖先元素上,加上transform: translateZ(0)的样式。具体原因还清楚。

    echarts不能正常初始化

    问题场景:当echarts图表所在组件正常移除后,重新创建组件并初始化echarts,发现echarts容器DOM并没有插入canvas标签,echarts图表没有初始化出来。

    console.log(this.echarts)   // echarts容器存在,且具有宽高
    const echartsObj = echarts.init(this.echarts)
    console.log(echartsObj)    // echarts初始化对象也存在

    解放方法:在组件将要移除时,手动删除echarts初始化的DOM容器。

    componentWillUnmount(){
        this.echarts.parentNode.removeChild(this.echarts)
    }

    其原因是由于react组件在移除后,react组件里面的DOM节点被缓存了下来,当react组件重新创建时,echarts的初始化函数init检测到容器DOM相同,echarts不能在单个容器上初始化多个 ECharts 实例,所以其容器DOM里就不会再插入canvas标签。

    本文转载于:猿2048https://www.mk2048.com/blog/blog.php?id=hbajjkacchj

  • 相关阅读:
    企业微信应用授权
    exec存储过程示例
    jquery判断对象是否存在
    IScroll5要防止重复加载
    transitionEnd不起作用解决方法
    微信接口 output {"errMsg":"translateVoice:fail, the permission value is offline verifying"}
    javascript保留两位小数
    html取消回车刷新提交
    企业微信后台登录
    企业微信开启开发者工具
  • 原文地址:https://www.cnblogs.com/10manongit/p/12774162.html
Copyright © 2011-2022 走看看