Next框架與主流工具的整合(二)—— 完善與優化
前言:18年12月24日專案成功上線了,在經歷了兩週的線上bug、UI以及程式碼優化後,解決了不少問題,於是再完善與優化一下這個 專案 。
- 佈局優化
- 高清配置
- antd-mobile 自定義配置
- antd-mobile Toast元件封裝
佈局優化
佈局優化在這篇文章完成了 → [移動端優雅佈局實踐](),最後我使用的方案是—— absolute脫離文件流 (好處是設定容器的 height: 100%
,可以直接繼承html視窗高度)。
高清配置
問題發現
開始專案之前沒有對dpr(device pixel radio)與縮放做過多的瞭解,在專案開發的時候就將它們都直接寫死為1了。到後來UI驗收的時候發現並沒有實現UI設計師預期的細線效果。我在解決這個問題的時候才去認真看了一下dpr的介紹。 這篇詳解dpr的文章 寫得還不錯。
從概念來說,dpr就是裝置的物理畫素與裝置獨立畫素(也就是css邏輯畫素,以下就稱為css邏輯畫素)的比率。
比如:iPhone 6的解析度是750*1334, window.screen.width
(css邏輯畫素)為375,因此
dpr = 750 /375 = 2
再比如:iPhone X的解析度是1125*2436, window.screen.width
(css邏輯畫素)也是375,因此
dpr = 1125 /375 = 3
那麼dpr有什麼用呢?
在這之前先提一下我們移動端必備的一個 meta
標籤:
<meta name="viewport" content="width=device-width,maximum-scale=1,minimum-scale=1,user-scalable=no" />
device-width在html中也同樣被解讀為理想(基準)視口的寬度,即320px,375px,414px,這裡的px就是指css畫素,通常也被稱為邏輯畫素;那我們可以認為html中的css畫素的顯示尺寸應該和NA中的pt、dp的顯示尺寸相等。
通過這個meta標籤,我們可以實現 initial-scale=1
初始縮放100%, 就可以達到 1px的css邏輯畫素 = 眼睛在裝置上看起來的1px
,換句話說 body { width: 375px; }
可以在iPhone 6上充滿豎屏的整個寬度 。
那麼問題就來了,如果我們要給一個盒子加上一個1px的細線:
border-bottom: 1px solid red;
。那麼在iPhone 6上真的是1px嗎?
iPhone 6真機截圖(寬度為702px):
可以看出高度明顯不止1畫素。這就是由於dpr造成的,因為iPhone 6的dpr為2,且縮放比例為100%,1px的css渲染出來就是2px物理畫素。
這就是我們UI粑粑和產品們不滿意的地方。
那接下來如何去解決這個問題呢?
解決方案
根據裝置的dpr來動態計算縮放比例,以及根節點的 font-size
。
// rem.js (function(doc, win) { var docEl = doc.documentElement, dpr = Math.min(win.devicePixelRatio, 3); dpr = window.top === window.self ? dpr : 1; //被iframe引用時,禁止縮放 var scale = 1 / dpr, resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize'; docEl.dataset.dpr = dpr; var metaEl = doc.createElement('meta'); metaEl.name = 'viewport'; metaEl.content = 'initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no,viewport-fit=cover'; docEl.firstElementChild.appendChild(metaEl); var recalc = function() { var width = docEl.clientWidth; // 大於1280按1280來算 if (width / dpr > 1280) { width = 1280 * dpr; } // px : rem = 100 : 1 docEl.style.fontSize = 100 * (width / 375) + 'px'; }; recalc(); if (!doc.addEventListener) return; win.addEventListener(resizeEvt, recalc, false); })(document, window);
如果有顯示富文字元素,則需要處理富文字元素的樣式
移動端為了適配不同的機型,使用 rem
為單位是一個不錯的選擇,而且我們也一直在用它。
這裡除了根據dpr來計算 initial-scale
,還調整了根節點的 font-size
,以至於在縮放的時候能夠還原到視窗大小(因為要縮放,所以要相應的增加 rem
的基數)。
這樣我們上面寫的 border-bottom: 1px solid red;
在這個方案顯示出來就是這樣的:
哇咔咔,可以看出明顯變細了,這才是我們UI粑粑們想要的O(∩_∩)O~~
但是這樣的設定在結合 ant-design-mobile
的時候,發現 ant-design-mobile
的元件都被縮小了。原來是,它的元素都是以px為單位,而我們縮放前沒有對它的最小單位乘以相應的基數。 那麼,我們需要給它配置一個基數 !
antd-mobile自定義配置
查文件發現 ant-design-mobile
提供了 主題配置 ,而且它提供了一個 @hd
的變數做為長度基本單位,它的預設值是 1px
。
我們只需要把 @hd
設定為 0.01rem
就可以解決問題。
這個 主題配置 的文件是以webpack專案來做的例子。那如何在 next.js
專案中完成自定義配置呢?
我在 next.js的examples 中沒有找到我所需要的example,不過找到了兩個相關的例子:一個是 with-antd-mobile ,一個是 with-ant-design-less 。第二個是 ant-design
的自定義主題配置,那應該就可以仿照這個example去增加 with-antd-mobile 的自定義主題配置。
這裡提一下,這個 with-antd-mobile 在我寫 Next框架與主流工具的整合 之後更新了 next.config.js
的配置,這裡也改成了最新的配置。
- 安裝解析 Less 與 normalize.css 的包
npm i @zeit/next-less @zeit/next-css less less-vars-to-js -S
- 修改
.babelrc
配置
{ "presets": ["next/babel"], "plugins": [ [ "import", { "libraryName": "antd-mobile", "style": true } ] ] }
- 修改
next.config.js
配置
/* eslint-disable */ const withCSS = require('@zeit/next-css'); const withSass = require('@zeit/next-sass'); const withLess = require('@zeit/next-less'); const lessToJS = require('less-vars-to-js'); const fs = require('fs'); const path = require('path'); // Where your antd-custom.less file lives const themeVariables = lessToJS(fs.readFileSync(path.resolve(__dirname, './antd-custom.less'), 'utf8')); // fix: prevents error when .less files are required by node if (typeof require !== 'undefined') { require.extensions['.less'] = file => {}; require.extensions['.css'] = file => {}; } module.exports = withCSS( withLess( withSass({ lessLoaderOptions: { javascriptEnabled: true, modifyVars: themeVariables } }) ) );
- 在專案下新建Less變數檔案
antd-custom.less
@hd: 0.01rem;
重啟專案,就大功告成了。
antd-mobile Toast元件封裝
antd-mobile
的 Toast.info()
元件在顯示的時候不能點選背景就消失,與原生的Toast有些差異,為了體驗,這裡再做了一層封裝,在點選背景的時候隱藏Toast。
// utils/toast.js static info = (content, duration, onClose, mask) => { Toast.info(content, duration, onClose, mask); const toastElement = document.getElementsByClassName('am-toast-mask')[0]; toastElement && toastElement.addEventListener('click', () => { Toast.hide(); onClose && onClose(); }); };
antd-mobile
的loading圖在Android上有些怪異,這裡也自定義了Loading:
static loading = (content, duration, onClose, mask) => { Toast.info( <div> <svg className="rotate360-800" width="0.26rem" height="0.26rem" viewBox="0 0 26 26"> <title>載入</title> <desc>Created with Sketch.</desc> <g id="首頁" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd"> <g transform="translate(-185.000000, -519.000000)" fill="#FFFFFF" id="載入"> <g transform="translate(185.000000, 519.000000)"> <g id="分組" transform="translate(0.625011, 0.625011)"> <path d="M12.3750144,0.0259531552 C5.53983848,0.0259531552 0,5.56600648 0,12.4009676 C0,19.2359286 5.53983848,24.775982 12.3750144,24.775982 C19.2099755,24.775982 24.7500288,19.2359286 24.7500288,12.4009676 C24.7500288,5.56579163 19.2099755,0.0259531552 12.3750144,0.0259531552 Z M12.3750144,22.028385 C7.05736759,22.028385 2.74781179,17.7185714 2.74781179,12.4009676 C2.74781179,7.08332074 7.05741056,2.7735501 12.3750144,2.7735501 C17.6926612,2.7735501 22.0026467,7.08336371 22.0026467,12.4009676 C22.0026467,17.7186144 17.6926182,22.028385 12.3750144,22.028385 Z" id="形狀" fillOpacity="0.2" fillRule="nonzero" /> <path d="M12.3749972,0.0259402646 L12.3750144,2.77353721 C17.6926612,2.77353721 22.0026467,7.08335082 22.0026467,12.4009547 L24.7500116,12.4009547 C24.7500116,5.56577874 19.2099583,0.0259402646 12.3749972,0.0259402646 Z" id="路徑" /> </g> </g> </g> </g> </svg> <div style={{ fontSize: '0.12rem' }}>{content}</div> </div>, duration, onClose, mask ); };
寫在最後
一個專案需要在不斷的優化與完善中才能變得更好。搭建專案是對一個專案負責人很大的考驗,如果在專案設計的初期有許多的問題沒有考慮到,就很有可能導致優化的時候需要耗費很大的精力。
總之,不要逃避困難與問題,這些都是成長路上不可或缺的。