知識點漏缺總結

模塊化

使用模塊化能夠給咱們帶來如下好處css

解決命名衝突html

提供複用性前端

提升代碼可維護性node

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 timer
  const now = Date.now
  let startTime = now()
  let endTime = startTime
  const 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);
// 3

addTen(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)//89

var 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 = name
    this.age = age
    this.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

對於 cookie 來講,咱們還須要注意安全性。

Service Worker

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 文件而後一系列的轉換過程。

接下來就是css樹與dom樹合併成渲染樹。渲染樹只會包括須要顯示的節點和這些節點的樣式信息, 若是某個節點是 display: none 的,那麼就不會在渲染樹中顯示。

爲何操做 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 標籤

在不考慮緩存和優化網絡協議的前提下,考慮能夠經過哪些方式來最快的渲染頁面,也就是常說的關鍵渲染路徑,這部分也是性能優化中的一塊內容。

當發生 DOMContentLoaded 事件後,就會生成渲染樹,生成渲染樹就能夠進行渲染了,這一過程更大程度上和硬件有關係了。

提示如何加速:

從文件大小考慮

從 script 標籤使用上來考慮

從 CSS、HTML 的代碼書寫上來考慮

從須要下載的內容是否須要在首屏使用上來考慮

性能優化

圖片優化

計算圖片大小

對於一張 100 * 100 像素的圖片來講,圖像上有 10000 個像素點,若是每一個像素的值是 RGBA 存儲的話,那麼也就是說每一個像素有 4 個通道,每一個通道 1 個字節(8 位 = 1個字節),因此該圖片大小大概爲 39KB(10000 * 1 * 4 / 1024)。

減小像素點

減小每一個像素點可以顯示的顏色

圖片加載優化

一、修飾圖片徹底能夠用 CSS 去代替。

二、沒有必要去加載原圖浪費帶寬。通常圖片都用 CDN加載,能夠計算出適配屏幕的寬度,而後去請求相應裁剪好的圖片。

三、小圖使用 base64 格式

四、將多個圖標文件整合到一張圖片中(雪碧圖)

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 繼續進行轉變最後再生成新的代碼,項目越大,轉換代碼越多,效率就越低。

優化方法:

一、首先咱們能夠優化 Loader 的文件搜索範圍,只做用在 JS 代碼上的,而後 node_modules 中使用的代碼都是編譯過的,因此咱們也徹底沒有必要再去處理一遍

二、將 Babel 編譯過的文件緩存起來(下次只須要編譯更改過的代碼文件便可,這樣能夠大幅度加快打包時間)

MVVM

首先先申明一點,無論是 React 仍是 Vue,它們都不是 MVVM 框架,只是有借鑑 MVVM 的思路。文中拿 Vue 舉例也是爲了更好地理解 MVVM 的概念。

傳統的 MVC 架構一般是使用控制器更新模型,視圖從模型中獲取數據去渲染。當用戶有輸入時,會經過控制器去更新模型,而且通知視圖進行更新。

可是MVC 有一個巨大的缺陷就是控制器承擔的責任太大了,隨着項目越發複雜,控制器中的代碼會愈來愈臃腫,致使出現不利於維護的狀況。

在 MVVM 架構中,引入了 ViewModel 的概念。ViewModel 只關心數據和業務的處理,不關心 View 如何處理數據,在這種狀況下,View 和 Model 均可以獨立出來,任何一方改變了也不必定須要改變另外一方,而且能夠將一些可複用的邏輯放在一個 ViewModel 中,讓多個 View 複用這個 ViewModel。

以 Vue 框架來舉例,ViewModel 就是組件的實例。View 就是模板,Model 的話在引入 Vuex 的狀況下是徹底能夠和組件分離的。

除了以上三個部分,其實在 MVVM 中還引入了一個隱式的 Binder 層,實現了 View 和 ViewModel 的綁定。

一樣以 Vue 框架來舉例, 這個隱式的 Binder 層就是 Vue 經過解析模板中的插值和指令從而實現 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) 中的 stateObject
  console.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被徹底構造,但連接的資源(例如圖像,樣式表和子幀)可能還沒有被加載。

相關文章
相關標籤/搜索