- 面試彙總一:2018大廠高階前端面試題彙總
- 高階面試:【半月刊】前端高頻面試題及答案彙總
css內容
響應式佈局
當前主流的三種預編譯器比較
- CSS前處理器用一種專門的程式語言,進行Web頁面樣式設計,然後再編譯成正常的CSS檔案,以供專案使用;
- 讓你的CSS更加簡潔、適應性更強、可讀性更佳,更易於程式碼的維護等諸多好處。
less,sass,stylus三者的區別
-
1.變數:
- Sass宣告變數必須是『$』開頭,後面緊跟變數名和變數值,而且變數名和變數值需要使用冒號:分隔開。
- Less 宣告變數用『@』開頭,其餘等同 Sass。
- Stylus 中宣告變數沒有任何限定,結尾的分號可有可無,但變數名和變數值之間必須要有『等號』。
-
2.作用域:
- Sass:三者最差,不存在全域性變數的概念。也就是說在 Sass 中定義了相同名字的變數時你就要小心蛋疼了。
- Less:最近的一次更新的變數有效,並且會作用於全部的引用!
- Stylus:Sass 的處理方式和 Stylus 相同,變數值輸出時根據之前最近的一次定義計算,每次引用最近的定義有效;
-
3.巢狀:
- 三種 css 預編譯器的「選擇器巢狀」在使用上來說沒有任何區別,甚至連引用父級選擇器的標記 & 也相同。
-
4.繼承:
- Sass和Stylus的繼承非常像,能把一個選擇器的所有樣式繼承到另一個選擇器上。使用『@extend』開始,後面接被繼承的選擇器。Stylus 的繼承方式來自 Sass,兩者如出一轍。
- Less 則又「獨樹一幟」地用偽類來描述繼承關係;
-
5.匯入@Import:
- Sass 中只能在使用 url() 表示式引入時進行變數插值:
$device: mobile; @import url(styles.#{$device}.css); 複製程式碼
- Less 中可以在字串中進行插值:
@device: mobile; @import "styles.@{device}.css"; 複製程式碼
- Stylus 中在這裡插值不管用,但是可以利用其字串拼接的功能實現:
device = "mobile" @import "styles." + device + ".css" 複製程式碼
-
總結
- 1.Sass和Less語法嚴謹、Stylus相對自由。因為Less長得更像 css,所以它可能學習起來更容易。
- 2.Sass 和 Compass、Stylus 和 Nib 都是好基友。
- 3.Sass 和 Stylus 都具有類語言的邏輯方式處理:條件、迴圈等,而 Less 需要通過When等關鍵詞模擬這些功能,這方面 Less 比不上 Sass 和 Stylus。
- 4.Less 在豐富性以及特色上都不及 Sass 和 Stylus,若不是因為 Bootstrap 引入了 Less,可能它不會像現在這樣被廣泛應用(個人愚見)。
link與@import區別與選擇
<style type="text/css">
@import url(CSS檔案路徑地址);
</style>
<link href="CSSurl路徑" rel="stylesheet" type="text/css" /
複製程式碼
- link功能較多,可以定義 RSS,定義 Rel 等作用,而@import只能用於載入 css;
- 當解析到link時,頁面會同步載入所引的 css,而@import所引用的 css 會等到頁面載入完才被載入;
- @import需要 IE5 以上才能使用;
- link可以使用 js 動態引入,@import不行;
垂直居中佈局
css3的高階特性舉例
h5新增了哪些內容或者API,使用過哪些?
1畫素邊框問題
什麼是BFC
BFC是什麼
- BFC 英文解釋: block formatting context。中文意思:塊級格式化上下文;
- formatting context 意思是:頁面中一個渲染區域,有自己的一套渲染規則,決定其子元素如何定位,以及和其他兄弟元素的關係和作用。
- BEC有如下特性
- 內部的box會在垂直方向,從頂部開始一個接一個地放置;
- box垂直方向的距離由margin決定。屬於同一個BFC的兩個相鄰的box的margin會發生摺疊;
- 每一個元素的margin box的左遍,與包含塊border box的左邊相接觸。即使浮動元素也是如此;
- BFC區域不會與float box疊加;
- 計算 BFC 的高度時,浮動子元素也參與計算;
- 文字層不會被浮動層覆蓋,環繞於周圍
產生BFC 作用的css 屬性
- float 除了none以外的值;
- overflow 除了visible 以外的值(hidden,auto,scroll );
- display (table-cell,table-caption,inline-block, flex, inline-flex);
- position值為(absolute,fixed) 這些屬性值得元素都會自動建立 BFC;
BFC作用
- BFC 最大的一個作用就是:在頁面上有一個獨立隔離容器,容器內的元素 和 容器 外的元素佈局不會相互影響。
- 解決上外邊距重疊;重疊的兩個box都開啟bfc;
- 解決浮動引起高度塌陷;容器盒子開啟bfc;
- 解決文字環繞圖片;左邊圖片div,右邊文字容器p,將p容器開啟bfc;
js基礎
for...in和for...of區別
- for...in
- 1.迴圈出來的是index索引,是字串型的數字;
- 2.遍歷順序有可能不是按照實際陣列的內部順序;
- 3.使用for in會遍歷陣列所有的可列舉屬性,包括原型上的以及陣列自定義的屬性;
- 所以for in更適合遍歷物件,不要使用for in遍歷陣列。
- 推薦在迴圈物件屬性的時候,使用for...in,在遍歷陣列的時候的時候使用for...of;
- for...in迴圈出的是key,for...of迴圈出的是value;
- 注意,for...of是ES6新引入的特性。修復了ES5引入的for...in的不足;
- for...of不能迴圈普通的物件,需要通過和Object.keys()搭配使用;
Object.prototype.objCustom = function () {};
Array.prototype.arrCustom = function () {};
let iterable = [3, 5, 7];
iterable.foo = "hello";
for (let i in iterable) {
console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}
for (let i of iterable) {
console.log(i); // logs 3, 5, 7
}
複製程式碼
script 引入方式:
- html 靜態',則只是在dom中插入了一行字串,就更不會管字串裡引入的js了,所以不能用這個方法插入script!!!
- 比如: 1.js 依賴 2.js,2.js 依賴 3.js;實際的載入順序是為 1.js,2.js,3.js
- 注意: 而實際模組執行的順序,才是 3.js,2.js,1.js。所以,檔案的載入、載入後檔案的執行、模組的執行,這是 3 個東西啊,別混了。
// 1.js 中的程式碼
require([], functionA() {
// 主要邏輯程式碼
})
複製程式碼
- 檔案的載入:將
<script src='1.js'>
節點插入dom中,之後,下載 1.js 檔案; - 載入後檔案的執行:1.js 檔案載入完後,執行 1.js 中的程式碼,即執行 require() 函式!!!
- 模組的執行: require回撥函式,上方的,主要邏輯程式碼,所在的函式,functionA,的執行!!!
- 檔案載入/檔案執行 順序: 1.js , 2.js , 3.js;
- 模組執行 順序:3.js , 2.js , 1.js;
webpack對比requirejs
- webpack在管理模組的時候不需要再封裝一層像requireJS如下的東西
define(['jquery'], function(jquery){})
複製程式碼
- 它實現了前端程式碼模組化,提高了程式碼的複用性,從而提供公共模組的快取功能。
- webpack通過打包,不同頁面單獨載入自己的模組的javascript 和 common javascript,而requireJS將所有的javascript檔案打包成一個檔案,使得一個站點中多個頁面之間公用的JS模組無法快取。
- Webpack 引入了切分點(split point)與程式碼塊(Chunk),切分點定義了所有依賴的模組,合起來就是一個程式碼塊,從而實現一個頁面引用一個程式碼塊。
主流框架Vue
vue2中的diff演算法是怎樣實現的?
- 參考:讓虛擬DOM和DOM-diff不再成為你的絆腳石
- 當資料發生改變時,set方法會讓呼叫Dep.notify通知所有訂閱者Watcher,訂閱者就會呼叫patch給真實的DOM打補丁,更新相應的檢視。
diff流程
- patch函式接收兩個引數oldVnode和Vnode分別代表新的節點和之前的舊節點
- 判斷兩節點是否值得比較,值得比較則執行patchVnode;
- 不值得比較則用Vnode替換oldVnode;
- patchVnode:當我們確定兩個節點值得比較之後我們會對兩個節點指定patchVnode方法;
- 找到對應的真實dom,稱為el;
- 判斷Vnode和oldVnode是否指向同一個物件,如果是,那麼直接return;
- 如果他們都有文字節點並且不相等,那麼將el的文字節點設定為Vnode的文字節點;
- 如果oldVnode有子節點而Vnode沒有,則刪除el的子節點;
- 如果oldVnode沒有子節點而Vnode有,則將Vnode的子節點真實化之後新增到el;
- 如果兩者都有子節點,則執行updateChildren函式比較子節點,這一步很重要;
- updateChildren函式圖解
- 現在分別對oldS、oldE、S、E兩兩做sameVnode比較,有四種比較方式,當其中兩個能匹配上那麼真實dom中的相應節點會移到Vnode相應的位置,這句話有點繞,打個比方:
- 如果是oldS和E匹配上了,那麼真實dom中的第一個節點會移到最後;
- 如果是oldE和S匹配上了,那麼真實dom中的最後一個節點會移到最前,匹配上的兩個指標向中間移動;
- 如果四種匹配沒有一對是成功的,那麼遍歷oldChild,S挨個和他們匹配,匹配成功就在真實dom中將成功的節點移到最前面,如果依舊沒有成功的,那麼將S對應的節點插入到dom中對應的oldS位置,oldS和S指標向中間移動。
vue雙向資料繫結
- vue.js 是採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥。
- 第一步:需要observe的資料物件進行遞迴遍歷,包括子屬性物件的屬性,都加上 setter和getter。這樣的話,給這個物件的某個值賦值,就會觸發setter,那麼就能監聽到了資料變化;
- 第二步:compile解析模板指令,將模板中的變數替換成資料,然後初始化渲染頁面檢視,並將每個指令對應的節點繫結更新函式,新增監聽資料的訂閱者,一旦資料有變動,收到通知,更新檢視;
- 第三步:Watcher訂閱者是Observer和Compile之間通訊的橋樑,主要做的事情是:
- 1、在自身例項化時往屬性訂閱器(dep)裡面新增自己
- 2、自身必須有一個update()方法
- 3、待屬性變動dep.notice()通知時,能呼叫自身的update()方法,並觸發Compile中繫結的回撥,則功成身退。
- 第四步:MVVM作為資料繫結的入口,整合Observer、Compile和Watcher三者,通過Observer來監聽自己的model資料變化,通過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通訊橋樑,達到資料變化 -> 檢視更新;檢視互動變化(input) -> 資料model變更的雙向繫結效果。
細節點詢問
- 基於vue資料繫結,如果data中的資料進行了一秒1000次的改變,每次改變會全部顯示在頁面中嗎?
vue生命週期的執行過程
- 首先建立一個vue例項,Vue();
- 在建立Vue例項的時候,執行了init(),在init過程中首先呼叫了beforeCreate。
- Created之前,對data內的資料進行了資料監聽,並且初始化了Vue內部事件。具體如下:
- 完成了資料觀測;
- 完成了屬性和方法的運算;
- 完成了watch/event事件的回撥;
- 但是此時還未掛載dom上,$el屬性是不可見的;
- beforeMount之前,完成了模板的編譯。把data物件裡面的資料和vue的語法寫的模板編譯成了html,但是此時還沒有將編譯出來的html渲染到頁面;
- 1、在例項內部有template屬性的時候,直接用內部的,然後呼叫render函式去渲染。
- 2、在例項內部沒有找到template,就呼叫外部的html(“el”option(選項))。例項內部的template屬性比外部的優先順序高。 render函式 > template屬性 > 外部html;
- 3、要是前兩者都不滿足,那麼就丟擲錯誤。
- Mounted之前執行了render函式,將渲染出來的內容掛載到了DOM節點上。mounted是將html掛載到頁面完成後觸發的鉤子函式;當mounted執行完畢,整個例項算是走完了流程;在整個例項過程中,mounted僅執行一次;
- beforeUpdate:資料發生變化時,會呼叫beforeUpdate,然後經歷virtual DOM,最後updated更新完成;
- beforeDestory是例項銷燬前鉤子函式,銷燬了所有觀察者,子元件以及事件監聽;
- destoryed例項銷燬執行的鉤子函式;
總結
- beforeCreate:初始化了部分引數,如果有相同的引數,做了引數合併,執行 beforeCreate;el和資料物件都為undefined,還未初始化;
- created :初始化了 Inject 、Provide 、 props 、methods 、data 、computed 和 watch,執行 created ;data有了,el還沒有;
- beforeMount :檢查是否存在 el 屬性,存在的話進行渲染 dom 操作,執行 beforeMount;$el和data都初始化了,但是dom還是虛擬節點,dom中對應的資料還沒有替換;
- mounted :例項化 Watcher ,渲染 dom,執行 mounted ;vue例項掛載完成,dom中對應的資料成功渲染;
- beforeUpdate :在渲染 dom 後,執行了 mounted 鉤子後,在資料更新的時候,執行 beforeUpdate ;
- updated :檢查當前的 watcher 列表中,是否存在當前要更新資料的 watcher ,如果存在就執行 updated ;
- beforeDestroy :檢查是否已經被解除安裝,如果已經被解除安裝,就直接 return 出去,否則執行 beforeDestroy ;
- destroyed :把所有有關自己痕跡的地方,都給刪除掉;
Vue.js的template編譯
- 參考:Vue2 原理淺談
const ast = parse(template.trim(), options) // 構建抽象語法樹
optimize(ast, options) // 優化
const code = generate(ast, options) // 生成程式碼
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
複製程式碼
- 可以分成三部分
- 將模板轉化為抽象語法樹;
- 優化抽象語法樹;
- 根據抽象語法樹生成程式碼;
- 具體做了什麼
- 第一部分其實就是各種正則了,對左右開閉標籤的匹配以及屬性的收集,通過棧的形式,不斷出棧入棧去匹配以及更換父節點,最後生成一個物件,包含children,children又包含children的物件;
- 第二部分則是以第一部分為基礎,根據節點型別找出一些靜態的節點並標記;
- 第三部分就是生成render函式程式碼了
- 簡言之,就是先轉化成AST樹,再得到的render函式返回VNode(Vue的虛擬DOM節點);
- 回答:
- 首先,通過compile編譯器把template編譯成AST語法樹(abstract syntax tree 即 原始碼的抽象語法結構的樹狀表現形式),compile是createCompiler的返回值,createCompiler是用以建立編譯器的。另外compile還負責合併option。
- 然後,AST會經過generate(將AST語法樹轉化成render funtion字串的過程)得到render函式,render的返回值是VNode,VNode是Vue的虛擬DOM節點,裡面有(標籤名、子節點、文字等等);
資料到檢視的整體流程
- 在元件級別,vue會執行一個new Watcher;
- new Watcher首先會有一個求值的操作,它的求值就是執行一個函式,這個函式會執行render,其中可能會有編譯模板成render函式的操作,然後生成vnode(virtual dom),再將virtual dom應用到檢視中;
- 其中將virtual dom應用到檢視中(這裡涉及到diff後文會講),一定會對其中的表示式求值(比如{{message}},我們肯定會取到它的值再去渲染的),這裡會觸發到相應的getter操作完成依賴的收集;
- 當資料變化的時候,就會notify到這個元件級別的Watcher,然後它還會去求值,從而重新收集依賴,並且重新渲染檢視;
vue的computed和watch區別
- computed 是計算一個新的屬性,並將該屬性掛載到 vm(Vue 例項)上,而 watch 是監聽已經存在且已掛載到 vm 上的資料,所以用 watch 同樣可以監聽 computed 計算屬性的變化(其它還有 data、props)
- computed 本質是一個惰性求值的觀察者,具有快取性,只有當依賴變化後,第一次訪問 computed 屬性,才會計算新的值,而 watch 則是當資料發生變化便會呼叫執行函式
- 從使用場景上說,computed 適用一個資料被多個資料影響,而 watch 適用一個資料影響多個資料;
computed的原理,是如何和被計算的資料聯絡起來的
- 需要考慮
- 如何與其他的屬性建立聯絡的;
- 屬性改變後,如何通知計算屬性重新計算的;
- 初始化data時,會使用Object.defineProperty 對所有的屬性資料劫持;
- 初始化computed,會遍歷所有的computed,對每一個計算屬性會呼叫initComputed函式,生成watcher例項;
- watcher例項中會進行依賴收集;
- computed計算時:
- 會將當前的watcher例項賦給 Dep.target;
- 執行計算屬性的getter方法;
- 去讀取被計算的屬性時,就會觸發這些被計算屬性的相應getter方法。當判定被計算的屬性是計算屬性的相關依賴時,就會去建立依賴關係,既將計算屬性的watcher新增到這些被計算屬性的watcher內的訊息訂閱器dep中。
- 完成之後將Dep.target 賦為 null 並返回求值函式結果。
vue元件間的七種互動
- 1.props和$emit
- 父元件向子元件傳遞資料是通過prop傳遞的,子元件傳遞資料給父元件是通過$emit觸發事件來做到的。
- 2.特性繫結$attrs和$listeners
- 如果父元件A下面有子元件B,元件B下面有元件C,這時如果元件A想傳遞資料給元件C怎麼辦呢? 如果繼續用上面的方法,會變得非常複雜,不利於維護;Vue 2.4開始提供了$attrs和$listeners來解決這個問題,能夠讓元件A之間傳遞訊息給元件C。
- 3.中央事件匯流排 Events Bus
- 新建一個Vue事件bus物件,然後通過bus.$emit觸發事件,bus.$on監聽觸發的事件。
- 4.依賴注入:provide和inject
- 父元件中通過provider來提供變數,然後在子元件中通過inject來注入變數。
- 不論子元件有多深,只要呼叫了inject那麼就可以注入provider中的資料。而不是侷限於只能從當前父元件的prop屬性來獲取資料,只要在父元件的生命週期內,子元件都可以呼叫。
// 父元件 name: "Parent", provide: { for: "demo" }, components:{ childOne } // 子元件 name: "childOne", inject: ['for'], data() { return { demo: this.for } }, components: { childtwo } 複製程式碼
- 5.v-model
- 父元件通過v-model傳遞值給子元件時,會自動傳遞一個value的prop屬性,在子元件中通過this.$emit(‘input’,val)自動修改v-model繫結的值
- 子元件引用:ref和$refs
- 7.父鏈和子索引:$parent和$children
- 8.vue1中boradcast和dispatch
- vue1.0中提供了這種方式,但vue2.0中沒有,但很多開源軟體都自己封裝了這種方式,比如min ui、element ui和iview等。
- 9.vuex
元件之間通訊使用的設計模式?
- 目前還沒有答案~~~~(阿里電話面試的一個問題)
vuex原理
- 參考:深入vuex原理(上)
- 參考:Vuex 原始碼解析
- vuex的state是藉助vue的響應式data實現的。
// 使用 this.$store.getters.xxx 獲取 xxx 屬性時,實際上是獲取的store._vm.data.$$state 物件上的同名屬性
get state () {
return this._vm._data.$$state
}
// 處理state 和 getter的核心函式resetStoreVM(this, state)
store._vm = new Vue({
data: {
$$state: state
}
})
// 由於vue的data是響應式的,所以,$$state也是響應式的,那麼當我們 在一個元件例項中 對state.xxx進行 更新時,基於vue的data的響應式機制,所有相關元件的state.xxx的值都會自動更新,UI自然也會自動更新
複製程式碼
- getter是藉助vue的計算屬性computed特性實現的。
// wrappedGetters方法
const computed = {};
// 處理getters
forEachValue(wrappedGetters, (fn, key) => {
computed[key] = () => fn(store) // 將getter儲存在computed上
//this.$store.getters.XXX的時候獲取的是store._vm.XXX
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true
})
})
複製程式碼
- 其設計思想與vue中央事件匯流排如出一轍。中央事件匯流排的實現,簡單講就是新建了一個vue物件,藉助vue物件的特性(emit 與on) 作為其他元件的通訊橋樑,實現元件間的通訊 以及資料共享!
Vue-router 中hash模式和history模式的區別
- hash模式url裡面永遠帶著#號,我們在開發當中預設使用這個模式。那麼什麼時候要用history模式呢?如果使用者考慮url的規範那麼就需要使用history模式,因為history模式沒有#號,是個正常的url適合推廣宣傳。
- 當然其功能也有區別,比如我們在開發app的時候有分享頁面,那麼這個分享出去的頁面就是用vue或是react做的,我們們把這個頁面分享到第三方的app裡,有的app裡面url是不允許帶有#號的,所以要將#號去除那麼就要使用history模式,但是使用history模式還有一個問題就是,在訪問二級頁面的時候,做重新整理操作,會出現404錯誤,那麼就需要和後端人配合讓他配置一下apache或是nginx的url重定向,重定向到你的首頁路由上就ok啦。
- 路由的雜湊模式其實是利用了window可以監聽onhashchange事件,也就是說你的url中的雜湊值(#後面的值)如果有變化,前端是可以做到監聽並做一些響應(搞點事情),這麼一來,即使前端並沒有發起http請求他也能夠找到對應頁面的程式碼塊進行按需載入。
- pushState與replaceState,這兩個神器的作用就是可以將url替換並且不重新整理頁面,好比掛羊頭賣狗肉,http並沒有去請求伺服器該路徑下的資源,一旦重新整理就會暴露這個實際不存在的“羊頭”,顯示404。這就需要伺服器端做點手腳,將不存在的路徑請求重定向到入口檔案(index.html)。
自定義元件
- 參考:Vue——關於自定義元件
- secondDemoComponent.js
- index.js:index.js檔案幫我們把所有自定義的元件都通過Vue.component註冊了,最後export一個包含install方法的物件給Vue.use()使用。
- 統一匯入
import global from './components/global/index.js'
Vue.use(global)
複製程式碼
計算機網路
http 狀態碼的分類?什麼是無狀態?
- http無狀態
- 伺服器中沒有儲存客戶端的狀態,客戶端必須每次帶上自己的狀態去請求伺服器;
- 每次的請求都是獨立的,<它的執行情況和結果>與<前面的請求>和<之後的請求>是無直接關係的
- 狀態碼分類
- 1XX指示資訊,表示資訊已接收,繼續處理;
- 2XX成功-表示請求已被成功接收;
- 3XX重定向,表示要完成請求進行更進一步操作;
- 4XX客戶端錯誤,請求依法錯誤或請求無法實現;
- 5XX伺服器端錯誤,伺服器未能實現合法請求;
- 舉例:
- 200,請求成功;
- 302:重定向;
- 304:上次請求後頁面未修改;
- 400:客戶端請求語法錯誤;
- 401:許可權問題;
- 403,伺服器接受請求,但拒絕提供服務;
- 404,請求資源未存在;
- 500,伺服器內部錯誤;
http 1.1 與 http 2 的區別
- HTTP/2採用二進位制格式而非文字格式
- 比起像HTTP/1.x這樣的文字協議,二進位制協議解析起來更高效、“線上”更緊湊,更重要的是錯誤更少。
- HTTP/2是完全多路複用的,而非有序並阻塞的——只需一個連線即可實現並行
- HTTP/1.x 有個問題叫線端阻塞(head-of-line blocking), 它是指一個連線(connection)一次只提交一個請求的效率比較高, 多了就會變慢。 HTTP/1.1 試過用流水線(pipelining)來解決這個問題, 但是效果並不理想(資料量較大或者速度較慢的響應, 會阻礙排在他後面的請求). 此外, 由於網路媒介(intermediary )和伺服器不能很好的支援流水線, 導致部署起來困難重重。而多路傳輸(Multiplexing)能很好的解決這些問題, 因為它能同時處理多個訊息的請求和響應; 甚至可以在傳輸過程中將一個訊息跟另外一個摻雜在一起。所以客戶端只需要一個連線就能載入一個頁面。
- 使用報頭壓縮,HTTP/2降低了開銷
- HTTP2通過gzip和compress壓縮頭部然後再傳送,同時客戶端和伺服器端同時維護一張頭資訊表,所有欄位都記錄在這張表中,這樣後面每次傳輸只需要傳輸表裡面的索引Id就行,通過索引ID就可以知道表頭的值了
- HTTP/2讓伺服器可以將響應主動“推送”到客戶端快取中
- HTTP2支援在客戶端未經請求許可的情況下,主動向客戶端推送內容
post和get請求的區別
基本區別
- 應用:表單的method屬性設定post時傳送的是post請求,其餘都是get請求(沒有考慮AJAX);
- 傳參方式:get請求通過url地址傳送請求引數,post請求通過請求體傳送請求引數;
- 安全性:get請求直接通過url地址傳送請求引數,引數在位址列可見,不太安全;post請求通過請求體傳送請求引數,引數在位址列不可見,相對安全;
- 大小限制:get請求直接通過url地址傳送請求引數,url地址的長度限制在255位元組內,所以get請求不能傳送過多的引數,post請求通過請求體傳送引數,長度沒有限制。
- Get方法提交的資料大小長度並沒有限制,而是IE瀏覽器本身對位址列URL長度(5.7備註:是瀏覽器對請求頭有限制,不能超過多少~~~)有最大長度限制:2048個字元。
高階區別
- GET 的本質是「得」,而 POST 的本質是「給」。GET 的內容可以被瀏覽器快取,而 POST 的資料不可以。
- 1.get產生一個TCP資料包,一個是請求頭,一個請求體的;post產生兩個TCP資料包;
- 2.在一次請求中,get一次性完成,post在部分瀏覽器(除了火狐)需要傳送兩次資訊,所以get比post更快,更有效率。
什麼是跨域,解決跨域的方法及原理是什麼?
- 1.不同源就是跨域
- 2.同源策略是瀏覽器的一種安全策略
- 3.協議,域名,埠號完全相同就是同源,只要有一處不一樣就是跨域
- 4.特例: ajax在判斷域名的時候只能解析字串,導致(localhost和127.0.0.1)在它看來也是跨域請求
- 5.解決跨域的方式通常用cors和jsonp
- 6.JSONP
- 1.JSONP是一種技巧,不是一門新的技術
- 2.利用scirpt標籤的src屬性不受跨域的限制的特點
- 3.解決跨域:
- 1.瀏覽器端:動態生成script標籤,提前定義好回撥函式,在合適的時機新增src屬性指定請求的地址。
- 2.伺服器端:後臺接收到回撥函式,將資料包括在回撥函式呼叫的控制程式碼中,一起返回。
- 3.只支援get請求
function jsonp({url,params,callback}){
return new Promise((resolve,reject)=>{
// 建立srcipt
let script = document.createElement("script")
window[callback] = function(data){
resolve(data)
document.body.removeChild(script)
}
// 引數重新格式化
params = {...params,callback} // wd=b&callback=show
let arrs = []
for(let key in params){
arrs.push(`${key}=${params[key]}`)
}
// 後臺獲取資料的介面拼接上引數
script.src = `${url}?${arrs.join('&')}`
// srcipt插入
document.body.appendChild(script)
})
}
jsonp({
url: 'http://localhost:3000/say',
params: { wd: 'Iloveyou' },
callback: 'show'
}).then(data=>{
console.log(databufen)
})
複製程式碼
- 7.CORS
- 1.瀏覽器端什麼也不用幹;
- 2.伺服器端設定響應頭:Access-Control-Allow-Origin
- 3.cors是一門技術,在本質上讓ajax引擎允許跨域
- 4.get和post請求都支援
為什麼cros能解決跨域?// TODO
- 和第一次傳送的option請求有關;
- 跨域時,瀏覽器會攔截Ajax請求,並在http頭中加Origin。
瀏覽器問題
瀏覽器快取
客戶端兩種儲存
sessionStorage用法和localStorage區別
- 生命週期:
- localStorage(本地儲存)生命週期是永久,這意味著除非使用者顯示在瀏覽器提供的UI上清除localStorage資訊,否則這些資訊將永遠存在。
- sessionStorage(會話儲存)生命週期為當前視窗或標籤頁,一旦視窗或標籤頁被關閉了,那麼所有通過sessionStorage儲存的資料也就被清空了。
- 作用域不同:不同瀏覽器無法共享localStorage或sessionStorage中的資訊。
- 相同瀏覽器的不同頁面間可以共享相同的 localStorage(頁面屬於相同域名和協議,主機和埠);
- 不同頁面或標籤頁間無法共享sessionStorage的資訊。
- localStorage只要在相同的協議、相同的主機名、相同的埠下,就能讀取/修改到同一份localStorage資料。
- sessionStorage比localStorage更嚴苛一點,除了協議、主機名、埠外,還要求在同一視窗(也就是瀏覽器的標籤頁)下。
- 儲存大小:localStorage和sessionStorage的儲存資料大小一般都是:5MB;
- 儲存位置:localStorage和sessionStorage都儲存在客戶端,不與伺服器進行互動通訊。
- 儲存內容型別:localStorage和sessionStorage只能儲存字串型別,對於複雜的物件可以使用ECMAScript提供的JSON物件的stringify和parse來處理;
- 獲取方式:localStorage:window.localStorage;sessionStorage:window.sessionStorage;
- 應用場景:localStoragese:常用於長期登入(+判斷使用者是否已登入),適合長期儲存在本地的資料;sessionStorage:敏感賬號一次性登入。
localStorage和cookie選擇使用的標準是什麼
- 應該是和服務端有關,
- cookie每次的傳送請求是被預設帶上的,但是東西存在localstorage是沒有帶上的,需要寫進去;
session的生命週期為什麼只在一次會話中?
- 至今沒有詳細的答案~~當時的回答如下:
- session是服務端的;它用來和客戶端的cookie進行匹配,說它一次會話儲存的意思是sessionID每次會話都是一個新的,但是session一直是有的。
Cookie儲存和Web Storage儲存區別
- localStorage與sessionStorage作為新時代的產物,相比舊時代的cookie有其巨大的優越性。優越性有三:
- 其一在能儲存的資料量,cookie最大能儲存4kb的資料,而localStorage與sessionStorage最大能儲存5Mb,目前各大瀏覽器支援的標準都是如此;
- 其二在功能上,cookie只能儲存String型別的資料,以往要將使用者資料儲存在本地,需要將資料拼接成字串,再存進cookie,取資料的時候同樣麻煩,先將整個cookie物件拿到(String物件),再按拼接的規則拆分,再拿需要的資料,存取都很麻煩! localStorage與sessionStorage不僅支援傳統的String型別,還可以將json物件儲存進去,存取資料都方便不少,json的優越性就不贅述,localStorage與sessionStorage無疑更現代化;
- 其三是cookie是不可或缺的,cookie的作用是與伺服器進行互動,作為http規範的一部分而存在;而web storage僅僅是為了在本地‘儲存’而生;
- 其四在語義層面上,localStorage與sessionStorage語法更優雅、簡便。
//cookie的操作 設定cookie: document.cookie = 'key=value'; 獲取cookie: document.cookie; 刪除cookie: document.cookie = "key=value;max-age=0"; 設定max-age儲存期限: document.cookie = "key=value;max-age=1000"; // 1000秒 //web storage操作 儲存資料 setItem(key,value) 讀取資料 getItem(key) 刪除單個資料 removeItem(key) 清空全部資料 clearItem() 獲取資料索引 key(index) 複製程式碼
瀏覽器核心?以及常見的瀏覽器相容問題
- 瀏覽器核心
- Trident(IE核心):以IE為代表,IE6、IE7、IE8(Trident 4.0)、IE9(Trident 5.0)、IE10(Trident 6.0)。
- Gecko(Firefox核心): Gecko 核心的瀏覽器還是Firefox (火狐) 使用者最多,所以有時也會被稱為Firefox核心。
- webkit(Safari核心,Chrome核心原型,開源):它是蘋果公司自己的核心,也是蘋果的Safari瀏覽器使用的核心。 Apple Safari也會用。
- presto(Opera前核心) (已廢棄): Opera12.17及更早版本曾經採用的核心,現已停止開發並廢棄。Opera現已改用Google Chrome的Blink核心。
常見的瀏覽器相容問題
HTML中的相容問題
- 不同瀏覽器的標籤預設的外補丁和內補丁不同;
- 場景:隨便寫幾個標籤,不加樣式控制的情況下,各自的margin 和padding差異較大。
- 解決方法:上來先消除預設樣式*{margin:0;padding:0;};
- 塊屬性標籤float後,又有橫行的margin情況下,在IE6顯示margin比設定的大(即雙倍邊距bug);
- 場景:常見症狀是IE6中後面的一塊被頂到下一行;
- 解決方法:在float的標籤樣式控制中加入 display:inline;將其轉化為行內屬性
- IE6中 z-index失效
- 場景:元素的父級元素設定的z-index為1,那麼其子級元素再設定z-index時會失效,其層級會繼承父級元素的設定,造成某些層級調整上的BUG;
- 原因:z-index起作用有個小小前提,就是元素的position屬性要 是relative,absolute或是fixed。
- 解決方案:1.position:relative改為position:absolute;2.去除浮動;3.浮動元素新增position屬性(如relative,absolute等)。
- 在寫a標籤的樣式,寫的樣式沒有效果,其實只是寫的樣式被覆蓋了
- 正確的a標籤順序應該:link/ visited/hover/active
- 24位的png圖片,ie6中不相容透明底兒
- 解決方式:使用png透明圖片唄,但是需要注意的是24位的PNG圖片在IE6是不支援的,解決方案有兩種:
- 使用8位的PNG圖片;
- 為IE6準備一套特殊的圖片
- 解決方式:使用png透明圖片唄,但是需要注意的是24位的PNG圖片在IE6是不支援的,解決方案有兩種:
js在不同瀏覽器中的相容問題
- 事件監聽的相容;
- IE不支援addEventListener;
- 解決:給IE使用attachEvent
var addHandler = function(el, type, handler, args) {
if (el.addEventListener) {
el.addEventListener(type, handler, false);
} else if (el.attachEvent) {
el.attachEvent('on' + type, handler);
} else {
el['on' + type] = handler;
}
};
var removeHandler = function(el, type, handler, args) {
if (el.removeEventListener) {
el.removeEventListener(type, handler, false);
} else if (el.detachEvent) {
el.detachEvent('on' + type, handler);
} else {
el['on' + type] = null;
}
};
複製程式碼
- event.target的相容,引發事件的DOM元素。
- IE6789不支援event.target;
- 解決方法:event.srcElement;
// 以下為相容寫法
target = event.target || event.srcElement;
複製程式碼
- 阻止系統預設的相容
- IE6789不支援event.preventDefault;
// 以下為相容寫法
event.preventDefault ? event.preventDefault() : (event.returnValue = false);
複製程式碼
- 阻止事件冒泡的相容
- IE6789不支援event.stopPropagation;
// 以下為相容寫法
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = false);
複製程式碼
業務問題
360搜尋圖片這種的瀑布流佈局
圖片懶載入
- 高逼格方法:getBoundingClientRect()直接獲取元素到頂部的距離。
- 原因:在頁面中我們往往需要放上很多張圖,效能是個大問題,一次性載入所有圖片一般都會卡很久,因此,在需要預覽的時候再載入,是一個很好的解決方案。
- 思路:當圖片出現在瀏覽器可視區域中時,再把圖片的url傳給它,也可以在這個時候建立圖片,而圖片被包裹在一個容器中,比如li或div,圖片的url放在其容器的自定義屬性data-src中;
- 過程:
- 給img標籤的元素新增一個自定義屬性,比如data-src,而真正的src設定為空;
- 判斷圖片距離頁面頂端的距離小於瀏覽器滾動距離加上可視區域高度,即它出現在可視區域時就載入他;
// h = window.innerHeight——可視區域高度 // s = document.documentElement.scrollTop || document.body.scrollTop——滾動距離; // getTop(imgs[i])通過getBoundingClientRect()獲取當前圖片距離頂端的高度 if (h + s > getTop(imgs[i]) && !imgs[i].getAttribute('src')) 複製程式碼
- 載入圖片
imgs[i].src = imgs[i].getAttribute('data-src');
- 最後在把頁面滾動函式賦值給元素window.onload,在所有元素載入完以後再進行操作,這一步很重要!
window.onload = window.onscroll = function () { // 懶載入的函式 lazyLoad(imgs); } 複製程式碼
專案中遇見的問題
ajax中使用get的問題
- 問題一. 快取:當每次訪問的url相同,客戶端直接讀取本地快取裡面的內容,即使後臺資料變化前臺也不會有變化;
- 解決方法:在?後面連結一個num=【隨機數Math.random()】或者num=【時間戳new Date().getTime()】,'1.php?username="May"&'+num(這裡沒有變數名,避免和後臺引數衝突)
效能優化
- 雪碧圖
- 減少dom操作:事件代理,fragment;
- 壓縮js和css,html;
主流框架問題
vue、react和angular區別
- 借鑑angular的模板和資料繫結技術;
- 借鑑react的元件化和虛擬DOM技術;
- 體積下, 執行效率高, 編碼簡潔, PC/移動端開發都合適;
- 它本身只關注UI, 可以輕鬆引入vue外掛和其它第三庫開發專案;
編碼題(位元組跳動的視訊面試)
- 第一題:定義一個Queue類,第一次呼叫可以隔1000秒輸出1,,第二次呼叫可以隔2000秒輸出2...最後可以呼叫run();
new Queue()
.task(() => {
console.log(1)
}, 1000) .task(() => {
console.log(2)
}, 2000) .task(() => {
console.log(3)
}, 3000) .run()
複製程式碼
- 第二題:定義一個函式,求和函式,可以無數次呼叫,並且在最後呼叫valueOf顯示最終求和結果;
sum(1)(2)(3)(4).valueOf()
複製程式碼
- 第三題:第一步Promise+axios實現併發請求;寫完之後,要求再在此基礎上實現每次都併發5個請求(不會~~~);
// Promise+axios實現併發請求
// 實現併發5次請求
// 第一個答案
function getRequest(){
return axios.get(url)
};
function postrequest(){
return axios.post(url)
};
axios.all([getRequest(),postrequest()])
.then()
複製程式碼
- 最近面試的一個整理情況,有些是面試中被問到的,有些是看文章時遇到感覺很有可能會問到的。有的有答案,有的還沒有答案,沒答案的原因有兩種的:一種是常見的問題,希望能閒下來之後詳細整理一下更高逼格的答案;另一種是目前還沒有解決,但是會在面試告一段落之後再研究研究給出答案,也特別歡迎大家在留言上給出這些題的答案或者思路。
- 有同學在評論中說了感覺題比較簡單,我想說,看到好多題的時候我也是這麼感覺得,但是,可能後面就會往深層次問你了。
- 比如:new或者instanceof的實現原理?第二問就是:請手寫一個new或者instanceof;再比如,cookie和session區別,第二問就是:從服務端的角度來說一下cookie和session區別把?然後:為什麼session只在一次回話有效,他是伺服器的一個欄位嗎?還是伺服器的什麼?再來,瞭解vue元件通訊嗎?第二問就是:請說出這些元件通訊的設計模式用了哪些?最後,用過npm和yarn嗎?第二問:你們是如何解決npm包的版本依賴的?package-lock的原理是什麼?
- 我想說的是,文字能呈現的東西很簡單,但是想深究的話,深無底線~~~ 這些很大的目的是作個記錄,可以在面試上樓前再瞜一眼~~~ 工程師們在工作中一定要時刻保持可以離開的資本啊,知其然並且知其所以然!另外,面試前一定一定要準備,至少一週啦~~~~