線程與進程之間的關系
進程是指在系統中正在運行的一個應用程序,線程是系統分配處理器時間資源的基本單元, 或者說進程之內獨立執行的一個單元。進程至少包括一個線程
獲取元素的位置、大小信息
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位表示尾數。
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 的執行過程
答案:
- 創建一個新對象obj
- 將obj.proto = Person
- Person.call(obj)
- 判斷構造函數有沒有返回值 沒有就直接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動態加載的原理是什么?
- 查看緩存里是否加載過模塊,沒有的話異步加載chunk
- 用promise 包裝的動態創建script標簽去加載資源
- 加載成功后,執行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等事件