模塊化
使用模塊化可以給我們帶來以下好處
解決命名沖突
提供復用性
提高代碼可維護性
Proxy
Proxy 來替換原本的 Object.defineProperty 來實現數據響應式。 Proxy 是 ES6 中新增的功能,它可以用來自定義對象中的操作。
let p = new Proxy(target, handler)
復制代碼
target 代表需要添加代理的對象 ,handler 用來自定義對象中的操作,比如可以用來自定義 set 或者 get 函數。
接下來我們通過 Proxy 來實現一個數據響應式
let onWatch = (obj, setBind, getLogger) => {let handler = {get(target, property, receiver) {getLogger(target, property)return Reflect.get(target, property, receiver)},set(target, property, value, receiver) {setBind(value, property)return Reflect.set(target, property, value)}}return new Proxy(obj, handler)
}let obj = { a: 1 }
let p = onWatch(obj,(v, property) => {console.log(`監聽到屬性${property}改變為${v}`)},(target, property) => {console.log(`'${property}' = ${target[property]}`)}
)
p.a = 2 // 監聽到屬性a改變
p.a // 'a' = 2
復制代碼
在上述代碼中,我們通過自定義 set 和 get 函數的方式,在原本的邏輯中插入了我們的函數邏輯,實現了在對對象任何屬性進行讀寫時發出通知。
當然這是簡單版的響應式實現,如果需要實現一個 Vue 中的響應式,需要我們在 get 中收集依賴,在 set 派發更新,之所以 Vue3.0 要使用 Proxy 替換原本的 API 原因在于 Proxy 無需一層層遞歸為每個屬性添加代理,一次即可完成以上操作,性能上更好,并且原本的實現有一些數據更新不能監聽到,但是 Proxy 可以完美監聽到任何方式的數據改變,唯一缺陷可能就是瀏覽器的兼容性不好了。
reduce
const arr = [1, 2, 3]
const sum = arr.reduce((acc, current) => acc + current, 0)
console.log(sum)
復制代碼
對于 reduce 來說,它接受兩個參數,分別是回調函數和初始值,接下來我們來分解上述代碼中 reduce 的過程
首先初始值為 0,該值會在執行第一次回調函數時作為第一個參數傳入 回調函數接受四個參數,分別為累計值、當前元素、當前索引、原數組
在一次執行回調函數時,當前值和初始值相加得出結果1,
該結果會在第二次執行回調函數時當做第一個參數傳入。
所以在第二次執行回調函數時,相加的值就分別是 1 和2,以此類推,循環結束后得到結果 6
就通過 reduce 來實現 map和filter 函數
const numbers = [10, 20, 30, 40];
const go = numbers.reduce((finalList, num) => {//實現map方法,映射的作用num = num * 2;if (num > 50) {//實現filter方法過濾finalList.push(num);}return finalList;
}, []);alert(go); // [60, 80]復制代碼
setInterval
其實這個函數作用和 setTimeout 基本一致,只是該函數是每隔一段時間執行一次回調函數。
通常來說不建議使用 setInterval。第一,它和 setTimeout 一樣,不能保證在預期的時間執行任務。第二,它存在執行累積的問題,請看以下偽代碼
function demo() {setInterval(function(){console.log(2)},1000)sleep(2000)
}
demo()
復制代碼
以上代碼在瀏覽器環境中,如果定時器執行過程中出現了耗時操作,多個回調函數會在耗時操作結束以后同時執行,這樣可能就會帶來性能上的問題。
如果你有循環定時器的需求,其實完全可以通過 requestAnimationFrame 來實現
特點
【1】requestAnimationFrame會把每一幀中的所有DOM操作集中起來,在一次重繪或回流中就完成,并且重繪或回流的時間間隔緊緊跟隨瀏覽器的刷新頻率
??【2】在隱藏或不可見的元素中,requestAnimationFrame將不會進行重繪或回流,這當然就意味著更少的CPU、GPU和內存使用量
??【3】requestAnimationFrame是由瀏覽器專門為動畫提供的API,在運行時瀏覽器會自動優化方法的調用,并且如果頁面不是激活狀態下的話,動畫會自動暫停,有效節省了CPU開銷
function setInterval(callback, interval) {let timerconst now = Date.nowlet startTime = now()let endTime = startTimeconst loop = () => {timer = window.requestAnimationFrame(loop)endTime = now()if (endTime - startTime >= interval) {startTime = endTime = now()callback(timer)}}timer = window.requestAnimationFrame(loop)return timer
}let a = 0
setInterval(timer => {console.log(1)a++if (a === 3) cancelAnimationFrame(timer)
}, 1000)
復制代碼
首先 requestAnimationFrame 自帶函數節流功能,基本可以保證在 16.6 毫秒內只執行一次(不掉幀的情況下),并且該函數的延時效果是精確的,沒有其他定時器時間不準的問題,當然你也可以通過該函數來實現 setTimeout。
進程與線程
進程和線程都是一個時間段的描述,是CPU工作時間段的描述。放在應用上來說就代表了一個程序。線程是進程中的更小單位,描述了執行一段指令所需的時間。
把這些概念拿到瀏覽器中來說,當你打開一個 Tab 頁時,其實就是創建了一個進程,一個進程中可以有多個線程,比如渲染線程、JS 引擎線程、HTTP 請求線程等等。當你發起一個請求時,其實就是創建了一個線程,當請求結束后,該線程可能就會被銷毀。
bind
和call很相似,第一個參數是this的指向,從第二個參數開始是接收的參數列表。區別在于bind方法返回值是函數以及bind接收的參數列表的使用。
bind返回值是函數
var obj = {name: 'Dot'
}function printName() {console.log(this.name)
}var dot = printName.bind(obj)
console.log(dot) // function () { … }
dot() // Dot
復制代碼
bind 方法不會立即執行,而是 返回一個改變了上下文 this 后的函數。 而原函數 printName 中的 this 并沒有被改變,依舊指向全局對象 window。
參數的使用
function fn(a, b, c) {console.log(a, b, c);
}
var fn1 = fn.bind(null, 'Dot');fn('A', 'B', 'C'); // A B C
fn1('A', 'B', 'C'); // Dot A B
fn1('B', 'C'); // Dot B C
fn.call(null, 'Dot'); // Dot undefined undefined
復制代碼
call 是把第二個及以后的參數作為 fn 方法的實參傳進去,而 fn1 方法的實參實則是在 bind 中參數的基礎上再往后排。
有時候我們也用bind方法實現函數珂里化,以下是一個簡單的示例:
var add = function(x) {return function(y) {return x + y;};
};var increment = add(1);
var addTen = add(10);increment(2);
// 3addTen(2);
// 12
復制代碼
在低版本瀏覽器沒有 bind 方法,我們也可以自己實現一個。
if (!Function.prototype.bind) {Function.prototype.bind = function () {var self = this, // 保存原函數context = [].shift.call(arguments), // 保存需要綁定的this上下文args = [].slice.call(arguments); // 剩余的參數轉為數組return function () { // 返回一個新函數self.apply(context, [].concat.call(args, [].slice.call(arguments)));}}
}
復制代碼
call apply bind應用場景
求數組中的最大和最小值
var arr = [1,2,3,89,46]var max = Math.max.apply(null,arr)//89var min = Math.min.apply(null,arr)//1
復制代碼
將類數組轉化為數組
var trueArr = Array.prototype.slice.call(arrayLike)
復制代碼
數組追加
var arr1 = [1,2,3];
var arr2 = [4,5,6];
var total = [].push.apply(arr1, arr2);//6
// arr1 [1, 2, 3, 4, 5, 6]
// arr2 [4,5,6]
復制代碼
判斷變量類型
function isArray(obj){return Object.prototype.toString.call(obj) == '[object Array]';
}
isArray([]) // true
isArray('dot') // false復制代碼
利用call和apply做繼承
function Person(name,age){// 這里的this都指向實例this.name = namethis.age = agethis.sayAge = function(){console.log(this.age)}
}
function Female(){Person.apply(this,arguments)//將父元素所有方法在這里執行一遍就繼承了
}
var dot = new Female('Dot',2)
復制代碼
使用 log 代理 console.log
function log(){console.log.apply(console, arguments);
}
// 當然也有更方便的 var log = console.log()
復制代碼
總結
call、apply和bind函數存在的區別:
bind返回對應函數, 便于稍后調用; apply, call則是立即調用。 除此外, 在 ES6 的箭頭函數下, call 和 apply 將失效, 對于箭頭函數來說:
箭頭函數體內的 this 對象, 就是定義時所在的對象,而不是使用時所在的對象;所以不需要類似于var _this = this這種丑陋的寫法
箭頭函數不可以當作構造函數,也就是說不可以使用 new 命令, 否則會拋出一個錯誤
箭頭函數不可以使用 arguments 對象,,該對象在函數體內不存在. 如果要用, 可以用 Rest 參數代替
不可以使用 yield 命令, 因此箭頭函數不能用作 Generator 函數
為什么 0.1 + 0.2 != 0.3
因為 JS 采用 IEEE 754 雙精度版本(64位),0.1 在二進制中是無限循環的一些數字,其實不只是 0.1,其實很多十進制小數用二進制表示都是無限循環的。 JS 采用的浮點數標準卻會裁剪掉我們的數字。 那么這些循環的數字被裁剪了,就會出現精度丟失的問題,也就造成了 0.1 不再是 0.1 了,而是變成了 0.100000000000000002
解決的辦法有很多,這里我們選用原生提供的方式來最簡單的解決問題
parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true
復制代碼
存儲
cookie,localStorage,sessionStorage,indexDB
Service Worker 是運行在瀏覽器背后的獨立線程,一般可以用來實現緩存功能。 使用 Service Worker的話,傳輸協議必須為 HTTPS。因為 Service Worker 中涉及到請求攔截,所以必須使用 HTTPS 協議來保障安全。
Service Worker 實現緩存功能一般分為三個步驟:首先需要先注冊 Service Worker,然后監聽到 install 事件以后就可以緩存需要的文件,那么在下次用戶訪問的時候就可以通過攔截請求的方式查詢是否存在緩存,存在緩存的話就可以直接讀取緩存文件,否則就去請求數據。
瀏覽器緩存機制
緩存可以說是性能優化中簡單高效的一種優化方式了,它可以顯著減少網絡傳輸所帶來的損耗。
對于一個數據請求來說,可以分為發起網絡請求、后端處理、瀏覽器響應三個步驟。瀏覽器緩存可以幫助我們在第一和第三步驟中優化性能。比如說直接使用緩存而不發起請求,或者發起了請求但后端存儲的數據和前端一致,那么就沒有必要再將數據回傳回來,這樣就減少了響應數據。
緩存位置
Service Worker
Memory Cache
Disk Cache
Push Cache
網絡請求
緩存策略
通常瀏覽器緩存策略分為兩種:強緩存和協商緩存,并且 緩存策略都是通過設置 HTTP Header 來實現的。
強緩存
強緩存可以通過設置兩種 HTTP Header 實現:Expires 和 Cache-Control 。
強緩存表示在緩存期間不需要請求,state code 為 200。
Expires
Expires: Wed, 22 Oct 2018 08:41:00 GMT Expires 是 HTTP/1 的產物,表示資源會在 Wed, 22 Oct 2018 08:41:00 GMT 后過期,需要再次請求。并且 Expires 受限于本地時間,如果修改了本地時間,可能會造成緩存失效。
Cache-control
Cache-control: max-age=30 Cache-Control 出現于 HTTP/1.1,優先級高于 Expires 。該屬性值表示資源會在 30 秒后過期,需要再次請求。
協商緩存
如果緩存過期了,就需要發起請求驗證資源是否有更新。協商緩存可以通過設置兩種 HTTP Header 實現 :Last-Modified 和 ETag 。
當瀏覽器發起請求驗證資源時,如果資源沒有做改變,那么服務端就會返回 304 狀態碼,并且更新瀏覽器緩存有效期。
Last-Modified 和 If-Modified-Since
Last-Modified 表示本地文件最后修改日期,
If-Modified-Since 會將 Last-Modified 的值發送給服務器,詢問服務器在該日期后資源是否有更新,有更新的話就會將新的資源發送回來,否則返回 304 狀態碼。
但是 Last-Modified 存在一些弊端:
如果本地打開緩存文件,即使沒有對文件進行修改,但還是會造成 Last-Modified 被修改,服務端不能命中緩存導致發送相同的資源 因為 Last-Modified 只能以秒計時,如果在不可感知的時間內修改完成文件,那么服務端會認為資源還是命中了,不會返回正確的資源
因為以上這些弊端,所以在 HTTP / 1.1 出現了 ETag 。
ETag 和 If-None-Match
ETag 類似于文件指紋,If-None-Match 會將當前 ETag 發送給服務器,詢問該資源 ETag 是否變動,有變動的話就將新的資源發送回來。并且 ETag 優先級比 Last-Modified 高。
以上就是緩存策略的所有內容了,看到這里,不知道你是否存在這樣一個疑問。如果什么緩存策略都沒設置,那么瀏覽器會怎么處理?
對于這種情況,瀏覽器會采用一個啟發式的算法,通常會取響應頭中的 Date 減去 Last-Modified 值的 10% 作為緩存時間。
實際場景應用緩存策略
頻繁變動的資源
對于頻繁變動的資源,首先需要使用 Cache-Control: no-cache
使瀏覽器每次都請求服務器,然后配合 ETag 或者 Last-Modified 來驗證資源是否有效。這樣的做法雖然不能節省請求數量,但是能顯著減少響應數據大小。
代碼文件
這里特指除了 HTML 外的代碼文件,因為 HTML 文件一般不緩存或者緩存時間很短。
一般來說,現在都會使用工具來打包代碼,那么我們就可以對文件名進行哈希處理,只有當代碼修改后才會生成新的文件名。基于此,我們就可以給代碼文件設置緩存有效期一年 Cache-Control: max-age=31536000,這樣只有當 HTML 文件中引入的文件名發生了改變才會去下載最新的代碼文件,否則就一直使用緩存。
瀏覽器渲染原理
瀏覽器從網絡中接收到 HTML 文件然后一系列的轉換過程。
為什么操作 DOM 慢
因為 DOM 是屬于渲染引擎中的東西,而 JS 又是 JS 引擎中的東西。當我們通過 JS 操作 DOM 的時候,其實 這個操作涉及到了兩個線程之間的通信,那么勢必會帶來一些性能上的損耗。 操作 DOM 次數一多,也就等同于一直在進行線程之間的通信,并且操作 DOM 可能還會帶來重繪回流的情況,所以也就導致了性能上的問題。
經典面試題:插入幾萬個 DOM,如何實現頁面不卡頓?
大部分人應該可以想到通過 requestAnimationFrame (requestAnimationFrame會把每一幀中的所有DOM操作集中起來,在一次重繪或回流中就完成,并且重繪或回流的時間間隔緊緊跟隨瀏覽器的刷新頻率)的方式去循環的插入 DOM。
其實還有種方式去解決這個問題:虛擬滾動(virtualized scroller)。
這種技術的原理就是只渲染可視區域內的內容,非可見區域的那就完全不渲染了,當用戶在滾動的時候就實時去替換渲染的內容。
什么情況阻塞渲染
首先渲染的前提是生成渲染樹,所以 HTML 和 CSS 肯定會阻塞渲染。如果你想渲染的越快,你越應該降低一開始需要渲染的文件大小,并且扁平層級,優化選擇器。
然后當瀏覽器在解析到 script 標簽時,會暫停構建 DOM,完成后才會從暫停的地方重新開始。也就是說,如果你想首屏渲染的越快,就越不應該在首屏就加載 JS 文件,這也是都建議將 script 標簽放在 body 標簽底部的原因。
當然在當下,并不是說 script 標簽必須放在底部,因為你可以給 script 標簽添加 defer 或者 async 屬性。
當 script 標簽加上 defer 屬性以后,表示該 JS 文件會并行下載,但是會放到 HTML 解析完成后順序執行,所以對于這種情況你可以把 script 標簽放在任意位置。
對于沒有任何依賴的 JS 文件可以加上 async 屬性,表示 JS 文件下載和解析不會阻塞渲染。
減少重繪和回流
使用 transform 替代 top
<div class="test"></div>
<style>.test {position: absolute;top: 10px;width: 100px;height: 100px;background: red;}
</style>
<script>setTimeout(() => {// 引起回流document.querySelector('.test').style.top = '100px'}, 1000)
</script>
復制代碼
使用 visibility 替換 display: none ,因為前者只會引起重繪,后者會引發回流(改變了布局)
不要把節點的屬性值放在一個循環里當成循環里的變量
for(let i = 0; i < 1000; i++) {// 獲取 offsetTop 會導致回流,因為需要去獲取正確的值console.log(document.querySelector('.test').style.offsetTop)
}
復制代碼
不要使用 table 布局,可能很小的一個小改動會造成整個 table 的重新布局
動畫實現的速度的選擇,動畫速度越快,回流次數越多,也可以選擇使用 requestAnimationFrame
CSS 選擇符從右往左匹配查找,所以要避免節點層級過多
將頻繁重繪或者回流的節點設置為圖層,圖層能夠阻止該節點的渲染行為影響別的節點。比如對于 video 標簽來說,瀏覽器會自動將該節點變為圖層。
設置節點為圖層的方式有很多,我們可以通過以下幾個常用屬性可以生成新圖層
will-change
video、iframe 標簽
在不考慮緩存和優化網絡協議的前提下,考慮可以通過哪些方式來最快的渲染頁面,也就是常說的關鍵渲染路徑,這部分也是性能優化中的一塊內容。
提示如何加速:
從文件大小考慮
從 script 標簽使用上來考慮
從 CSS、HTML 的代碼書寫上來考慮
從需要下載的內容是否需要在首屏使用上來考慮
性能優化
圖片優化
計算圖片大小
對于一張 100 * 100 像素的圖片來說,圖像上有 10000 個像素點,如果每個像素的值是 RGBA 存儲的話,那么也就是說每個像素有 4 個通道,每個通道 1 個字節(8 位 = 1個字節),所以該圖片大小大概為 39KB(10000 * 1 * 4 / 1024)。
減少像素點
減少每個像素點能夠顯示的顏色
圖片加載優化
1、修飾圖片完全可以用 CSS 去代替。
2、沒有必要去加載原圖浪費帶寬。一般圖片都用 CDN加載,可以計算出適配屏幕的寬度,然后去請求相應裁剪好的圖片。
3、小圖使用 base64 格式
4、將多個圖標文件整合到一張圖片中(雪碧圖)
5.選擇正確的圖片格式:
對于能夠顯示 WebP 格式的瀏覽器盡量使用 WebP 格式。因為 WebP 格式具有更好的圖像數據壓縮算法,能帶來更小的圖片體積, 而且擁有肉眼識別無差異的圖像質量,缺點就是兼容性并不好 小圖使用 PNG,其實對于大部分圖標這類圖片,完全可以使用 SVG 代替 照片使用 JPEG
DNS 預解析
DNS 解析也是需要時間的,可以通過預解析的方式來預先獲得域名所對應的 IP。
<link rel="dns-prefetch" href="//yuchengkai.cn">
復制代碼
防抖節流也是性能優化
預加載
有些資源不需要馬上用到,但是希望盡早獲取,這時候就可以使用預加載。
預加載其實 是聲明式的 fetch ,強制瀏覽器請求資源,并且不會阻塞 onload 事件,可以使用以下代碼開啟預加載
<link rel="preload" href="http://example.com">
復制代碼
預加載可以一定程度上降低首屏的加載時間,因為可以將一些不影響首屏但重要的文件延后加載,唯一缺點就是兼容性不好。
預渲染
可以通過預渲染將下載的文件預先在后臺渲染,可以使用以下代碼開啟預渲染
<link rel="prerender" href="http://example.com">
復制代碼
預渲染雖然可以提高頁面的加載速度,但是要確保該頁面大概率會被用戶在之后打開,否則就是白白浪費資源去渲染。
懶加載
懶加載的原理就是只加載可視區域,但也可以是即將進入可視區域)內需要加載的東西。對于圖片來說,先設置圖片標簽的 src 屬性為一張占位圖,將真實的圖片資源放入一個自定義屬性中,當進入可視區域時,就將自定義屬性替換為 src 屬性,這樣圖片就會去下載資源,實現了圖片懶加載。
CDN
CDN 的原理是盡可能的在各個地方分布機房緩存數據,這樣即使我們的根服務器遠在國外,在國內的用戶也可以通過國內的機房迅速加載資源。
因此,我們可以將靜態資源盡量使用 CDN 加載,由于瀏覽器對于單個域名有并發請求上限,可以考慮使用多個 CDN 域名。并且對于 CDN 加載靜態資源需要注意 CDN 域名要與主站不同,否則每次請求都會帶上主站的 Cookie,平白消耗流量。
Webpack 性能優化
減少 Webpack 打包時間
優化 Loader
對于 Loader 來說,影響打包效率首當其沖必屬 Babel 了。因為 Babel 會將代碼轉為字符串生成 AST,然后對 AST 繼續進行轉變最后再生成新的代碼,項目越大,轉換代碼越多,效率就越低。
優化方法:
1、首先我們可以優化 Loader 的文件搜索范圍,只作用在 JS 代碼上的,然后 node_modules 中使用的代碼都是編譯過的,所以我們也完全沒有必要再去處理一遍
2、將 Babel 編譯過的文件緩存起來(下次只需要編譯更改過的代碼文件即可,這樣可以大幅度加快打包時間)
MVVM
首先先申明一點,不管是 React 還是 Vue,它們都不是 MVVM 框架,只是有借鑒 MVVM 的思路。文中拿 Vue 舉例也是為了更好地理解 MVVM 的概念。
傳統的 MVC 架構通常是使用控制器更新模型,視圖從模型中獲取數據去渲染。當用戶有輸入時,會通過控制器去更新模型,并且通知視圖進行更新。
但是MVC 有一個巨大的缺陷就是控制器承擔的責任太大了,隨著項目愈加復雜,控制器中的代碼會越來越臃腫,導致出現不利于維護的情況。
在 MVVM 架構中,引入了 ViewModel 的概念。ViewModel 只關心數據和業務的處理,不關心 View 如何處理數據,在這種情況下,View 和 Model 都可以獨立出來,任何一方改變了也不一定需要改變另一方,并且可以將一些可復用的邏輯放在一個 ViewModel 中,讓多個 View 復用這個 ViewModel。
除了以上三個部分,其實在 MVVM 中還引入了一個隱式的 Binder 層,實現了 View 和 ViewModel 的綁定。
對于 MVVM 來說,其實最重要的并不是通過雙向綁定或者其他的方式將 View 與 ViewModel 綁定起來,而是通過 ViewModel 將視圖中的狀態和用戶的行為分離出一個抽象,這才是 MVVM 的精髓。
路由原理
前端路由實現起來其實很簡單,本質就是監聽 URL 的變化,然后匹配路由規則,顯示相應的頁面,并且無須刷新頁面。目前前端使用的路由就只有兩種實現方式
Hash 模式
History 模式
Hash 模式
www.test.com/#/ 就是 Hash URL,當 # 后面的哈希值發生變化時,可以通過 hashchange 事件來監聽到 URL 的變化,從而進行跳轉頁面,并且無論哈希值如何變化,服務端接收到的 URL 請求永遠是 www.test.com。
window.addEventListener('hashchange', () => {// ... 具體邏輯
})
復制代碼
Hash 模式相對來說更簡單,并且兼容性也更好。
History 模式
History 模式是 HTML5 新推出的功能,主要使用 history.pushState 和 history.replaceState 改變 URL。
通過 History 模式改變 URL 同樣不會引起頁面的刷新,只會更新瀏覽器的歷史記錄。
// 新增歷史記錄
history.pushState(stateObject, title, URL)
// 替換當前歷史記錄
history.replaceState(stateObject, title, URL)
復制代碼
當用戶做出瀏覽器動作時,比如點擊后退按鈕時會觸發 popState 事件
window.addEventListener('popstate', e => {// e.state 就是 pushState(stateObject) 中的 stateObjectconsole.log(e.state)
})
復制代碼
兩種模式對比
Hash 模式只可以更改 # 后面的內容,History 模式可以通過 API 設置任意的同源 URL
History 模式可以通過 API 添加任意類型的數據到歷史記錄中,Hash 模式只能更改哈希值,也就是字符串
Hash 模式無需后端配置,并且兼容性好。History 模式在用戶手動輸入地址或者刷新頁面的時候會發起 URL 請求,后端需要配置 index.html 頁面用于匹配不到靜態資源的時候
computed 和 watch 區別
computed 是計算屬性,依賴其他屬性計算值,來動態獲得值并且 computed 的值有緩存,只有當計算值變化才會返回內容。
watch 監聽到值的變化就會執行回調,在回調中可以進行一些復雜業務邏輯操作。
keep-alive 組件有什么作用
如果你需要在組件切換的時候,保存一些組件的狀態防止多次渲染,就可以使用 keep-alive 組件包裹需要保存的組件。
對于 keep-alive 組件來說,它擁有兩個獨有的生命周期鉤子函數,分別為 activated 和 deactivated 。用 keep-alive 包裹的組件在切換時不會進行銷毀,而是緩存到內存中并執行 deactivated 鉤子函數,命中緩存渲染后會執行 actived 鉤子函數。
Vue進階知識
Object.defineProperty 的缺陷
如果通過下標方式修改數組數據或者給對象新增屬性并不會觸發組件的重新渲染,因為 Object.defineProperty 不能攔截到這些操作,更精確的來說,對于數組而言,大部分操作都是攔截不到的
所以 Vue 內部通過重寫函數的方式解決了這個問題。
編譯過程
將模板解析為 AST
優化 AST
將 AST 轉換為 render 函數
NextTick 原理分析
nextTick 可以讓我們在下次 DOM 更新循環結束之后執行延遲回調,用于獲得更新后的 DOM。
持續補充
關于function a()與var a = function()
mili();function mili() {console.log('mili');}mogu();var mogu = function () {console.log('mogu');};
復制代碼
打印結果是:mili Typeerror:mogu is not a function
原因 :因為通過function a ()這種方式是函數聲明和賦值都提前了。而通過var a = function 則只是函數聲明提前了,而賦值要到執行到var a = function這步才會賦值。
瀏覽器緩存圖解
window.onload和document.ready的區別
最基本區別
1.執行時間
window.onload必須等到頁面內包括圖片的所有元素加載完畢后再去執行。
$(document).ready()時DOM結構回執完畢后就執行,不必等到加載完畢。
2.編寫個數不同
window.onload不同同時編寫多個,如果有多個window.onload方法,只會執行一個
$(document).ready()可以同時編寫多個,并且可以得到執行
document.onDOMContentLoaded在頁面中觸發[DOMContentLoaded]事件時觸發。此時,文檔被加載和解析,并且DOM被完全構造,但鏈接的資源(例如圖像,樣式表和子幀)可能尚未被加載。