字節面經收集

線程與進程之間的關系

進程是指在系統中正在運行的一個應用程序,線程是系統分配處理器時間資源的基本單元, 或者說進程之內獨立執行的一個單元。進程至少包括一個線程

獲取元素的位置、大小信息

dom.getBoundingClientRect()

實現一個屬性不可修改

Object.defineProperty(obj, 'name', {writable: false})
Object.freeze(obj)

Object.defineProperty第三個參數屬性值

configure,
writable,
enumerable,
value,
get,
set

Proxy

Proxy,代理,是ES6新增的功能,可以理解為代理器
var proxy = new Proxy(target, handler)
target:目標對象
handler:一個對象,其屬性是當執行一個操作時定義代理的行為的函數。

Reflect屬于內置的靜態類 ,提供了一些靜態方法,來操作對象;Proxy實際也是調用Reflect的方法去操作對象的;

// vue3中的ref()方法是使用的Object.defineProperty

promise、async await、generate關系

都是js中用于處理異步的方法
promise es6的新api,可以通過then方法鏈式調用處理異步
generate 函數遇到yield就停止執行; 需要調用next方法; 實際例子:koa1  (co庫)
async 函數 內置了迭代器 去執行函數內的 await 后的異步;并最終返回的一個promise對象 

1. 寫一個處理加法可能產生精度的函數0.1 + 0.2 = 0.3

原因:Number類型的有效整數范圍是-0XFFFFFFFFFFF至0X1FFFFFFFFFF,所以無法精確到超過這個范圍的整數.

解法:(0.1 * 1000+0.2 * 1000)/1000==0.3
原因:JS的浮點數實現也是遵循IEEE 754標準,采用雙精度存儲(double precision),使用64位固定長度來表示,其中1位用來表示符號位,11位用來表示指數,52位表示尾數。


image.png
function bigAdd (a, b) {
  const maxLength = Math.max(a.length, b.length)
  a = a.padStart(maxLength, '0')
  b = b.padStart(maxLength, '0')

  let x = 0
  let y = 0
  let sum = ''
  for (let i = maxLength - 1; i >= 0; i--) {
    x = Number(a[i]) + Number(b[i]) + y
    y = Math.floor(x / 10)
    sum = (x % 10) + sum 
  }
  if (y == 1) {
    sum = '1' + sum
  }
  return sum
}
// 小數點
function smallAdd (a, b) {
  let s1, s2
  try {
    s1 = a.split('.')[1].length
  } catch (e) {
    s1 = 0
  }
  try {
    s2 = b.split('.')[1].length
  } catch (e) {
    s2 = 0
  }
  const max = Math.max(s1, s2)
  const n = Math.pow(10, max)
  return ((a*n) + (b*n)) / n
}

2.寫 new 的執行過程

答案:

  1. 創建一個新對象obj
  2. 將obj.proto = Person
  3. Person.call(obj)
  4. 判斷構造函數有沒有返回值 沒有就直接return obj
function myNew(constrc, ...args) {
    const obj = {}; // 1. 創建一個空對象
    obj.__proto__ = constrc.prototype; // 2. 將obj的[[prototype]]屬性指向構造函數的原型對象
    constrc.apply(obj, args); // 3.將constrc執行的上下文this綁定到obj上,并執行
    return obj;  //4. 返回新創建的對象
}

3.Cookie和Session有什么區別?

(1)Session 是在服務端保存的一個數據結構,用來跟蹤用戶的狀態,這個數據可以保存在集群、數據庫、文件中;

(2)Cookie 是客戶端保存用戶信息的一種機制,用來記錄用戶的一些信息,也是實現 Session 的一種方式。

瀏覽器如何實現Session的?
1、用戶向服務器發送用戶名和密碼。

2、服務器驗證通過后,在當前對話(session)里面保存相關數據,比如用戶角色、登錄時間等等。

3、服務器向用戶返回一個 session_id,寫入用戶的 Cookie。

4、用戶隨后的每一次請求,都會通過 Cookie,將 session_id 傳回服務器。

5、服務器收到 session_id,找到前期保存的數據,由此得知用戶的身份。

jwt登錄

JSON Web Token(縮寫 JWT)
JWT 的原理是,服務器認證以后,生成一個 JSON 對象,發回給用戶,就像下面這樣。
它是一個很長的字符串,中間用點(.)分隔成三個部分。
Header(頭部)base64url加密
Payload(負載)base64url加密
Signature(簽名)

單點登錄

同一級域名
1.系統共享session
2.客戶端共享一級域名下cookie

不同域名
1.app1.com 初次登錄 請求sso 賬號密碼權限認證 完成登錄;跳回系統app1并返回ticket
2.app1系統拿到ticket后,從后臺向SSO發送請求,驗證ticket是否有效。
3.驗證通過寫入app1系統的session,cookie
4.訪問app2.com 請求sso,發現是已登錄,跳回系統app2并返回ticket
5.驗證通過寫入app2系統的session,cookie

說一下瀏覽器緩存;

強緩存
瀏覽器在加載資源時,會先根據本地緩存資源的 header 中的信息判斷是否命中強緩存,如果命中則直接使用緩存中的資源不會再向服務器發送請求。

這里的 header 中的信息指的是 expires 和 cahe-control.
Expires
該字段是 http1.0 時的規范,它的值為一個絕對時間的 GMT 格式的時間字符串,\這種方式有一個明顯的缺點,由于失效時間是一個絕對時間,所以當服務器與客戶端時間偏差較大時,就會導致緩存混亂。

Cache-Control
Cache-Control 是 http1.1 時出現的 header 信息,主要是利用該字段的 max-age 值來進行判斷,它是一個相對時間,例如 Cache-Control:max-age=3600,代表著資源的有效期是 3600 秒。cache-control 除了該字段外,還有下面幾個比較常用的設置值:

no-cache:需要進行協商緩存,發送請求到服務器確認是否使用緩存。

no-store:禁止使用緩存,每一次都要重新請求數據。

public:可以被所有的用戶緩存,包括終端用戶和 CDN 等中間代理服務器。

private:只能被終端用戶的瀏覽器緩存,不允許 CDN 等中繼緩存服務器對其緩存。

Cache-Control 與 Expires 可以在服務端配置同時啟用,同時啟用的時候 Cache-Control 優先級高。

協商緩存
當強緩存沒有命中的時候,瀏覽器會發送一個請求到服務器,服務器根據 header 中的部分信息來判斷是否命中緩存。如果命中,則返回 304 ,告訴瀏覽器資源未更新,可使用本地的緩存。

這里的 header 中的信息指的是 Last-Modify/If-Modify-Since 和 ETag/If-None-Match.

Last-Modify/If-Modify-Since
瀏覽器第一次請求一個資源的時候,服務器返回的 header 中會加上 Last-Modify,Last-modify 是一個時間標識該資源的最后修改時間。

當瀏覽器再次請求該資源時,request 的請求頭中會包含 If-Modify-Since,該值為緩存之前返回的 Last-Modify。服務器收到 If-Modify-Since 后,根據資源的最后修改時間判斷是否命中緩存。

如果命中緩存,則返回 304,并且不會返回資源內容,并且不會返回 Last-Modify。

缺點:

短時間內資源發生了改變,Last-Modified 并不會發生變化。

周期性變化。如果這個資源在一個周期內修改回原來的樣子了,我們認為是可以使用緩存的,但是 Last-Modified 可不這樣認為,因此便有了 ETag。
ETag/If-None-Match
與 Last-Modify/If-Modify-Since 不同的是,Etag/If-None-Match 返回的是一個校驗碼。ETag 可以保證每一個資源是唯一的,資源變化都會導致 ETag 變化。服務器根據瀏覽器上送的 If-None-Match 值來判斷是否命中緩存。

與 Last-Modified 不一樣的是,當服務器返回 304 Not Modified 的響應時,由于 ETag 重新生成過,response header 中還會把這個 ETag 返回,即使這個 ETag 跟之前的沒有變化。

Last-Modified 與 ETag 是可以一起使用的,服務器會優先驗證 ETag,一致的情況下,才會繼續比對 Last-Modified,最后才決定是否返回 304。

避免緩存

1. Cache-Control:no-cache, Expires: 0;
2. 請求query加時間戳或隨機字符串

webpack動態加載的原理是什么?

  1. 查看緩存里是否加載過模塊,沒有的話異步加載chunk
  2. 用promise 包裝的動態創建script標簽去加載資源
  3. 加載成功后,執行resolve, 執行自定義的回調

回流重繪

1.解析生產dom tree
2.解析css生產cssom
3.根據每個可見節點以及其對應的樣式,組合生成渲染樹
回流:計算節點確切位置和大小
重繪:渲染樹的每個節點都轉換為屏幕上的實際像素
回流一定觸發重繪。重繪不一定觸發回流

減少回流和重繪
合并所有的改變然后依次處理:使用cssText、修改class類名

dom:
當我們需要對DOM對一系列修改的時候,可以通過以下步驟減少回流重繪次數:
使元素脫離文檔流
對其進行多次修改
將元素帶回到文檔中。
(隱藏元素,應用修改,重新顯示)
對于復雜動畫效果,使用絕對定位讓其脫離文檔流

css3硬件加速(GPU加速)

劃重點:使用css3硬件加速,可以讓transform、opacity、filters這些動畫不會引起回流重繪 。但是對于動畫的其它屬性,比如background-color這些,還是會引起回流重繪的,不過它還是可以提升這些動畫的性能。

常見的觸發硬件加速的css屬性:

  • transform
  • opacity
  • filters
  • Will-change

get post 區別

1.get能被緩存、對數據長度的限制
2.post option請求
3.報文結構,不同, 
4.post 提交在body,get在url

性能優化指標

fp: 收字節到達時間
fcp:首容繪制時間
lcp: 主要內容繪制時間
fid:用戶可交互時間
cls: 頁面抖動

script標簽defer async區別

defer/async 都是異步加載js
defer用于開啟新的線程下載腳本文件,并使腳本在文檔解析完成后執行。
async是異步下載,下載完后立即執行

window.onload 和document.ready區別

頁面加載完成有兩種事件
一是ready,表示文檔結構已經加載完成(不包含圖片等非文字媒體文件)
二是onload,指示頁面包含圖片等文件在內的所有元素都加載完成。

性能優化

1.合并請求,減少 HTTP 請求數量,
2.使用http2;HTTP2 是基于幀的協議、多路復用(避免隊頭堵塞)、首部壓縮、服務器推送
3.使用服務端渲染,更快的內容到達時間
4.靜態資源使用 CDN,全站加速dcdn
5.將 CSS 放在文件頭部,避免沒有樣式;JavaScript 文件放在底部,避免阻塞瀏覽器渲染;defer屬性可以異步加載;preload/prefetch/dns-prefetch
6.使用字體圖標 iconfont 代替圖片圖標;字體圖標是矢量圖,不會失真。字體壓縮后文件特別小;
7.善用緩存(強緩存,協商緩存,服務端渲染秒級緩存)不重復加載相同的資源;
8.壓縮文件,content-encoding br, gzip
9.圖片優化: 圖片壓縮(webp)、圖片剪裁、圖片懶加載
10.通過 webpack 按需加載代碼,提取第三庫代碼,減少 ES6 轉為 ES5 的冗余代碼
11.減少重繪重排,
12.使用事件委托
13.js動畫使用requestAnimationFrame、
14.使用 transform 和 opacity 屬性更改來實現動畫,硬件加速(will-change/translateZ(0))

vue
15.避免響應所有數據,不需要響應式的數據可以定義在實例上
16.v-for添加key且避免同時使用v-if
17區分computed和watch使用場景
18.區分v-if與v-show使用場景
19.路由懶加載

dns過程

本地緩存,本地host,本地dns服務器遞歸查詢、然后遍歷查詢跟dns服務器,定級dns服務器;

百度用ip可以訪問,知乎不可以

因為知乎訪問的是cdn節點服務器 缺少 host時無法判斷是誰的服務,拒絕響應

常見header

Accept
Accept-Encoding
host
referer
access-controll-allow-origin/header/methods/credentials/
authreztion
cookie
last-modified
Etag
Cache-control
if-modified
expires
connection:keep-alive
content-type: x-www-form-urlencoded,multipart/form-data
content-encoding: gzip、br
user-agent

Vue渲染過程

1.Observer
2.Dep
3.Watcher
4.遍歷整個組件樹

vue組件實例化時,會對data屬性深度遍歷(遇到數組或者對象)為每一個屬性添加數據劫持。數據劫持就是使用Object.defineProperty(de fai in pro pu tei)方法添加get/set方法。
在這個過程中會實例化一個Dep類。
1.在get攔截器里觸發dep實例的depend方法,進行依賴收集,實質是在dep的實例屬性sub數組中添加依賴這個屬性的watcher實例。
2.在set攔截器里觸發了dep實例的notify(nao te fai)方法,對收集到的所有依賴派發更新,(watcher的update方法)


vue組件實例化時會實例化一個渲染watcher,渲染watcher實例化過程會做兩件事情。
1.創建vnode,在這個過程中,訪問了data屬性,觸發了get方法,完成了依賴收集。
2.觸發了子組件的實例化,子組件實例化又會重復上述數據劫持的過程。
這個過程就是對組件樹的深度遍歷。

結合組件生命周期來看整個過程,父組件會先觸發created鉤子,子組件后觸發created鉤子。而子組件mouted鉤子會先執行,父組件的mouted鉤子后執行。

vue diff算法

完整的diff算法時間復雜度是O(n^3),顯示是一個很昂貴的算法;
Vue選擇了一個折中的方式;時間復雜度為O(n);
策略就是只進行同層比較,跨層級的移動認為是刪除和創建操作;
不同的類型的節點認為是新創建;不遞歸比較其子節點;
列表結構的節點,通過key值進行確認是刪除還是創建或者移動;
時間復雜度之所以是O(n),因為比較新舊節點時使用了首位各兩個指針;遍歷一次

vue組件通信

1.props $emit (v-modle)
2.vuex
3.eventbus
4.$refs
5.$parent $children
6.provide /inject 

文件斷點續傳

大文件上傳

前端上傳大文件時使用 Blob.prototype.slice 將文件切片,并發上傳多個切片,最后發送一個合并的請求通知服務端合并切片
服務端接收切片并存儲,收到合并請求后使用流將切片合并到最終文件
原生 XMLHttpRequest 的 upload.onprogress 對切片上傳進度的監聽
使用 Vue 計算屬性根據每個切片的進度算出整個文件的上傳進度

使用 spark-md5 根據文件內容算出文件 hash
通過 hash 可以判斷服務端是否已經上傳該文件,從而直接提示用戶上傳成功(秒傳)
通過 XMLHttpRequest 的 abort 方法暫停切片的上傳
上傳前服務端返回已經上傳的切片名,前端跳過這些切片的上傳

nodejs 集群

ipc通信:傳遞數據,和句柄(socket,server,udp socket)
利用cpu多核

nodejs/webpack 熱更新

webpack  HMR
是webpack一個重要的特性,當代碼文件修改并保存之后,webapck通過watch監聽到文件發生變化,
會對代碼文件重新打包生成兩個模塊補丁文件manifest(json)和一個(或多個)updated chunk(js),websocket鏈接發送manifest,瀏覽器按照創建script標簽更新,觸發
將結果存儲在內存文件系統中,通過websocket通信機制將重新打包的模塊發送到瀏覽器端,
瀏覽器動態的獲取新的模塊補丁替換舊的模塊,瀏覽器不需要刷新頁面就可以實現應用的更新。
用到了webpack-dev-middleware、webpack-hot-middleware、webpack-dev-server

nodemon

vue ssr

vue-server-renderer
插件:VueSSRServerPlugin、VueSSRClientPlugin

VueSSRClientPlugin
vue-ssr-client-manifest.json就是通過VueSSRClientPlugin生成的clientManifest文件,稱為客戶端構建清單。

它將輸入到renderer中,為模板的資源加載提供信息,推斷和注入資源預加載和數據預取指令和首次渲染需要加載script標簽。

1、獲取編譯對象中的包含編譯信息的stats對象

2、從stats.assets得到應用用到所有資源數組allFiles(元素是文件名)

3、從入口文件對象中找出初次渲染加載的js和css文件數組initialFiles

4、allFiles與initialFiles的差集,過濾出js和css,作為可異步加載資源asyncFiles

5、分別從stats.modules找出每個模塊對應的chuck代碼文件,在將chunk依賴文件轉換為allFiles中的indexArray,然后moduleId做key,indexArray作為value組成map對象。

6、最后組裝成json對象,序列化后掛上compilation.assets對象上,最為json文件輸出。

VueSSRServerPlugin

vue服務端構建插件做的事情跟客戶端構建插件一樣,都是輸出一個json文件,源碼中的邏輯幾乎也一樣,不一樣的地方在于輸出的文件內容 
var bundle = {
  entry: entry,
  files: {}, // 編譯的源碼
  maps: {}  // sourceMap
};

構建出clientManifest和server bundle兩個json文件主要是為了讓vue-server-renderer支持一些高級特性,比如自動注入(預加載和預取,inlineStyle關鍵css)、state等,已達到對輸出html更細粒度的控制能力。

完整過程:
VueSSRServerPlugin 生產serveBundle
VueSSRClientPlugin生產clientBundle(客戶端資源清單)
vue-server-renderer 提供了方法createBundleRanderer方法接受serveBundle;
根絕url找到對應sourceMap, 組成當前路由的vue 實例;
然后返回renderToString、renderToStream;

執行renderToString輸出html文件拼接到模板HTML文件上;

在通過clientBundle 拼接當前使用到的js和css,以及preload、prefetch link標簽;

vue ssr 流式渲染 就是nodejs stream流;data、end、error等事件

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容