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


前言

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

談談你對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開發也只是需要一點熟悉度罷了,沒有什么可說的,就此打住吧

結語

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM