摘要:最近的一次更新的變量有效,并且會作用于全部的引用的處理方式和相同,變量值輸出時根據之前最近的一次定義計算,每次引用最近的定義有效嵌套三種預編譯器的選擇器嵌套在使用上來說沒有任何區別,甚至連引用父級選擇器的標記也相同。
面試匯總一:2018大廠高級前端面試題匯總
高級面試:【半月刊】前端高頻面試題及答案匯總
css內容 響應式布局 當前主流的三種預編譯器比較CSS預處理器用一種專門的編程語言,進行Web頁面樣式設計,然后再編譯成正常的CSS文件,以供項目使用;
讓你的CSS更加簡潔、適應性更強、可讀性更佳,更易于代碼的維護等諸多好處。
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區別與選擇
"CSSurl路徑" rel="stylesheet" type="text/css" /
link功能較多,可以定義 RSS,定義 Rel 等作用,而@import只能用于加載 css;
當解析到link時,頁面會同步加載所引的 css,而@import所引用的 css 會等到頁面加載完才被加載;
@import需要 IE5 以上才能使用;
link可以使用 js 動態引入,@import不行;
垂直居中布局css經典布局系列一——垂直居中布局
css3的高級特性舉例 h5新增了哪些內容或者API,使用過哪些? 1像素邊框問題 什么是BFCBFC 英文解釋: block formatting context。中文意思:塊級格式化上下文;
formatting context 意思是:頁面中一個渲染區域,有自己的一套渲染規則,決定其子元素如何定位,以及和其他兄弟元素的關系和作用。
BEC有如下特性
內部的box會在垂直方向,從頂部開始一個接一個地放置;
box垂直方向的距離由margin決定。屬于同一個BFC的兩個相鄰的box的margin會發生折疊;
每一個元素的margin box的左遍,與包含塊border box的左邊相接觸。即使浮動元素也是如此;
BFC區域不會與float box疊加;
計算 BFC 的高度時,浮動子元素也參與計算;
文字層不會被浮動層覆蓋,環繞于周圍
float 除了none以外的值;
overflow 除了visible 以外的值(hidden,auto,scroll );
display (table-cell,table-caption,inline-block, flex, inline-flex);
position值為(absolute,fixed) 這些屬性值得元素都會自動創建 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!!!
2.各個js文件的加載時機(script標簽插入文檔的時機順序)
比如: 1.js 依賴 2.js,2.js 依賴 3.js;實際的加載順序是為 1.js,2.js,3.js
注意: 而實際模塊運行的順序,才是 3.js,2.js,1.js。所以,文件的加載、加載后文件的運行、模塊的運行,這是 3 個東西啊,別混了。
3.文件模塊的執行時機
// 1.js 中的代碼
require([], functionA() {
// 主要邏輯代碼
})
文件的加載:將節點插入dom中,之后,下載 1.js 文件;
加載后文件的運行:1.js 文件加載完后,執行 1.js 中的代碼,即執行 require() 函數!!!
模塊的運行: require回調函數,上方的,主要邏輯代碼,所在的函數,functionA,的運行!!!
文件加載/文件運行 順序: 1.js , 2.js , 3.js;
模塊運行 順序:3.js , 2.js , 1.js;
webpack對比requirejswebpack在管理模塊的時候不需要再封裝一層像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打補丁,更新相應的視圖。
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請求都支持
和第一次發送的option請求有關;
跨域時,瀏覽器會攔截Ajax請求,并在http頭中加Origin。
瀏覽器問題 瀏覽器緩存前端優化:瀏覽器緩存技術介紹
客戶端兩種存儲生命周期:
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:敏感賬號一次性登錄。
應該是和服務端有關,
cookie每次的發送請求是被默認帶上的,但是東西存在localstorage是沒有帶上的,需要寫進去;
至今沒有詳細的答案~~當時的回答如下:
session是服務端的;它用來和客戶端的cookie進行匹配,說它一次會話保存的意思是sessionID每次會話都是一個新的,但是session一直是有的。
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內核。
不同瀏覽器的標簽默認的外補丁和內補丁不同;
場景:隨便寫幾個標簽,不加樣式控制的情況下,各自的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準備一套特殊的圖片
事件監聽的兼容;
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 ");false);
阻止事件冒泡的兼容
IE6789不支持event.stopPropagation;
// 以下為兼容寫法 event.stopPropagation ");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);
}
項目中遇見的問題
問題一. 緩存:當每次訪問的url相同,客戶端直接讀取本地緩存里面的內容,即使后臺數據變化前臺也不會有變化;
解決方法:在?后面鏈接一個num=【隨機數Math.random()】或者num=【時間戳new Date().getTime()】,"1.php");
性能優化
雪碧圖
減少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的原理是什么?
我想說的是,文字能呈現的東西很簡單,但是想深究的話,深無底線~~~ 這些很大的目的是作個記錄,可以在面試上樓前再瞜一眼~~~ 工程師們在工作中一定要時刻保持可以離開的資本啊,知其然并且知其所以然!另外,面試前一定一定要準備,至少一周啦~~~~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/6735.html
摘要:一些知識點有哪些方法方法前端從入門菜鳥到實踐老司機所需要的資料與指南合集前端掘金前端從入門菜鳥到實踐老司機所需要的資料與指南合集歸屬于筆者的前端入門與最佳實踐。 工欲善其事必先利其器-前端實習簡歷篇 - 掘金 有幸認識很多在大廠工作的學長,在春招正式開始前為我提供很多內部推薦的機會,非常感謝他們對我的幫助。現在就要去北京了,對第一份正式的實習工作也充滿期待,也希望把自己遇到的一些問題和...
TCP/IP HTTP和HTTPS有何區別? httpbin 一個簡單的HTTP請求和響應服務。 TCP的三次握手與四次揮手 通俗易懂版,詳細版本 MySQL CHAR和VARCHAR存取的差別 《高性能MySQL》筆記 - MySQL 鎖的基本類型 MySQL中的鎖之一:鎖的必要性及分類 MySQL中的鎖之二:行鎖、頁鎖、表鎖 MySQL Like與Regexp的區別 數據結構 數...
TCP/IP HTTP和HTTPS有何區別? httpbin 一個簡單的HTTP請求和響應服務。 TCP的三次握手與四次揮手 通俗易懂版,詳細版本 MySQL CHAR和VARCHAR存取的差別 《高性能MySQL》筆記 - MySQL 鎖的基本類型 MySQL中的鎖之一:鎖的必要性及分類 MySQL中的鎖之二:行鎖、頁鎖、表鎖 MySQL Like與Regexp的區別 數據結構 數...
閱讀 3558·2021-11-22 15:11
閱讀 4633·2021-11-18 13:15
閱讀 2702·2019-08-29 14:08
閱讀 3576·2019-08-26 13:49
閱讀 3090·2019-08-26 12:17
閱讀 3287·2019-08-26 11:54
閱讀 3110·2019-08-26 10:58
閱讀 2030·2019-08-26 10:21