【答阿里寒冬面試題】呵呵,大神的面試題就是好!

葉小釵發表於2014-08-08

前言

今天微博看到了寒冬大神的面試題,覺得挺有意思的,這裡就做一點解答

談談你對CSS佈局的理解

講講輸入完網址按下回車,到看到網頁這個過程中發生了什麼。

談談你對Web前端元件化的理解,Web Component會帶來怎樣的影響

談談你對前端資源下載效能優化的經驗和思考

現在有很多的MV*框架,你對它們有什麼看法

iOS體驗好在哪裡,Web能趕上麼?

網頁遊戲怎麼做?

Hybrid技術應當如何應用?

你最愛的前端框架是什麼,為什麼?

首先老師的題目都很大,不好答,我這裡技術有缺陷,答得不好大家不要噴我,可以好好教育我嘛

淺談CSS佈局

概述

從網頁表現來看,HTML是實體本身,要實現各種優雅的佈局的需要依賴CSS
HTML元素兩大型別為塊級元素與行內元素,網頁的佈局一般由塊級元素組織,元素不同便有不一樣的表現
要實現佈局就要用到與佈局有關的CSS屬性為position,常用屬性為static、absoulute、fixed、relative
position的作用是改變元素的狀態,由文件流轉為相對文件流或者脫離文件流元素(定位元素)
當然float也能引發塊級元素脫離文件流,但是其表現與原理與上面不一致,而且要慎用float佈局
於是這裡便已經有三種“流”了,不同的佈局元素會處於不同的“執行環境”,在“執行環境”中才能方便的對其進行管理
這個管理的依據便是BFC(塊級作用域上下文),根據我粗淺的理解是,BFC也就是瀏覽器提供的一塊渲染區
文件流的元素處於一個渲染區,而定位元素處於另一層渲染區,float元素又會與之不同
各個渲染區中的元素具有其定位規則,最簡單的便是文件流中的元素,遵循其塊級元素獨佔一行+盒模型的規則:
① 塊級元素獨佔一行,並且具有框(經典盒模型)
② 塊級元素間由margin屬性分割,並且垂直方向的margin會取其大者
③ 塊級元素必定左靠這父級塊級元素左邊框(包含塊,佈局上下文)
④ float元素不能影響塊級元素的BFC區域,但是其高度會參與計算
 
建立BFC的方式有:
① float
② overflow
③ display(使行內元素具有BFC)
④ position
 
基於此,我們便可以實現我們的佈局了,這裡以最簡單的兩列布局為例(左邊導航,右邊內容主體)
由於先出現的DOM先展示,所以內容優先原則,我們會將主體dom先展示
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
  <style>
    * { margin: 0; padding: 0; }
    div { border: 1px solid black; }
    #sidebar { position: absolute; left: 0; top: 0; width: 95px; }
    #content { margin-left: 100px; }
  </style>
  <script src="../jquery-1.7.1.js" type="text/javascript"></script>
</head>
<body>
  <div id="content">
    主體
  </div>
  <div id="sidebar">
    導航
  </div>
</body>
</html>
這裡從理論上說,便是觸發了siderbar元素的定位特性,其BFC與文件流不相關了
當然,這裡siderbar可以使用float實現,但是float本身應該用於處理文字與圖片包裹的需求,我們本著職責分離的想法就別那樣幹了
而且使用float還會引起元素坍塌,這裡還要費勁去處理清除浮動

行內元素

行內元素的處理比較複雜,首先行內元素的高度由其line-height決定,所以不要妄想其設定一個行內元素的高度,這會引起意想不到的問題
舉一個例子來說,很多大型網站都會具有統計程式碼,而此類統計程式碼一般是以img做請求發出,這個時候可能會導致10px左右的白屏問題
這個就是妄想設定行內元素高度的結果,獨立的inline元素出現時,會為其建立一個line boxes作為容器(文字框)
一行文字一個line Boxes,一行高度由其中最高的行內元素確定,上例中有一個height為0的img,卻為其生成了一個高度為10的行高
下面的程式碼中,div會被行內元素撐開,其高度會由span中line-height高者決定
<div>
  <span id="span1">主體 </span><span id="span2">導航 </span>
</div>

移動端的建議

① 移動端的文件流渲染效率最高,應該避免大範圍使用定位元素,在小米,三星等低端下定位元素可能渲染不出來(解決方案是引起迴流)
② img元素載入失敗時候會有邊框,需要搞掉
③ 慎用fixed屬性,fixed對於移動端來說有點頭疼,首先會有文字聚焦fixed定位錯亂問題,其次在ios4的螢幕中,如果加上瀏覽器上下工具條,再用fixed可視區域會變得很小
④ ios是按塊渲染的,佈局時候可以分塊,不會渲染可視區域外的內容
⑤ 動畫或者border-radius、box-shadow等屬性皆會使手機耗電加劇(但是這個好像與我們沒關係)
⑥ fixed元素為其設定html可能不響應或者說渲染看不見,常見於ipod或者低端android
⑦ 當然能在佈局上用上一些語義化標籤自然是好事,這樣對SEO或者特殊人事有一定幫助,比如strong、article之類的

總結

因為我本身專注點在js對css瞭解較少只能做此解析,請儘量拍磚指正

一次請求的完整流程

我們一次網址輸入會發起一連串連鎖效應,但是因為我這邊對HTTP一塊不是十分熟悉,只能解釋自己瞭解的
一次URl輸入後,其流程如下,首先是請求響應的流程:
1 解析URL,解析域名生成唯一IP地址,開始搜尋伺服器
2 找到伺服器後,伺服器接收請求被伺服器程式攔截;一般而言,.net的程式會被IIS處理,java會被jBoss或者tomcat處理
3 伺服器處理請求,如果靜態檔案的話直接返回,若是.net或者java等動態指令碼會經過伺服器編譯,執行其中的伺服器端程式碼
4 若是伺服器端具有資料庫操作的話,這裡還需要與DB建立連線,運算元據庫
5 伺服器處理結束後,生成最終的靜態HTML字串返回,開始向請求者(客戶端)返回請求字串,一次返回量過大就會分批次,這是一個優化點
6 伺服器響應到達瀏覽器,瀏覽器開始處理請求,進入瀏覽器解析流程

請求返回後,便進入了我們關注的前端模組

簡單來說,瀏覽器會解析HTML生成DOM Tree,其次會根據CSS生成CSS Rule Tree,而javascript又可以根據DOM API操作DOM
上面的文字描述的很簡單,事實上發生的事情,卻很複雜,這裡與程式碼實現也很有關聯,但是有幾個關鍵點:
頁面一定會等所有的HTML結構與CSS載入下來才渲染(webkit核心
比如我們這裡使用fiddler限制其外鏈載入,我們為其限速為10s
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
  <style>
    div { border: 1px solid black; }
    a { color: Red; }
  </style>
</head>
<body>
  <div>
    <a id="span1" href="http://www.baidu.com">主體 </a><span id="span2">導航 </span>
  </div>
  <link href=" http://kb.cnblogs.com/style/common.css" rel="stylesheet" />
</body>
</html>

我們看到事實上62ms後頁面整體dom結構就載入好了,這個時候我們是可以使用js操作dom結構的,但這裡一個重點是:
CSS外鏈載入會阻塞js的執行,並且重置CSS會引起瀏覽器的迴流或者重繪,也就是:
css外鏈會阻塞整個頁面的渲染(顯示),但是其DOM結構是可操作的,中間如果有阻塞性的操作,比如alert的話會強制瀏覽器繪製頁面
上面的特性與css外鏈的順序無關,並且有幾個樣式相關的操作,便會執行幾次,最終採用最近或者優先順序最高的樣式
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
  <style>
    div { border: 1px solid black; }
    a { color: Red; }
  </style>
</head>
<body>
  <div>
    <a id="span1" href="http://www.baidu.com">主體 </a><span id="span2">導航 </span>
  </div>
  <script>
    document.getElementById('span2').innerHTML = '測試';
    alert(1) 
  </script>
  <script>
    alert(2) 
  </script>
  <link href=" http://kb.cnblogs.com/style/common.css" rel="stylesheet" />
</body>
</html>

一個需要注意的地方是,chrome與firefox或者IE表現不一致,firefox與IE都是先渲染頁面最後等段CSS下來後再次渲染
所以我們很多同學面試時說將css外鏈放到header中,而不是body中的理解都是一知半解,這裡真實的處理辦法是非同步插入css外鏈才行
至少對於chrome需要做這個處理,否則頁面就是出不來,因為多數手機是webkit核心,保不齊這裡出多少事情
根據以上流程後,頁面也基本出來了,這裡簡述其流程
① 生成DOM樹
② 計算CSS樣式
③ 構建render Tree
④ reflow,定位元素位置與大小
⑤ 繪製頁面
上面過程如果js操作dom或者終結有一段style都會引起reflow,由於img會在文件載入結束後載入,可能會撐開頁面,導致迴流,所以一般需要對img設定尺寸
對於這塊的優化各位自己來吧,我這裡便只能理解到這個程度了
PS:事實上chrome獲得請求時本身也有幾個事件點,這裡可以用其開發效能外掛,但對前端基本透明,我們這裡不關注了

MVC與元件化

這裡我將2塊題目合到一起了,我覺得這裡可以連到一起做說明

View分離

很多程式的變革集中積累在VIEW一塊的處理,原因是這裡的需求是最複雜的,就View分離來說,最成功的我覺得是ASP到ASP.net的變革
最初的ASP程式,會將C#程式碼寫在asp程式碼中,這樣的結果便是頁面裡面既有html又有C#程式碼,甚至一個js迴圈中會巢狀出C#的迴圈
PS:你一定要相信,那個時候不會有js分成的概念,因為需求達不到
所謂的View分離便是,程式設計師確實受不了維護顯示與邏輯同時處於一個頁面的程式了,於是他選擇了改變,改變的結果便是拆分
View分離一大核心思維便是,分得開,合得攏,他們這裡引入一個codebehind方案將一個頁面檔案分成兩個(事實上是三個)

① index.apsx

② index.aspx.cs

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="_00綜合_11mvc_index" %>
public partial class _00綜合_10doc_write_index : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
}

最終這兩個檔案會和到一起,兩個檔案之間也可通訊,而通訊的橋樑是.net一個個被人詬病的元件,比如:

① button元件

② datalist

③ label元件

④ 偉大的gridview

各個元件在C#層面上會擁有一個ID進行約束,.cs檔案便可以根據該ID為其註冊各個事件,這個與javascript的模型便非常類似了

View分離的第一個優點便是,使用者的介面變得乾淨了,不會有程式碼混雜的現象,第二個優點便是業務端的程式碼可以再分離,從而多了資料層、業務層等各個概念

而元件的出現讓.net變成了世界上開發最快的語言,這點誰也不可否認,而這些都是前端MVC出現或者元件出現的意義

前端View分離

與asp當初的情況一致,js進行dom操作或者字串拼接的目的事實上就是想形成一個可供展示的View,前端jser已經再也不能忍受這種程式碼了:

很多時候,我們想將對應的模板單獨的放到一個地方,每次樣式若是有更改,只需要更改那個檔案即可,只要與之呼應的“ID”不丟失即可,比如這樣的結構和程式碼:

View Code
<div class="cui-pop-box">
  <div class="cui-hd">
      <%=title%>
  </div>
  <div class="cui-bd">
    <div class="cui-error-tips">
      <%=content%></div>
    <div class="cui-roller-btns">
      <% for(var i = 0, len = btns.length; i < len; i++ ) {%>
      <div class="cui-flexbd <%=btns[i].className%>">
        <%=btns[i].name%></div>
      <% } %>
    </div>
  </div>
</div>

這樣做的道理是,這個樣子做到了表現與行為分離,我們分得開,然後回根據基類的一個機制,讓他們最終合到一塊,以完成功能

這裡我們依舊需要強調一個前提:“ID”關聯不可丟失,否則“行為”將不可表現,只要兩者間關聯不丟,這裡便可輕鬆滿足以下邏輯:

① CSS名修改(非標識性CSS名)

② 標籤改變

③ 描述性文字改變

......

MVC在前端

重構一書有說,重複性或者類似重複性的程式碼應該抽象為一個方法,程式設計師也不傻,在重複使用需要提示框需求後便會學會形成alert元件

元件的出現是程式碼抽象,程式碼重用的標識,這個樣子既可以減少工作量也可以減少程式碼容量,而元件的實現與MVC思維的很好詮釋

比如以下程式碼:

View Code

事實上,MVC的任意一塊都可以作為單獨的模組實現,比如會有這樣的需求:一個model例項的變化會影響兩個View的顯示,但是僅僅對UI一塊來說我們做了如下處理:

核心點變成了幾個屬性:

① template,根據他生成UI

② datamodel,根據他生成viewModel提供給template使用

③ eventArr,業務事件註冊點

這個程式碼是Blade框架的UI基類,他是一個迷你MVC框架

 1     propertys: function () {
 2       //模板狀態
 3       this.template = '';
 4       this.datamodel = {};
 5       this.events = {};
 6       this.wrapper = $('body');
 7       this.id = _.uniqueId('ui-view-');
 8 
 9       //自定義事件
10       //此處需要注意mask 繫結事件前後問題,考慮scroll.radio外掛型別的mask應用,考慮元件通訊
11       this.eventArr = {};
12 
13       //初始狀態為例項化
14       this.status = 'init';
15 
16       //      this.availableFn = function () { }
17 
18     },

template作為View的實現,datamodel作為model實現,我們會根據datamodel與template生成基本的view實體

這裡datamodel與template之間會有一個viewModel的對映關係,是為了防止伺服器端突然將title變成Title而導致模板解析錯誤

而整個檔案便是一個控制器,view有的事件行為採用javascript委託技術全部註冊在根元素上,如此整個UI就活了,他的好處是:

① 具有繼承關係,可以擁有統一的資源釋放,或者View通訊機制

② 程式碼量也會減少

MVC與元件化的意義

MVC對前端的意義甚大,因為jser對MVC或者分模組的思維的深入理解,我們才會將前端程式碼做分離,這樣會有效的避免多人更改一個檔案的難題

這對前端的推動作用不可謂不大!

元件化是對重複工作,重複程式碼的降低,當然是好東西......

資源載入

前端優化的瓶頸始終在資源載入,只要載入快,無論你程式碼寫的多慢(不要糾結死迴圈),都一定快,所以前端優化事實上一直都是一個主題:瘦身

壓縮

css sprite

延遲載入(主要針對圖片)

按需載入(主要針對首屏未用到的UI)

快取

CDN

預載入

等詞語不斷的在前端出現,但是一個不可避免的事實是,需求越發複雜,體驗要求越高,所以一個前端可能的事實是:

前端資源體積越來越大,避免此事發生不是前端說了算的,需要產品與互動一起努力,清晰、體驗好、輕量級的設計誰不喜歡,但是廣告營銷也不能不考慮,總之此事路還長!

webapp的資源載入

對於webapp來說,首次載入的體積會更加大,因為其要求的庫更多,並且網速更慢,這個時候可以採取fake頁的方案

我們應該避免頁面長時間白頁,這個時候便提出了fake頁的概念。頁面渲染只需要完整的HTML以及CSS,這個便是第一個優化點。

從資料請求數以及請求量來說,webapp首頁的響應應該比較慢,若是任由js載入完成再渲染頁面,使用者很有可能失去耐心。

但是從DOMContentLoaded來看,首頁事實上頁面響應比較迅速,所以這個載入結束後頁面第一屏便渲染結束,然後再非同步載入js,當js改變後再動態改變dom結構中的一些關鍵點

這個時候一個靜態HTML頁面,裝載首屏的基本內容,讓首頁快速顯示

然後js載入結束後會馬上重新渲染整個頁面,這個樣子,使用者就可以很快的看到頁面響應,給使用者一個快的錯覺,給人感覺快得多。

是否按需載入

按需載入的話確實會對首屏載入有好處,但是是否按需載入卻不一定了,以下面兩個體驗為例:

資源完全載入

http://yexiaochai.github.io/blade/demo/dest.html

按需載入

http://yexiaochai.github.io/blade/demo/debug.html

PS:我這裡debug版本點選時候未做mask處理,大家不要瘋狂點選了

這裡各位直觀的感受是什麼呢?是不是,首次載入後後續操作十分流暢,而按需載入的話,每次皆會感覺有點“卡”

按需載入需要載入js和模板,這個過程自然卡,所以真正是不是要按需載入,或者多少資源按需載入有個臨界值,需要不斷的測試才行

最後,要減少資源的話,程式碼的質量神馬的也需要考慮的,但對容量來說效用可能不是太大

IOS的體驗

IOS的體驗好,主要好在兩點:

① 清晰、簡潔、傻瓜化設計,連我媽都可以輕巧的操作蘋果手機其簡易型可想而知

② 使用者行為模擬,IOS體驗第二點便是其無敵的動畫效果

若是說web應用想趕上(寒冬老師這裡趕上一詞用得十分貼切,他要說超過估計沒人理他了)native開發的話還是有可能

但是如果說web應用要趕上IOS的設計的話,那麼還有無數荊棘需要踩過!並且有兩大前提:

① 網速快,使用者不必關心資源大

② 手機效能好,不說超越PC,至少能“趕上”PC吧

以上兩點成立的話,web應用趕上IOS體驗才變得可能,簡單來說就動畫而言,前端有哪些問題呢?

webapp中的動畫

webapp的一大優勢便是在view切換時候可以擁有媲美與native的動畫效果,但是很多時候那只是一種想法,真正的情況卻不是這樣
產生此問題的原因有:
① 手機CPU爛!
② 手機顯示卡爛!就算四核其渲染也很有問題
③ 高階手機瀏覽器會有BUG
④ 低端手機支援不好(國內山寨機笑而不語)
因為以上原因,事實上做webapp的都會不同程度的弱化動畫,或者在區域性區域使用動畫

難在何處?

dom樹過多
view的移動與簡單的圖片slider元件相差甚劇!原因便是其dom結構可能很複雜,大dom樹的移動在移動端效果很差
就簡單列表頁來說,當專案超過100個時,使用IScroll類外掛都應該很慎重,這類移動可能非常卡!
而且dom樹複雜度與業務直接相關,我們沒有任何辦法去控制dom樹,因為業務程式碼可能不會經過我們的手,就算經過,你又肯定自己做出來的dom樹有多小?不見得吧
長短頁問題
所謂長短頁便是一個view很長一個view很短,這裡問題處理十分討厭
首先我們每次做切換需要將view位置切換至頭部(window.scroll(0, 0))如此的話ios中會引起
頁面viewport的變化(系統自動發生),或者會觸發低端工具欄的出現,這個時候頁面抖動無可避免
若是每次不執行window.scroll(0, 0),切換時候又會導致短的view不可見
我現階段想到的解決方案是,移動時候將scroll設得比較大,移動時候將bview的top值與scrollTop相同
最後仍然需要執行window.scroll(0, 0)的操作,所以,這個問題只能緩解,無法解決
手機渲染問題
只要是做移動端的朋友,一定會對三星機或者一些低端機的渲染嗤之以鼻!
具體表現為多次操作style後,後面的操作瀏覽器不會搭理你
解決方案是:
① 引起瀏覽器強烈重繪
② 臨時增刪一個dom結構
但是涉及view切換動畫的話,很有可能會出現一些莫名其妙的問題!

所以單單由簡單的webapp的切換都如此困難,web應用想趕上IOS的話還要等幾年......

Hybrid對前端的意義

應該說Ajax帶動了前端的首次革命,而移動端帶來了二次革命,而Hybrid將前端推上了風口浪尖!

原來我廠app是使用native開發的,會出現此等問題:一個應用需要養3個團隊(ios、android、winphone),而且一旦業務改變會讓開發抓狂!

而Hybrid的出現解決了此問題,一套前端程式碼可以用於四個地方:瀏覽器、ios、android、winphone,而這個卻是網際網路公司最需要的

最近兩年,移動大潮襲來,各個公司皆在搶佔移動端的份額,誰贏了這場戰鬥誰就是下一代王者,所以出現了一個事實:

市場的佔領、業務的擴充套件是第一位!

所以Hybrid的市場與需求比native大,但是這不是說native沒用了,因為更好的體驗我們的追求,所以在Hybrid佔領市場結束後,可能需要改版為native

除非那時前端的體驗能縮小與native的距離

Hybrid開發的問題

Hybrid提高了前端開發的門檻,因為Hybrid的除錯難,但是業內也出現了一些除錯的方案,我這裡有一套Hybrid除錯的技巧,有興趣可以交流

Hybrid 相容BUG多,總所周知,H5站點上看著好好的程式,一旦到了webview上就出問題了,這個除錯也很痛苦

哎,其實Hybrid開發也只是需要一點熟悉度罷了,沒有什麼可說的,就此打住吧

結語

寒冬老師喜歡出題,我喜歡答題,其中幾道題我這裡沒有什麼概念就交白卷了,不知道我的回答是否可以及格

若是寒冬老師或者各位覺得我的回答能勉強及格,左邊微博求粉!!!尼瑪我粉絲太少了!!!

相關文章