採用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標籤。