前端面試百問

臭豆腐蛋發表於2020-11-21

請問websocket如何相容低瀏覽器的

websocket是H5開始提供的一種在單個 TCP 連線上進行全雙工通訊的協議。

WebSocket 使得客戶端和伺服器之間的資料交換變得更加簡單,允許服務端主動向客戶端推送資料。在 WebSocket API 中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以建立永續性的連線,並進行雙向資料傳輸。

對於低版本的瀏覽器我們可以使用下面幾種方法對低版本瀏覽器進行相容。

  1. 引入SockJS庫,他是JavaScript的一個庫,支援websocket的瀏覽器會優先使用原生的websorcket,如果不支援,則會使用引用的庫檔案。
  2. 引用socket.IO的庫檔案,這同樣是基於時間的雙向通訊,如何不支援則會使用替代的方案。

Vue單頁面的SEO解決方案

方法:用prerender-spa-plugin結合vue-meta-info來實現首頁的seo

首先就是安裝外掛:

npm install prerender-spa-plugin vue-meta-info --save

然後在專案的目錄下找到檔案build/webpack.prod.conf.js,新增如下程式碼:

const PrerenderSpaPlugin = require('prerender-spa-plugin')

new PrerenderSpaPlugin(
  //將渲染的檔案放到dist目錄下
      path.join(__dirname, '../dist'),
      //需要預渲染的路由資訊
      [ '/index','/about' ],
      {
      //在一定時間後再捕獲頁面資訊,使得頁面資料資訊載入完成
        captureAfterTime: 50000,
        //忽略打包錯誤
        ignoreJSErrors: true,
        phantomOptions: '--web-security=false',
        maxAttempts: 10,
      },
    )

描述圖片懶載入的方案,並對其原理進行簡單的描述

圖片懶載入是前端頁面優化的一種方式,在頁面中有很多圖片的時候,圖片載入就需要很多時間,很耗費伺服器效能,不僅影響渲染速度還會浪費頻寬,為了解決這個問題,提高使用者體驗,所以就出現了懶載入這種方式來減輕伺服器的壓力,優先載入可視區域的內容,其他部分等進入了可視區域再載入,從而提高效能。

思路:在圖片沒有進入可視區域時,先不給的src賦值,這樣瀏覽器就不會傳送請求了,等到圖片進入可視區域再給src賦值。圖片的真實地址需要儲存在data-src中。圖片沒有進入可視區域,也就是說圖片的offsetTop需要小於頁面的可視高度,但想一想,當圖片在頁面的下方的時候呢,需要頁面滾動了一段距離之後才能看到圖片,所以這裡需要滿足img.scrollTop < 頁面的可視區域高度+頁面滾動的高度,這裡是實現圖片懶載入的關鍵,接下來看具體程式碼的實現

<img src="loading.gif" data-src="1.jpg" alt="">
<img src="loading.gif" data-src="2.jpg" alt="">
<img src="loading.gif" data-src="3.jpg" alt="">
<img src="loading.gif" data-src="4.jpg" alt="">
<img src="loading.gif" data-src="5.jpg" alt="">
<img src="loading.gif" data-src="6.jpg" alt="">
<img src="loading.gif" data-src="7.jpg" alt="">
<img src="loading.gif" data-src="8.jpg" alt="">
<img src="loading.gif" data-src="9.jpg" alt="">
<img src="loading.gif" data-src="10.jpg" alt="">
......

*{
	margin: 0;
	padding: 0;
}
img{
	vertical-align: top;
	width: 100%;
	height: auto;
}

let img = document.getElementsByTagName('img');
let n = 0; //儲存圖片載入到的位置,避免每次都從第一張圖片開始遍歷
lazyload(); 
window.addEventListener('scroll',lazyload);
function lazyload(){  //監聽頁面滾動事件
	var seeHeight = window.innerHeight;  //可見區域高度
	var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
	for(let i = n; i < img.length; i++){
		if(img[i].offsetTop < seeHeight + scrollTop){
			if(img[i].getAttribute("src") == 'loading.gif'){
				img[i].src = img[i].getAttribute("data-src");
			}
			n = i + 1;
		}
	}
}

請問浮動元素引起的問題和解決方法

浮動元素引起的問題:

  1. 由於浮動元素已脫離文件流,所以父元素無法被撐開,影響與父級元素同級的元素。
  2. 與浮動元素同級的非浮動元素(內聯元素)會跟隨其後,也是由於浮動元素脫離文件流,不佔據文件流中額位置。
  3. 如果該浮動元素不是同級第一個浮動的元素,則它之前的元素也應該浮動,否則容易影響頁面的結構顯示。

如何清楚浮動:

  1. 使用css樣式中的clear:both
  2. 給父元素新增clearfix樣式
.clearfix:after{
    content: ".";       
    display: block;  
    height: 0;  
    clear: both;  
    visibility: hidden;
} 

.clearfix{
    display: inline-block; 
} /* for IE/Mac */

跨域的原因與解決方式

跨域是指瀏覽器的不執行其他網站指令碼的,由於瀏覽器的同源策略造成,是對JavaScript的一種安全限制.說白點理解,當你通過瀏覽器向其他伺服器傳送請求時,不是伺服器不響應,而是伺服器返回的結果被瀏覽器限制了.

跨域的解決方案

JSONP方式

缺點: get請求,前後端都要修改

優點:無相容問題(js哪個瀏覽器不相容 站出來)

思路:利用js儲存全域性變數來傳遞資料,因為js不受同源策略控制,script標籤載入機制html

反向代理,ngixn

你在工作中如何優化專案程式碼,請舉例說明.

深拷貝和淺拷貝的區別,請你實現深拷貝的幾種方法?

深拷貝和淺拷貝最根本的區別在於是否真正獲取一個物件的複製實體,而不是引用。

淺複製:僅僅是指向被複制的記憶體地址,如果原地址發生改變,那麼淺複製出來的物件也會相應的改變。

深複製:在計算機中開闢一塊新的記憶體地址用於存放複製的物件。

實現深拷貝:

let obj={
    name:'zs',
    age:18
}
 
let deepCloneObj=JSON.parse(JSON.stringify(obj))
deepCloneObj.name=28;
console.log(obj.age);//18

優點:優點是方便快捷,效能相對比較好;

缺點:但是複雜的物件進行JSON轉換有可能會丟失屬性.

let obj={
    name:'zs',
    main:{
        a:1,
        b:2
    },
  fn:function(){},
  friends:[1,3,5,7,9]
}
 
function copy(obj){
    let newObj=null;
    if(typeof(obj)=='object'&&obj!==null){
        newObj=obj instanceof Array ? []:{};
        for(var i in obj){
            newObj[i]=copy(obj[i])
        }
    }else{
        newObj=obj
    }
    return newObj
}
var obj2 = copy(obj)
obj2.name = '修改成功'
obj2.main.a = 100
console.log(obj,obj2)

請你實現快速排序演算法

function quickSort(arr) {
   /*
    * 建立len儲存陣列的長度,每次獲取陣列的長度都要實時查詢不利於效能;
    * index作為儲存取到的中間值;
    * pivot儲存比較參照物;
    * left、right作為子陣列的容器;
    */
    var len = arr.length,
        index,
        pivot,
        left=[],
        right=[];
    // 如果陣列只有一位,就直接返回陣列,遞迴的終止條件;
    if (len <= 1) return arr;

    //獲取中間值的索引,使用Math.floor向下取整;
    index = Math.floor(len / 2);

    // 使用splice擷取中間值,第一個引數為擷取的索引,第二個引數為擷取的長度;
    // 如果此處使用pivot=arr[index]; 那麼將會出現無限遞迴的錯誤;
    // splice影響原陣列,原陣列長度減一;
    pivot = arr.splice(index, 1);
    len -= 1;

    // 小於arr[pivot]的存到left陣列裡,大於arr[pivot]的存到right陣列;
    for (var i = 0; i < len; i++) {
        if (pivot > arr[i]) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    // 不斷把分割的左右子陣列傳入quickSort,直到分割的只有一位直接返回子陣列本身,遞迴終止;

    // 把每次分割的陣列一層一層的用concat連線起來;
    // 每一層left裡的元素都小於對應的pivot,right裡的元素都大於對應的pivot;
    return quickSort(left).concat(pivot, quickSort(right));
}

如何判斷JS變數的資料型別

  1. typeof
1 // 特殊的基本資料型別判斷
2 typeof null // 'object'
3 // 特殊的引入資料型別判斷
4 typeof function () {} // 'function'
5 typeof new Function() // 'function'

  1. instanceof
1 function Car () {} // 定義一個建構函式
2 console.log(new Car() instanceof Car)
3 console.log({} instanceof Object) // true
4 console.log([] instanceof Array) // true
5 console.log(new String('') instanceof String) // true
6 console.log('' instanceof String) // false
Object.prototype.toString.call(null) // [object Null]
Object.prototype.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call(1) // [object Number]
Object.prototype.toString.call(new Number(1)) // [object Number]
Object.prototype.toString.call('a') // [object String]
Object.prototype.toString.call(new String('a')) // [object String]

Object.prototype.toString.call({}) // [object Object]
Object.prototype.toString.call([]) // [object Array]
Object.prototype.toString.call(new Date()) // [object Date]
Object.prototype.toString.call(/^d$/g) // [object RegExp]
Object.prototype.toString.call(() => {}) // [object Function]
Object.prototype.toString.call(new Function()) // [object Function]

function Car () {} // 定義一個建構函式
Object.prototype.toString.call(new Car()) // [object Object]

rem要如何配置

rem是只相對於根元素htm的font-size,即只需要設定根元素的font-size,@media可以針對不同的螢幕尺寸設定不同的樣式

例如:

/*dpi*/
/* for 1080+ px width screen */
/* for 1080 px width screen */
/* for 800 px width screen */
/* for 800 px width screen */
@media only screen and (min-width: 751px) { html, body { font-size: 31.25px; } }
/* for 800 px width screen */
@media only screen and (max-width: 750px) { html, body { font-size: 31.25px; } }
/* for 720 px width screen */
@media only screen and (max-width: 720px) { html, body { font-size: 30px; } }
/* for 640 px width screen */
@media only screen and (max-width: 640px) { html, body { font-size: 27px; } }
/* for 540 px width screen */
@media only screen and (max-width: 540px) { html, body { font-size: 22.5px; } }
/* for 480 px width screen */
@media only screen and (max-width: 480px) { html, body { font-size: 20px; } }
/* for 450 px width screen */
@media only screen and (max-width: 450px) { html, body { font-size: 18.9px; } }
/* for 414 px width screen */
@media only screen and (max-width: 414px) { html, body { font-size: 17.25px; } }
/* for 375 px width screen */
@media only screen and (max-width: 375px) { html, body { font-size: 15.625px; } }
/* for 320 px width screen */
@media only screen and (max-width: 320px) { html, body { font-size: 13.5px; } }

vue中key的作用

key值大多情況下使用在迴圈語句中,從本質來講主要作用大概有以下兩點:

  1. 主要用在 Vue 的虛擬 DOM 演算法,在新舊 nodes 對比時辨識 VNodes,相當於唯一標識ID。
  2. Vue 會盡可能高效地渲染元素,通常會複用已有元素而不是從頭開始渲染, 因此使用key值可以提高渲染效率,同理,改變某一元素的key值會使該元素重新被渲染。

至少寫出三種減少頁面載入時間的方法

  1. 重複的HTTP請求數量應儘量減少
  • 減少呼叫其他頁面,檔案的數量
  • 精靈圖
  1. 壓縮JavaScript css程式碼
  2. 在檔案頭部配置css樣式的定義
  3. 在檔案末尾放javascript指令碼
  4. css,javascript改為外部呼叫
  5. 使用CDN網路加速
  6. 伺服器啟用Gzip壓縮功能
  7. ajax採用快取呼叫
  8. 縮減iframe的使用,如無必要儘量別使用
  9. 優化圖片檔案

路由導航(路由)守衛

全域性導航守衛

  • 全域性前置守衛router.beforeEach(fn)
  1. n中有三個引數
    • to:表示要去哪裡
    • from:表示從哪裡來
    • next:表示通不通過
  • 關於next的使用

    • next() 等價於 next( true ) 表示可以從當前路由跳轉到目標路由
    • next( false ) 表示不通過, 表示從當前路由跳轉不到目標路由
    • next(’/login’) 等價於 next({path:’/login’}) 跳轉指定的路由
    • next(’/login’) 等價於 next({path:’/login’,params,query})
    • next( fn ) 資料預載
    
    // 全域性前置路由守衛
    router.beforeEach( (to,from,next) =>{
        const name = localStorage.getItem('name');//檢視本地儲存上是否有name物件
        if( name || to.path === '/login'){//短路邏輯,有就可以繼續執行,沒有就跳轉到登入頁面
            next()
        }else{
         next( '/login' )//跳轉登入頁面
        }
    })
    
    
    // 全域性後置路由守衛 router.afterEach(fn),表示離開當前路由時執行
    router.afterEach( (to,from,next)=> {
        if(to.path === '/user'){//當to的path值等於這個時
            alert('確定進入user嗎')
        }
    })
    
  • 全域性的後置守衛 afterEach(fn)

路由獨享守衛

  • 寫在路由表中的守衛鉤子
  • 針對的是和當前路由相關的,那麼其他與之不相關的路由,它是無法監聽它的變化情況的
{
path: '/user',
component: User,
beforeEnter: ( to,from,next ) =>{
    // console.log(to)
    const name = localStorage.getItem('name');//判斷本地儲存有沒有name物件
    if( name){//存在就可以繼續執行
        next()
    }else{
        setTimeout(()=>{
                alert('你還沒有登入,點選跳轉登入')//不存在就彈窗告訴沒有登入,點選登入
                next('/login')
            },0)
       }
    }
},

手寫axios的二次封裝

ajax是基於哪個物件實現的?如何中斷Ajax請求?

DOM阻塞

DOM的阻塞會導致頁面的載入時間延遲,阻塞的資源有html,css(包括web font),javascript

不是單頁面能使用vuex麼

看你個人需要了。單頁應用為什麼要用的?還不是他的邏輯太複雜了,混亂的不得了,不找個工具管理一下各種坑人。如果一個單頁應用裡面什麼js都沒有,就是html,那你引入vuex有什麼用嗎?那麼好了,並不是說單頁應用什麼的,是你遇到了什麼問題,用什麼東西去解決好了附上vuex裡面其實也寫了,為了管理你的共享的東西,共享的狀態。

swagger的瞭解

Swagger 是一個規範和完整的框架,用於生成、描述、呼叫和視覺化 RESTful 風格的 Web 服務。

1、是一款讓你更好的書寫API文件的規範且完整框架。

2、提供描述、生產、消費和視覺化RESTful Web Service

3、是由龐大工具集合支撐的形式化規範。這個集合涵蓋了從終端使用者介面、底層程式碼庫到商業API管理的方方面面。

資料分頁

定義資料集

獲取定位

測效能工具

物件導向的理解

在現實生活中任何物體都可以歸為一類事物,而每一個個體都是一個事物的例項,物件導向的程式設計是以物件為中心.

其中物件導向的有三大特徵,分別是封裝,繼承多型

其中的多型表現為:方法的過載和方法的重寫;

方法過載:過載是指不同的函式使用相同的函式名,但是函式的引數個數或型別不同。呼叫的時候根據函式的引數來區別不同的函式.

方法重寫:重寫(也叫覆蓋)是指在派生類中重新對基類中的虛擬函式(注意是虛擬函式)重新實現。即函式名和引數都一樣,只是函式的實現體不一樣.

H5有哪些螢幕觸碰事件

touchstart:觸控開始的時候觸發

touchmove:手指在螢幕上滑動的時候觸發

touchend:觸控結束的時候觸發

而每個觸控事件都包括了三個觸控列表,每個列表裡包含了對應的一系列觸控點(用來實現多點觸控):

touches:當前位於螢幕上的所有手指的列表。

targetTouches:位於當前DOM元素上手指的列表。

changedTouches:涉及當前事件手指的列表。

每個觸控點由包含了如下觸控資訊(常用):

identifier:一個數值,唯一標識觸控會話(touch session)中的當前手指。一般為從0開始的流水號(android4.1,uc)

target:DOM元素,是動作所針對的目標。

pageX/pageX/clientX/clientY/screenX/screenY:一個數值,動作在螢幕上發生的位置(page包含滾動距離,client不包含滾動距離,screen則以螢幕為基準)。 

radiusX/radiusY/rotationAngle:畫出大約相當於手指形狀的橢圓形,分別為橢圓形的兩個半徑和旋轉角度。初步測試瀏覽器不支援,好在功能不常用,歡迎大家反饋。

Label通常怎麼用,用什麼場景

label標籤為input元素定義標註(標記),它不會向使用者呈現任何特殊效果,和span標籤類似。但label標籤和span標籤最大的區別就是它為滑鼠使用者改進了可用性,可以關聯特定的表單控制元件。

主要的應用場景:

label標籤常用於與checkboxradio關聯,以實現點選文字也能選中/取消checkboxradio

css寫一個三角形

<div></div>
<style>
div {
	width: 0;
	height: 0;
	border-top: 50px solid transparent; /*這行去掉也行*/
	border-bottom: 50px solid yellow;
	border-right: 50px solid transparent;
	border-left: 50px solid transparent;
}	
</style>

或者

<div></div>
<style>
div {
	height:0px;
	width:0px;
	border-top:solid 100px red;
	border-left:solid 100px green;
	
}	
</style>

移步>>>>>>

相關文章