個人第一次移動端頁面製做 — 總結與思考

最近被分配到移動端開發組,支持某活動的頁面頁面製做。這算是我第一次真正接觸移動端頁面製做,下面就談談我的總結和思考。javascript

總體流程

開會大致講解、討論與排期 -> 交互設計 -> 視覺設計 -> 頁面頁面製做 -> 前端開發 -> 測試css

每一個步驟環環相扣,每一個職位都須要和其先後的人溝通協調。html

測試遇到問題則會反饋到相應環節負責人。前端

固然,涉及的職位也不只於此,還有法務同事審覈內容是否符合當前法規等等。java

構建工具

Athena

前端開發離不開構建工具,除了敲代碼,其他都交給構建工具(如組件開發、CSS 兼容處理、圖片 Base6四、圖片雪碧圖和壓縮處理等)。
Athena 中,文件層級結構以下:項目 project -> 模塊 module(具體每一個活動) -> 頁面 page -> 部件 widget。 web

舉例: 某項目 -> X、Y 活動 -> 預熱頁和高潮頁 -> 頭部、彈框等 widget。通常文件目錄以下:編程

Xproject
    - gb (公共部分,如初始化樣式和一些經常使用 widget)
    - X活動
        - page
            - 預熱頁
            - 高潮頁
        - widget
            - header
            - footer
            - diglog
    - Y 活動
    - ...

剛開始接觸時,存在這樣的一個疑惑:什麼是 widget,一個不可複用的頁面頭部能夠做爲 widget 嗎?
答:我最初的想法是:「錯誤地把 widget 當成 component,component 一直被強調的 特性之一是可複用性。對於不可複用的部分就不該該抽出爲一個widget了?」其實對於一個相對獨立的功能,咱們就可把它抽出來。這無疑會加強程序的可維護性。 瀏覽器

對於一個項目,通常一個模塊由一我的負責。但考慮到每一個模塊間可能存在(或將來存在)可複用的 widget,須要規範命名以造成命名空間,防止衝突(具體會在下面的規範-命名中闡述)。多線程

Component 與 Widget 的區別
Component 是更加廣義抽象的概念,而Widget是更加具體現實的概念。因此Component的範圍要比Widget大得多,一般 Component 是由多個 Widget 組成。app

舉個例子,可能不是很恰當,但願幫助你的理解,好比家是由牀,櫃子等多個 Component 組成,櫃子是由多個抽屜 Widget 組成的。
而 Component 和 Widget 的目的都是爲了模塊化開發。

其實,在這裏並無對 widget 和 component 作這麼細的區分。

規範

widget

正如上面討論的,一個頁面由多個 widget 組成。所以,一個頁面看起來以下:

<body ontouchstart>
  <div class="wrapper">
    <!-- S 主會場頭部 -->
    <%= widget.load("app_market_main_header") %>
    <!-- E 主會場頭部 -->
    <!-- S 達人問答區 -->
    <%= widget.load("app_market_answer") %>
    <!-- E 達人問答區 -->
    <!-- S 優惠券 -->
    <%= widget.load("app_market_coupons") %>
    <!-- E 優惠券 -->
    <!-- S 達人集中營 -->
    <%= widget.load("app_market_camp") %>
    <!-- E 達人集中營 -->
    <!-- S 達人穿搭公式 -->
    <%= widget.load("app_market_collocation") %>
    <!-- E 達人穿搭公式 -->
    <!-- S 卡券相關彈框 -->
    <%= widget.load("app_market_dialog") %>
    <!-- E 卡券相關彈框 -->
  </div>

widget 通常存在可複用性。但如何控制細粒度呢?分得越細代碼就越簡潔,但工做量和維護難度可能會上升,所以須要權衡你當時的狀況。

CSS 命名

命名空間

因爲一個項目中,一個模塊由某一我的負責,但模塊之間的 widget 存在或將來存在可複用的可能(並且開發可能會爲你的頁面添加已有的組件,如頁面會嵌在某 APP 內,該 APP 已有現成的一些提示框)。所以,須要命名空間將其它們進行區分以防止衝突。因爲 CSS 不存在命名空間,所以只能經過相似 BEM 的方式(具體根據團隊的規範),如:app_market_headerapp_market_list_itemapp_market 是模塊(即某個活動)的標識,在該項目下,它是惟一的。

另外,還有一點:類名是否要按照 html 層級關係層層添加呢?如:

div.app_market_header
    div.app_market_header_icon
    div.app_market_header_**

對於 app_market_header_icon,儘管在 header 中,但 icon 並不僅屬於 header,而屬於整個模塊(活動),那麼咱們就能夠改成 app_market_icon

命名存在的問題

老司機 Code review 後,講了如下內容:
反面教材:

<div class="app_market_answer">
  <div class="app_market_secheader"></div>
  <div class="app_market_answer_list">
    <div class="app_market_answer_item">
      <div class="app_market_answer_item_top"></div>
      <div class="app_market_answer_item_middle"></div>
      <a href="javascript:;" class="app_market_answer_item_bottom">去圍觀</a>
    </div>
</div>

存在的問題是:嵌套層級越深,類名就越長。

較好的解決方案:

<div class="app_market_answer">
  <div class="app_market_secheader"></div>
  <div class="app_market_answer_list">
    <div class="app_market_answer_item">
      <div class="app_market_answer_itop"></div>***
      <div class="app_market_answer_imid"></div>***
      <a href="javascript:;" class="app_market_answer_ibtm">去圍觀</a>***
    </div>
</div>

這是基於『姓名』原理進行優化的,舉例:app_market_answer_item 是姓名(庫日天),那麼它的子元素只需繼承它的『姓』(庫姆斯) app_market_answer_itop,而不是它的姓名(庫日天姆斯) app_market_answer_item_top。每當類名達到三到四個單詞長時,就要考慮簡化名字。

進一步優化,app_market 能夠當作是『複姓』,有時爲了書寫便利,能夠以兩個單詞的首字母結合造成一個新的『新姓』- 『am』。固然,追求便利的反作用是犧牲了代碼的可讀性。若是你負責的項目或頁面沒有太大的二次維護或者交叉維護的可能性,推薦作此簡化。

BTW:此簡化後的『姓』能夠在代碼中稍加註釋說明,以下代碼所示:

<!-- am = app_market -->
<div class="am_answer">
  <div class="am_secheader"></div>
  <div class="am_answer_list">
    <div class="am_answer_item">
      <div class="am_answer_itop"></div>
      <div class="am_answer_imid"></div>
      <a href="javascript:;" class="am_answer_ibtm">去圍觀</a>
    </div>
</div>

針對類名書寫樣式

<div>
    <a href="javascript:;">...</a>
</div>

至少加一個類名,任什麼時候候都儘可能要『針對類名書寫樣式,而不是針對元素書寫樣式』,除非你能預判元素是末級元素。
所以對於如下 CSS:

.app_market_coupons > div {
    ...
}

可優化成:

.app_market_coupons > .xxx {
    ...
}

技術涉及

REM

移動端採用 rem 佈局方式。經過動態修改 html 的 font-size 實現自適應。

實現方式

REM 佈局有兩種實現方式:CSS 媒介查詢和 JavaScript 動態修改。因爲 JavaScript 更爲靈活,所以如今更多地採用此方式。

JavaScript

凹凸的實現方式是:在 head 標籤末加入如下代碼

<script type="text/javascript">
    !function(){
      var maxWidth=750;
      document.write('<style id="o2HtmlFontSize"></style>');
      var o2_resize=function(){
          var cw,ch;
          if(document&&document.documentElement){
              cw=document.documentElement.clientWidth,ch=document.documentElement.clientHeight;
          }
          if(!cw||!ch){
              if(window.localStorage["o2-cw"]&&window.localStorage["o2-ch"]){
                  cw=parseInt(window.localStorage["o2-cw"]),ch=parseInt(window.localStorage["o2-ch"]);
              }else{
                  chk_cw();//定時檢查
                  return ;//出錯了
              }
          }

          var zoom=maxWidth&&maxWidth<cw?maxWidth/375:cw/375,zoomY=ch/603;//由ip6 weChat
          window.localStorage["o2-cw"]=cw,window.localStorage["o2-ch"]=ch;
          //zoom=Math.min(zoom,zoomY);//保證ip6 wechat的顯示比率
          window.zoom=window.o2Zoom=zoom;
          document.getElementById("o2HtmlFontSize").innerHTML='html{font-size:'+(zoom*20)+'px;}.o2-zoom,.zoom{zoom:'+(zoom/2)+';}.o2-scale{-webkit-transform: scale('+zoom/2+'); transform: scale('+zoom/2+');} .sq_sns_pic_item,.sq_sns_picmod_erea_img{-webkit-transform-origin: 0 0;transform-origin: 0 0;-webkit-transform: scale('+zoom/2+');transform: scale('+zoom/2+');}';
      },
      siv,
      chk_cw=function(){
          if(siv)return ;//已經存在
          siv=setInterval(function(){
              //定時檢查
              document&&document.documentElement&&document.documentElement.clientWidth&&document.documentElement.clientHeight&&(o2_resize(),clearInterval(siv),siv=undefined);
          },100);
      };
      o2_resize();//當即初始化
      window.addEventListener("resize",o2_resize);
  }();
  </script>

從以上代碼可得出如下信息:

  1. 以 iPhone 6 爲基準,iPhone 6 的縮放比 zoom1

  2. 因爲只針對移動端,所以最大寬度爲768(剛好等於 iPad 的豎屏寬度)

  3. 經過 document.documentElement.clientWidth 獲取視口寬度

  4. resize 事件主要考慮橫豎屏切換和你在PC上調試時?

  5. zoom 係數是 20。係數決定了在寬度 375 的 iPhone6 下,1 rem 的值是多少 px(20px)。固然若是想過渡到 vw,能夠將 zoom 係數設置爲 3.75,那麼 100rem 就是 375px 了

爲何要用

有人說 rem 佈局是 vwvh 的替換方案,當 vwvh 成熟時,二者可能會各司其職吧。

vw 的兼容性:在安卓 4.3 及如下是不支持的。

哪些地方要用

因爲 rem 佈局是相對於視口寬度,所以任何須要根據屏幕大小進行變化的元素(width、height、position 等)均可以用 rem 單位。

但 rem 也有它的缺點——不精細(在下一節闡述),其實這涉及到了瀏覽器渲染引擎的處理。所以,對於須要精細處理的地方(如經過 CSS 實現的 icon),能夠用 px 等絕對單位,而後再經過 transform: scale() 方法等比縮放。

字體

font-size 是否也要用 rem 單位呢? 這也是我曾經糾結的地方。若是不等比縮放,對不起設計師,並且對於小屏幕,一些元素內的字體會換行或溢出。固然這能夠經過 CSS3 媒介查詢解決這種情況。

字體不採用 rem 的好處是:在大屏手機下,能顯示更多字體。

看到 網易新聞聚划算 的字體大小都採用 rem 單位,我就不糾結了。固然,也有其它網站是採用絕對單位的,二者沒有絕對的對與錯,取決於你的實際狀況。

缺點

小數點(不精細,有間隙)

因爲 rem 佈局是基於某一設備實現的(目前通常採用 iPhone6),對於 375 倍數寬的設備無疑會擁有最佳的顯示效果。而對於非 375 倍數寬的設備,zoom 就多是擁有除不盡的小數,根元素的字體大小也相應會有小數。而瀏覽器對小數的處理方式不一致,致使該居中的地方沒徹底居中,但你又不能爲此設置特定樣式(如 margin-top: *px;),由於瀏覽器多如牛毛,這個瀏覽器微調居中了,而本來居中的瀏覽器變得不居中了。

對於圖標 icon,rem 的不精細緻使經過多個元素(僞元素)組合而成的 icon 會造成錯位/誤差。所以,在這種狀況下,須要權衡是否須要使用 CSS 實現了。

SASS

SASS 無疑加強了本來聲明式的 CSS,爲 CSS 注入了可編程等能力。在此次項目,算是我第一次使用 SASS,因爲構建工具和基礎庫的完善,只需經過查看/模仿已有項目的 SASS 用法,就能快速上手。後續仍是要系統地學習,以更合理地使用 SASS。

使用 SASS 的最大問題是:層級嵌套過深,這也是對 SASS 理解不深刻的緣由。能夠關注一下轉譯後的 CSS。

兼容性

此次項目的 APP 採用手機自帶瀏覽器內核,而這些瀏覽器內核依賴於系統版本等因素。另外,國產機也會對這些內核進行定製和修改。特別是華爲、OPPO。

下面列出我所遇到的兼容性問題(不列具體機型,由於這些兼容性處理終會過期,沒必要死記硬背,遇到了能解決就好(要求基礎紮實)):

  • flexbox:在構建工具處理下(實現了新舊語法)能夠大膽用,但個別設備不支持 flex-wrap: wrap。所以對於想使用 flex-wrap 實現自動分行的狀況,建議使用其餘實現。若是個數固定(如 N 行,每行 M 個),則可以使用 N 個 flexbox(這樣就可使用 flexbox 的特性了)。flexbox 的其餘屬性也有支持很差的狀況,能夠經過顯式聲明 display、overflow、width、height 等方法解決。

  • background-size:須要單獨寫,不然在 安卓 4.3 及如下,IOS 6.1及如下不兼容

  • 漸變:線性漸變大膽使用,徑向漸變有兼容性問題。可是不建議對總體背景使用,會有性能問題(可簡單地經過 1px 高的圖片替代,注意,不要 background-size: 100% auto; 應該採用 background-size: 100% 1px; 由於有些瀏覽器(視口寬度較小)會忽略小數點【auto = img.Height * (screen.Width/img.Width)】,致使圖片未顯示)。另外,須要注意的是:透明的色標在iOS 默認是黑色的,即 transparent 等於 rgba(0,0,0,0)。所以即便是徹底透明的色標,也要指定顏色。不然後果以下:
    此處輸入圖片的描述

  • classlist.remove(String[, String]),傳遞多個參數時,會有不兼容的狀況。建議每次寫一個。add (String[, String])同理。

  • 根節點 html font-size 渲染錯誤:在華爲、魅族的某設備上(手Q),會出現一個很是奇葩的渲染 Bug,同一個網頁,「掃一掃」打開 html 的 font-size 正常,直接點擊連接會出現渲染出來的 html font-size 會比設置得值大(如:設置25.8,渲染出來是 29),所以致使總體變大,且佈局錯亂。
    個人方法是:爲 html font-size 從新設置大小:渲染字體大小 - (渲染與正常差值)

    function getStyle(ele, style) {
    return document.defaultView.getComputedStyle(ele, null)[style]
    }
    ;(function fixFontSize() {
    var target = window.o2Zoom * 20
    var cur = parseInt(getStyle(document.documentElement, "fontSize"))
    while(cur - target >= 1) {
        document.documentElement.style["fontSize"] = target - (cur - target) + "px"
        cur = parseInt(getStyle(document.documentElement, "fontSize"))
    }          
    })();

有網友提供這個方法 <meta name="wap-font-scale" content="no">,經測試不可行。此方法是針對 UC 瀏覽器的。

上面主要列出了對使用有影響的兼容性問題,有些因爲瀏覽器渲染引擎致使的問題(不影響使用),若沒法經過 transform、z-index 等解決,也許只能經過 JavaScript 解決或進行取捨了。

其餘一些知識點

  • 圖片佔位元素:對於寬高比例固定的坑位(如商品列表項),經過將圖片放置在佔位元素中,可避免圖片加載時引發的頁面抖動和圖片尺寸不一致而致使的頁面佈局錯亂。代碼實現:

    .img_placeholder {
    position: relative;
    height: 0;
    overflow: hidden;
    padding-top: placeholder 的高/寬%; // padding-top/bottom: 百分比; 是基於父元素的寬度
    img {
        width: 100%;
        height: auto;
        position: absolute;
        left: 0;
        top: 0;
    }
    }
  • 1px:在 retina 屏幕下,1 CSS像素是用 4 個物理像素表示,爲了在該屏幕下顯示更精細,經過爲 ::after 應用如下代碼(以上邊框爲例):

    div {
    position: relative;
    &::after {
        content: '';
        position: absolute;
        z-index: 1;
        pointer-events: none;
        background: $borderColor;
        height: 1px;left: 0;right: 0;top: 0;
        @media only screen and (-webkit-min-device-pixel-ratio:2) {
            &{
                -webkit-transform: scaleY(0.5);
                -webkit-transform-origin: 50% 0%;
            }
        }
    }
    }
  • 根據元素個數應用特定樣式:

    /* one item */
    li:first-child:nth-last-child(1) {
    width: 100%;
    }
    /* two items */
    li:first-child:nth-last-child(2),
    li:first-child:nth-last-child(2) ~ li {
    width: 50%;
    }
    /* three items */
    li:first-child:nth-last-child(3),
    li:first-child:nth-last-child(3) ~ li {
    width: 33.3333%;
    }
    /* four items */
    li:first-child:nth-last-child(4),
    li:first-child:nth-last-child(4) ~ li {
    width: 25%;
    }

    應用樣例有:根據元素個數自適應標籤樣式。

根據元素個數自適應標籤樣式
而對於反方向標籤,可先首先對總體 transform: scale(-1),而後再對字體 transform: scale(-1) 恢復從左向右的方向。效果以下:
標籤反向

  • 卡券:『帶孔且背景是漸變的卡券』在複雜背景中的實現。因爲背景是複雜的(非純色),所以孔不能簡單地經過覆蓋(與背景同色)產生。這裏能夠應用徑向漸變 background-image: radial-gradient(rem(189/2) 100%, circle, transparent 0, transparent 3px, #fa2c66 3px);,其中 3px 是孔的半徑。另外,卡券的上下部分是線性漸變的,所以能夠在上下部分分別經過僞類元素添加 background-image: linear-gradient(to top, #fa2e67 0, #fb5584 100%);,固然,要從離外上/下邊界 3px 的地方開始。雖然這不能完美地從最邊界開始,但效果仍是能夠的。但因爲徑向漸變的兼容性問題,我最終仍是用圖片替換了這種實現。?
    帶孔且背景是漸變的卡券

  • 多行文本的多行padding:讓背景只出如今有文字的地方,可直接設置 display: inline;,但還會存在一個問題是:padding 只會出如今多行文本的首和尾,對於須要爲每行文本的首尾都須要相同的 padding,能夠參考這篇文章:《multi-line-padded-text》 。該文章提供了多種實現方式,根據具體狀況選擇一種便可。另外,對於每行的間距,可經過設置 line-height 和 padding-top/bottom 實現,其中 line-height 要大於(字體高度+padding-top/bottom)。
    此處輸入圖片的描述

此處輸入圖片的描述

  • 最小字體限制:PC上最小字體是 12px、移動端最小是 8px,固然可經過 transform:scale() 突破限制。

不止頁面頁面製做

  1. 基礎:合理運用 CSS 的威力更好地完成對設計稿的重現目的。

  2. 溝通:因爲分工較細,只負責頁面製做的同窗,須要與產品和設計溝通,以達到交給開發後更少修改的目的。如哪些地方可跳轉、哪些地方最多顯示幾行文字、超出如何處理(直接隱藏/省略號等)、坑位中的圖片擺放(頂部對齊/居中等)等等。

  3. 代碼上的溝通:HTML 註釋要寫好、HTML 與 CSS 代碼要規範(命名等)清晰。

思考

因爲工具的成熟,我不須要考慮構建工具的搭建。
因爲發佈方式的成熟,頁面製做和開發能更好地分離,頁面製做者負責輸出 HTML、CSS,開發負責 copy html 代碼和引入 CSS 頁面片。CSS 頁面片由頁面製做者更新發布,開發無需關心。這達到了互不干擾、多線程並行的效果。
成熟的基礎設施讓咱們免除了非代碼相關的煩惱,但這也讓我擔憂:假若有一天我脫離了這些基礎設施,我該如何保持高效。

延伸:頁面片是什麼?

CSS 頁面片

<!-- #include virtual="/folder/branch.shtml" -->
<link combofile="/folder/branch.shtml" rel="stylesheet" href="//website/folder/gb.min_1151b5b0.css,/folder/branch.min_925332fc.css" />

JS 頁面片

<!-- #include virtual="/folder/branch_js.shtml" -->
<script combofile="/folder/branch.shtml" src="//website/path/branch.min_8971778a.js"></script>

Combo Handler是Yahoo!開發的一個Apache模塊,它實現了開發人員簡單方便地經過URL來合併JavaScript和CSS文件,從而大大減小文件請求數。 http://www.cnblogs.com/zhengy...


這就是個人第一次...? 學習不少,完!

以上僅是我我的完成某項目頁面製做的思考和總結,不當心暴露了團隊下限。?

相關文章
相關標籤/搜索