當你陪著家人嗑著瓜子,和家人一起看著春晚,順便拿著手淘參與春晚抽獎互動的時候,杭州還有兩百多程式設計師還奮戰在一線當中。現在年也過完了,獎也抽了,紅包也拿了。也該好好回來工作的時候了。這次很榮幸,自己能參與手淘過年專案(紅包開光和春晚互動專案)的專案中,雖然僅僅參與其中的部分工作,但事後感覺有些東西還是應該總結總結的,為之後的專案做準備。那麼簡單的來總結一下,我自己在參與專案中用到的一些前端技術。
這些技術其實也並不是什麼鮮為人知的技術棧,因為這些技術點已經出現很久了,只不過大家習慣了自己的開發模式,加上專案時間緊,怕嘗試新的東西。事實上我自己也是如此,害怕使用這些技術點,給專案帶來其他的風險(本來專案時間就很緊),慶幸的是,接下來了到的一些東西,經住了專案的考驗,雖然當中踩過一些坑,但總算是無驚無險。
過年專案
手淘過年專案,事實上分為兩個,其中一個叫紅包開光,另一個是春晚抽獎的互動專案。
上面兩張圖分別是紅包開光和春晚互動的主介面視覺圖。如果你參與過手淘過年互動的活動中,這兩個介面應該對您來說並不會太陌生。
經過團隊同學一起討論,這次兩個專案都基於Vue來開發,Vue只是一個JavaScript庫而以,選擇他並不沒有太多的主要原因,而是想讓團隊在今後的專案開發的時候,JavaScript庫能趨於統一,從而慢慢在專案中有所沉澱與積累。基於這個原因,我在其中主要做的事情,在這個腳手架中(也就是Vue-cli的基礎)新增了以下三個部分:
- PostCSS外掛
vw
適配方案- iPhone X 適配
PostCSS外掛
在互動腳手架中,目前已配置的PostCSS外掛主要有:
- postcss-import
- postcss-url
- postcss-aspect-ratio-mini
- postcss-cssnext
- autoprefixer
- postcss-px-to-viewport
- postcss-write-svg
- cssnano
- postcss-viewport-units
PostCSS外掛的配置
Webpack專案的.postcssrc.js
最終的PostCSS外掛的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
module.exports = { "plugins": { "postcss-import": {}, "postcss-url": {}, "postcss-aspect-ratio-mini": {}, "postcss-write-svg": { utf8: false }, "postcss-cssnext": {}, "postcss-px-to-viewport": { viewportWidth: 750, viewportHeight: 1334, unitPrecision: 3, viewportUnit: 'vw', selectorBlackList: ['.ignore', '.hairlines'], minPixelValue: 1, mediaQuery: false }, "postcss-viewport-units":{}, "cssnano": { preset: "advanced", autoprefixer: false, "postcss-zindex": false } } } |
對於這些PostCSS外掛所起的作用和怎麼配置,在其對應的GitHub上都有詳細的描述。這裡簡要的描述一下,為什麼在我們的專案中會採用這些PostCSS的外掛:
postcss-import
和postcss-url
兩個主要是用於處理引入的檔案和資源路徑的處理以及工作模式。如果你的專案也使用的是Vue,並且配置了vue-loader
,並且配置了相關的引數,那就就具有類似的功能。
autoprefixer
主要用來處理瀏覽器的私有字首,這個已經是大家經常使用的一個PostCSS外掛了。這裡需要提出的是,如果你的專案中使用了postcss-next
和cssnano
,那麼autoprefixer
外掛可以不引入,而且在postcss-next
和cssnano
兩者中選擇其一關閉autoprefixer
,因為這兩個外掛都整合了autoprefixer
外掛的特性。
postcss-cssnext
其實就是cssnext
。該外掛可以讓我們使用CSS未來的特性,其會對這些特性做相關的相容性處理。其包含的特性主要有:
有關於
cssnext
的每個特性的操作文件,可以點選這裡瀏覽。
cssnano
主要用來壓縮和清理CSS程式碼。在Webpack中,cssnano
和css-loader
捆綁在一起,所以不需要自己載入它。不過你也可以使用postcss-loader
顯式的使用cssnano
。有關於cssnano
的詳細文件,可以點選這裡獲取。
注:由於
cssnano
的preset
配置使用的是advanced
,所以需要安裝npm install cssnano-preset-advanced --save-dev
。另外cssnext
和cssnano
都具有autoprefixer
的外掛,因此在cssnano
中將autoprefixer
設定為false
。
postcss-write-svg
外掛主要用來處理移動端1px
的解決方案。該外掛主要使用的是border-image
和background
配合SVG繪製的向量圖來做1px
的相關處理。後續將會專門花一節的內容來介紹postcss-write-svg
或者說怎麼能更好的使用SVG來處理移動端1px
。
postcss-aspect-ratio-mini
主要用來處理元素容器寬高比。在專案當中很多地方會使用img
、object
或者video
,那麼這個外掛能更好的幫助我們完美處理寬高比的縮放。在實際使用的時候,具有一個預設的結構:
1 2 3 |
<div aspectratio> <div aspectratio-content></div> </div> |
在實際使用的時候,你可以把自定義屬性aspectratio
和aspectratio-content
換成相應的類名,比如:
1 2 3 |
<div class="aspectratio"> <div class="aspectratio-content"></div> </div> |
我個人比較喜歡用自定義屬性,它和類名所起的作用是同等的。結構定義之後,需要在你的樣式檔案中新增一個統一的寬度比預設屬性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[aspectratio] { position: relative; } [aspectratio]::before { content: ''; display: block; width: 1px; margin-left: -1px; height: 0; } [aspectratio-content] { position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; } |
如果我們想要做一個188:246
(188
是容器寬度,246
是容器高度)這樣的比例容器,只需要這樣使用:
1 2 3 4 |
[w-188-246] { aspect-ratio: '188:246'; } |
有一點需要特別注意:aspect-ratio
屬性不能和其他屬性寫在一起,否則編譯出來的屬性只會留下aspect-ratio
的值,比如:
1 |
<div aspectratio="" w-188-246="" class="color"></div> |
編譯前的CSS如下:
1 2 3 4 5 6 |
[w-188-246] { width: 188px; background-color: red; aspect-ratio: '188:246'; } |
編譯之後:
1 2 3 4 |
[w-188-246]:before { padding-top: 130.85106382978725%; } |
主要是因為在外掛中做了相應的處理,不在每次呼叫aspect-ratio
時,生成前面指定的預設樣式程式碼,這樣程式碼沒那麼冗餘。所以在使用的時候,需要把width
和background-color
分開來寫:
1 2 3 4 5 6 7 8 |
[w-188-246] { width: 188px; background-color: red; } [w-188-246] { aspect-ratio: '188:246'; } |
這個時候,編譯出來的CSS就正常了:
1 2 3 4 5 6 7 8 |
[w-188-246] { width: 25.067vw; background-color: red; } [w-188-246]:before { padding-top: 130.85106382978725%; } |
這個現象也算是一個天坑吧。而這個坑是該外掛自己帶來的,上面的處理方式只是治標而不能治本。所以在使用該外掛的時候,需要特別注意這個細節。
目前採用PostCSS外掛只是一個過渡階段,在將來我們可以直接在CSS中使用aspect-ratio
屬性來實現長寬比。當然,如果你對cssnext
熟悉的話,可以給其新增這樣的一個PR,將CSS原生的aspect-ratio
屬性新增到cssnext
特性當中,這樣只要你使用postcss-next
就可以忽略這個外掛了。
剩下的postcss-px-to-viewport
和postcss-viewport-units
兩個PostCSS外掛主要是用於vw
適配方案,算是這次專案中必不可少的PostCSS外掛。其中,postcss-px-to-viewport
外掛主要用來把px
單位轉換為vw
、vh
、vmin
或者vmax
這樣的視窗單位,也是vw
適配方案的核心外掛之一。
在配置中需要配置相關的幾個關鍵引數:
1 2 3 4 5 6 7 8 9 10 |
"postcss-px-to-viewport": { viewportWidth: 750, // 視窗的寬度,對應的是我們設計稿的寬度,一般是750 viewportHeight: 1334, // 視窗的高度,根據750裝置的寬度來指定,一般指定1334,也可以不配置 unitPrecision: 3, // 指定`px`轉換為視窗單位值的小數位數(很多時候無法整除) viewportUnit: 'vw', // 指定需要轉換成的視窗單位,建議使用vw selectorBlackList: ['.ignore', '.hairlines'], // 指定不轉換為視窗單位的類,可以自定義,可以無限新增,建議定義一至兩個通用的類名 minPixelValue: 1, // 小於或等於`1px`不轉換為視窗單位,你也可以設定為你想要的值 mediaQuery: false // 允許在媒體查詢中轉換`px` } |
目前出視覺設計稿,我們都是使用750px
寬度的,那麼100vw = 750px
,即1vw = 7.5px
。那麼我們可以根據設計圖上的px
值直接轉換成對應的vw
值。在實際擼碼過程,不需要進行任何的計算,直接在程式碼中寫px
,比如:
1 2 3 4 5 6 7 8 9 10 11 |
.test { border: .5px solid black; border-bottom-width: 4px; font-size: 14px; line-height: 20px; position: relative; } [w-188-246] { width: 188px; } |
編譯出來的CSS:
1 2 3 4 5 6 7 8 9 10 11 |
.test { border: .5px solid #000; border-bottom-width: .533vw; font-size: 1.867vw; line-height: 2.667vw; position: relative; } [w-188-246] { width: 25.067vw; } |
在不想要把px
轉換為vw
的時候,首先在對應的元素(html
)中新增配置中指定的類名ignore
或hairlines
(hairlines
一般用於設定border-width:0.5px
的元素中):
1 |
<div class="box haspx"></div> |
寫CSS的時候:
1 2 3 4 5 6 7 8 9 |
.ignore { margin: 10px; background-color: red; } .box { width: 180px; height: 300px; } |
編譯出來的CSS:
1 2 3 4 5 6 7 8 9 |
.box { width: 24vw; height: 40vw; } .ignore { margin: 10px; /*.box元素中帶有.ignore,在這個類名寫的`px`不會被轉換*/ background-color: red; } |
上面解決了px
到vw
的轉換計算。
由於瀏覽器對vw
還具有一定的相容性,其在Android 4.4之下和iOS8以下的版本都存有一定的問題。為了讓vw
、vh
、vmin
和vmax
這些viewport
單位能更好的使用。其相容方案就是使用viewport
的polyfill:Viewport Units Buggyfill。
而在採用Viewport Units Buggyfill的時候,需要手動給使用viewport
單位的樣式中新增其對應的Hack程式碼,比如:
1 2 3 4 5 6 7 |
.box { top: 2vw; left: 1vw; content: 'viewport-units-buggyfill;top: 2vw;left: 1vw;'; } |
如果每一個都這樣來做,那麼將是災難性的。幸運的是,可以使用postcss-viewport-units
。其主要是給CSS的屬性新增content
的屬性,配合viewport-units-buggyfill
庫給vw
、vh
、vmin
和vmax
做適配的操作。
另一個坑,使用
postcss-viewport-units
將會給具有content
屬性的元素造成一定的影響,比如你的專案中使用偽元素::before
、::after
或者偽類:before
、:after
之類。那麼使用該外掛,會自動替換你原來的content
內容,為了避免該現象,需要在content
的屬性值末尾新增!important
。
上面這些PostCSS外掛是在這次專案中使用的,也將會在後面的專案中繼續使用,使用其主要原因是幫助我們解放雙手能更好的擼。或許你對其中一些外掛有更好的使用心得,歡迎和我們一起分享,如果你有更好的外掛,能幫助我們解放雙手,也歡迎分享給我們。
vw適配方案
vw
適配方案,主要是用於解決移動端佈局的問題。事實上,在手淘,甚至到目前為止都還在使用Flexible的佈局方案,用於適配移動端的各種終端。在15年雙11之後,寫了一篇《使用Flexible實現手淘H5頁面的終端適配》博文,將此方案分享給業內,而且該方案在業內快速的被複用和修改(原理是一樣的)。
Flexible的適合方案,在那個時期是非常強大的,想出這個方案的大神讓我膜拜已久。當然,事物是兩極的,他非常強大,但他也有自己的不足之處,特別是在vw
得到更多的支援的時候,我覺得Flexible應該退出其歷史的使用(這是我自己YY的)。所以在17年年初我開始在探討vw
在移動端中的使用,經過一段時間的探討和嘗試,我寫下了《再聊移動端頁面的適配》一文。
使用vw
可以看到測試用例得到了眾多裝置的支援:
大家看到眾多,或許會生疑,那麼還有不支援的將會是怎麼?特別是老闆跟我說,這次過年專案我們們使用vw
來做適配佈局吧。其實聽到這個訊息,我自己是非常高興的,畢竟學習過的技術方案有較大的專案來驗證。心裡是美的,但也略感壓力,就害怕又會折騰出新的妖蛾子。想想都怕怕(^_^)。
眾所周知,瀏覽器對vw
還具有一定的相容性,其在Android 4.4之下和iOS8以下的版本都存有一定的問題。為了讓vw
、vh
、vmin
和vmax
這些viewport
單位能更好的使用。需要考慮viewport
單位在不支援的瀏覽器(或裝置)做相應的處理。
為了能讓專案更安全,在決定過年專案中採用vw
佈局方案的時候,我就又再一次做了一個技術驗證,這次是基於Vue的Vue-cli腳手架的上來做的,畢竟我們的專案也要有Vue嘛。在這個腳手加上,我將上面介紹的PostCSS外掛配置進去,特別是postcss-px-to-viewport
和postcss-viewport-units
兩個PostCSS外掛和Viewport Units Buggyfill讓我完美的解決了vw
相容問題。而且讓開發者無感知。他們不需要考慮怎麼處理相容,只需要按著設計稿前行。
當然,完成這個技術方案的驗證,其中還是碰到一些坑的,幸好能像打老怪一樣,一個一個Fix。這裡就不闡述整個過程,如果你感興趣可以閱讀《如何在Vue專案中使用vw實現移動端適配》一文。接下來簡單的介紹一下vw
相容方案處理方式。
vw相容方案
移動端使用vw
佈局,其相容方案就是使用viewport
的polyfill:Viewport Units Buggyfill。使用viewport-units-buggyfill
主要分以下幾步走:
引入JavaScript檔案
在你的HTML檔案,比如index.html
中的</head>或</body>
引入下面的JavaScript檔案:
1 |
<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script> |
呼叫viewport-units-buggyfill的方法
同樣在HTML檔案中呼叫viewport-units-buggyfill
的方法,比如:
1 2 3 4 |
<script> window.onload = function () { window.viewportUnitsBuggyfill.init({ hacks: window.viewportUnitsBuggyfillHacks }); } </script> |
有關於Viewport Units Buggyfill更多的方法,可以閱讀其官網文件。
Flexible專案無縫過渡到vw
在前面釋出的部落格當中,有同學提到一個問題,如何能快速的將使用Flexible佈局的專案無縫過渡到vw
佈局,剛好借這次專案的機會做了一個小測試。實現這個也並不複雜。簡單的描述一下其過程:
第一步
將Flexible專案中的flexible.js
和flexible.css
刪除,並使用下面的vw
的相容指令碼:
1 |
<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script> |
第二步
在<head>標籤新增
meta
標籤:
1 |
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,viewport-fit=cover"> |
為了相容iPhone X的適配,在
meta
標籤中新增了viewport-fit="cover"
引數處理。有關於這方面的可以閱讀《iPhone X的缺口和CSS》和《iPhone X的Web設計》。
第三步
新增前面提到的PostCSS外掛的配置,當然,你可以只新增處理px
轉vw
和vw
配合Viewport Units Buggyfill的兩個PostCSS外掛:postcss-px-to-viewport
和postcss-viewport-units
。
PostCSS外掛配置有一個強大之處,不管你使用的是什麼腳手架,他的配置都非常的靈活,支援流行的配置工具,比如Webpack,gulp等。所以你不用擔心不好配置。因為在使用Flexible時,也需要
px2rem
的PostCSS外掛配置。
第四步
重新編譯你的程式碼,並在瀏覽器中驗證一下編譯後的頁面。為什麼要這麼做呢?主要是因為postcss-px-to-viewport
和postcss-viewport-units
兩個外掛會對::before
(:before
)、::after
(:after
)、img
中的content
做覆蓋。如果你的專案中有使用到具有content
屬性的元素,需要做一定的清理工作。目前這部分沒有較好的方式,我也諮詢過這兩個外掛的作者,他們反饋也沒找到更好的方案,只能人肉處理,或者變相不使用::before
和::after
這樣的東東,新增額外的元素標籤來替代。如果你嘗試之後,發現有更好的方案,歡迎把你的方案分享給我們。
iPhone X 適配
自從蘋果出了iPhone X的裝置,對於前端開發的同學而言,避免不了對其適配處理。而且這部分適配的處理相對而言是較為繁鎖的。我也一直在探尋從設計開始就能規避一些常規的適配問題。這部分內容正在整理,當其成熟之後再與大家分享。下面簡單的羅列一下自己對iPhone X適配的處理思路。
蘋果對於iPhone X上H5頁面的適配,提供了特殊屬性支援,包括meta標籤的viewport
屬性值中加入viewport-fit
和加入constant(safe-area-inset-*)
和env(safe-area-inset-*)
,這些屬性是與iOS11以上的所有iPhone機型(不僅僅包括iPhone X)都相關的,故以iOS版本為區別具體分析一下全屏下的H5頁面:
- 針對iOS11.0以下系統:將不識別H5頁面
meta
標籤下的viewport-fit
及constant(safe-area-inset-*)/env(safe-area-inset-*)
屬性。 - 針對於iOS11.0-iOS11.1的系統:當設定了
viewport-fit="cover"
,H5頁面會覆蓋頁面安全區域全屏展示,但是這樣會帶來頁面元素會被“劉海兒”和底部Home Indicator遮擋問題,所以蘋果提供在CSS中設定constant(safe-area-inset-*)
距離來規避遮擋問題。另外,頁面不加viewport-fit="cover"
預設viewport-fit="contain/auto"
,也就是我們看到的頁面不能覆蓋安全區域的情況,此時constant(safe-area-inset-*)
的值都為0
。所以在meta標籤的viewpoint
中加viewport-fit="cover"
時iOS10和iOS11下constant(safe-area-inset-*)
值的表現是不一樣的。 - 針對iOS11.2及iOS11.2以上的系統:
constant()
改成了env()
。另外,iOS11.2新增了CSS function:min()
和max()
。例如:padding-left: max(12px, env(safe-area-inset-left));
。在env(safe-area-inset-left)
值因為Webview變化時值也可以做出相應變化,取12px
和env(safe-area-inset-left)
的較大值。
總結如下圖:
通過媒體查詢針對 iPhone X採用個性化樣式處理:
1 2 3 4 |
@media only screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) { /* iPhone X 獨有樣式寫在這裡*/ } |
總結
最後感謝您花時間把這篇文章閱讀完。上面介紹的內容就是我自己在手淘過年專案中採用到的部分前端技術,稍作整理與大家分享。如果其中有不對之處,煩請路過的大嬸斧正。如果您在自己的專案中將採用上述提到的一些技術方案,踩到任何坑也歡迎一起探討。