采用react框架开发移动端的话,通常的架构是Preact + antd-mobile + react-hammerjs + iscroll。css
Preact是React的3kb轻量化方案,拥有一样的ES6 API,其拥有如下优势: 1. 体积小,React V16.0有34.8kb,而Preact官网声称只有3kb 2. 性能高,是最快的虚拟DOM框架之一 3. 生态好,官方提供preact-compat,能够无缝使用React生态系统中的各种组件
1:安装preact
,preact-compat
,preact-compat
会让你编译后的代码量增长2kb左右,可是它胜在支持npm仓库中的绝大多数的React模块,另外,preact-compat
包在Preact
的基础上提供必要的适配,让它表现的跟react和react-dom同样。html
npm i -S preact preact-compat
2:在webpack配置resolve.alias中,将react
和react-dom
的路径指向到preact-compat
。react
{ "resolve": { "alias": { "react": "preact-compat", "react-dom": "preact-compat" } } }
关于移动端的适配布局有不少解决方案,其中手机淘宝使用Flexible
的布局方案,在项目中只须要引入lib-flexible
库便可。webpack
<script src="http://g.tbcdn.cn/mtb/lib-flexible/{{version}}/??flexible_css.js,flexible.js"></script>
flexible的实质是JS动态修改meta的viewport,其主要作了如下几件事:git
其核心代码以下:github
(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做为单位。web
/** 设置根元素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。npm
vw(view-width),vh(view-height)是CSS3新增的两个单位,表示视区宽度/高度,视区总宽度为100vw,总高度为100vh,随着viewport单位愈来愈受到总多浏览器的支持,vw就能够直接运用因而适配布局中。canvas
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 }
postcss-px-to-viewport
插件会将css中的px都转化成vw/vh单位,尤为是第三方的UI框架,这实际上是咱们所不指望的,对于不但愿转换的px样式需额外添加.ignore
类名postcss-px-to-viewport
并不能将内嵌样式中的px转换成vw/vhflexible下1px边框是最容易处理的,由于flexible动态修改了viewport视口缩放比例,因此直接设置border:solid 1px #ddd;
便可,可是flexible对Android机并无作任何处理,因此在Android机上线条很是粗,且因为选用了vw/vh方案适配页面,因此不得不考虑其余方案处理1px的问题。
可使用postcss-write-svg
插件,代码以下:
@svg square { height: 2px; @rect { fill: var(--color, black); width: 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伪类,并进行缩放,代码以下:
#example { position: relative; } // 添加1px的下边界 #example:after { content: '', position: absolute; border-bottom: solid 1px #ddd; bottom: 0; left:0; width: 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) } }
该方案的缺点:
,移动端在派发点击事件时,一般会出现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是在检测到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,并无使用echarts-for-react等第三方库。
import echarts from 'echarts/lib/echarts' import 'echarts/lib/chart/line' import 'echarts/lib/component/tooltip'
在iphone手机端safari浏览器上,屏幕可见区域的echarts图表正常渲染,滚动区域下面的DOM元素是正常渲染,echarts图表不渲染,可是当手动点击图表区域后,echarts又正常渲染出来。以下图所示:
解决方法:在echarts图表的父元素及祖先元素上,加上transform: translateZ(0)
的样式。具体缘由还清楚。
问题场景:当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标签。