前端框架及項目面試 聚焦VUE,React和webpack(二)

Vue原理(大廠必考)

  • 組件化
  • 響應式
  • vdom和diff
  • 模板編譯
  • 渲染過程
  • 前端路由

組件化基礎

  • "很久以前"就有組件化
  • 數據驅動視圖(MVVM,setState)

"很久以前"的組件化

  • asp jsp php已經有組件化了
  • nodejs中也有類似的組件化

數據驅動視圖

  • 傳統組件,只是靜態渲染,更新還要依賴於操作DOM
  • 數據驅動視圖——vue MVVM
  • 數據驅動視圖——React setState(暫時按下不表)

Vue MVVM在這裏插入圖片描述
總結

  • 組件化
  • 數據驅動視圖
  • MVVM

Vue響應式

  • 組件data的數據一旦變化,立刻觸發視圖的更新

  • 實現數據驅動視圖的第一步

  • 考察Vue原理的第一題

  • 核心API——Object.defineProperty

  • 如何實現響應式,代碼演示

  • Object.defineProperty的一些缺點(Vue3.0 啓用Proxy)

Proxy有兼容性問題

  • Proxy兼容性不好,且無法polyfill
  • Vue2.x還會存在一段時間,所以都得學
  • Vue3.0相關知識,下一章講,這裏只是先提一下

Object.defineProperty基本用法
在這裏插入圖片描述
在這裏插入圖片描述

  • 監聽對象,監聽數組
  • 複雜對象,深度監聽
  • 幾個缺點

深度監聽與數組監聽

//觸發更新視圖
function updateView(){
    console.log('視圖更新')
}

//重新定義數組原型
const oldArrayProperty=Array.prototype
//創建新對象,原型指向oldArrayProperty,再擴展新的方法不會影響原型
const arrProto=Object.create(oldArrayProperty);
['push','pop','shift','unshift','splice'].forEach(methodName=>{
    arrProto[methodName]=function(){
        updateView()//觸發視圖更新
        oldArrayProperty[methodName].call(this,...arguments)
    }
})
//重新定義屬性,監聽起來
function defineReactive(target,key,value){
    observer(value)
    //核心 API
    Object.defineProperty(target,key,{
        get(){
            return value;
        },
       set(newValue){
           if(newValue!==value){

               observer(newValue)
               //設置新值
               //注意,value一直在閉包中,此處設置完之後,再get時也是會獲取最新的值
               value=newValue

               //觸發更新視圖
               updateView()
           }
       } 

    } )
}

//監聽對象屬性
function observer(target){
    if(typeof target!=='object'||target===null){
        //不是對象或數組
        return target;
    }

    if(Array.isArray(target)){
        target.__proto__=arrProto;
    }
    //重新定義各個屬性(for in 也可以遍歷數組)
    for(let key in target){
        defineReactive(target,key,target[key])
    }
}

const data={
    name:'zhangsan',
    age:20,
    info:{ 
        address:'北京' //需要深度監聽 
    },
    nums:[10,20,30]
}

//監聽數據
observer(data)

//測試
//data.name='lisi'
//data.age=21
//data.age={num:21};
//data.age.num=22;
//data.x='100' //新增屬性,監聽不到——所以有Vue.set
//delete data.name //刪除屬性,監聽不到——所以有Vue.delete
//data.info.address='上海'  //深度監聽
data.nums.push(4) //監聽數組


Object.defineProperty()的缺點

  • 深度監聽,需要遞歸到底,一次性計算量大
  • 無法監聽新增屬性/刪除屬性
  • 無法原生監聽數組,需要特殊處理

總結

  • 基礎API——Object.defineProperty
  • 如何監聽對象(深度監聽),監聽數組
  • Object.defineProperty的缺點

虛擬DOM(Virtual DOM)和diff

  • vdom是實現vue和React的重要基石

  • diff算法是vdom中最核心,最關鍵的部分

  • vdom是一個熱門話題,也是面試中的熱門問題

  • DOM操作非常耗費性能

  • 以前用jQuery,可以自行控制DOM操作的時機,手動調整

  • Vue和React是數據驅動視圖,如何有效控制DOM操作?

解決方案-vdom

  • 有了一定複雜度,想減少計算次數比較難
  • 能不能把計算,更多的轉移爲JS計算?因爲JS執行速度很快
  • vdom-用JS模擬DOM結構,計算出更小的變更,操作DOM

用JS模擬DOM結構

在這裏插入圖片描述
通過snabbdom學習vdom

  • 簡潔強大的vdom庫,易學易用
  • Vue參考它實現的vdom和diff
  • Vue3.0重寫了vdom的代碼,優化了性能
  • 但vdom的基本理念不變,面試考點也不變
  • React vdom具體實現和Vue也不同,但不妨礙統一學習

diff算法

  • diff算法是vdom中最核心,最關鍵的部分
  • diff算法能在日常使用vue React中體現出來(如key)
  • diff算法是前端熱門話題,面試"寵兒"
  • diff即對比,是一個廣泛的概念,如linux diff命令,git diff等
  • 兩個js對象也可以做diff
  • 兩棵樹做diff,如這裏的vdom diff

diff算法概述

在這裏插入圖片描述
樹diff的時間複雜度O(n^3)

  • 第一,遍歷tree1;第二,遍歷tree2
  • 第三,排序
  • 1000個節點,要計算1億次,算法不可用

優化時間複雜度到O(n)

  • 只比較同一層級,不跨級比較

  • tag不相同,則直接刪掉重建,不再深度比較

  • tag和key,兩者都相同,則認爲是相同節點,不再深度比較
    在這裏插入圖片描述
    在這裏插入圖片描述
    diff算法總結

  • patchVnode

  • addVnodes removeVnodes

  • updateChildren(key的重要性)

vdom和diff-總結

  • 細節不重要,updateChildren的過程也不重要,不要深究
  • vdom核心概念很重要:h,vnode,patch,diff,key等
  • vdom存在的價值更重要:數據驅動視圖,控制DOM操作

模板編譯

  • 模板是vue開發中最常用的部分,即與使用相關聯的原理
  • 它不是html,有指令,插值,JS表達式,到底是什麼?
  • 面試不會直接問,但會通過“組件渲染和更新過程”考察
  • 前置知識:JS的with語法
  • vue template complier將模板編譯爲render函數
  • 執行render函數生成vnode

with語法

在這裏插入圖片描述

  • 改變{}內自由變量的查找規則,當做obj屬性來查找
  • 如果找不到匹配的obj屬性,就會報錯
  • with要慎用,它打破了作用域規則,易讀性變差

編譯模板

  • 模板編譯爲render函數,執行render函數返回vnode
  • 基於vnode在執行patch和diff
  • 使用webpack vue-loader,會在開發環境下編譯模板(重要)

vue組件中使用render代替template

在這裏插入圖片描述

  • 講完模板編譯,再講這個render,就比較好理解了
  • 在有些複雜情況中,不能用template,可以考慮用render
  • React一直都用render(沒有模板),和這裏一樣

總結

  • with語法
  • 模板到render函數,再到vnode,再到渲染和更新
  • vue組件可以用render代替template

組件渲染/更新

  • 一個組件渲染到頁面,修改data觸發更新(數據驅動視圖)
  • 其背後原理是什麼,需要掌握哪些要點
  • 考察對流程瞭解的全面程度

回顧學過的知識

  • 響應式:監聽data屬性getter setter(包括數組)
  • 模板編譯:模板到render函數,再到vnode
  • vdom:patch(elem,vnode)和patch(vnode,newVnode)
  • 初次渲染過程
  • 更新過程
  • 異步渲染

初次渲染過程

  • 解析模板爲render函數(或在開發環境已完成,vue-loader)
  • 觸發響應式,監聽data屬性getter setter
  • 執行render函數,生成vnode,patch(elem,vnode)

在這裏插入圖片描述

  • 修改data,觸發setter(此前在getter中已被監聽)

  • 重新執行render函數,生成newVnode

  • patch(vnode,newVnode)
    在這裏插入圖片描述
    異步渲染

  • 回顧$nextTick

  • 彙總data修改,一次性更新視圖

  • 減少DOM操作次數,提高性能

總結1

  • 渲染和響應式的關係
  • 渲染和模板編譯的關係
  • 渲染和vdom的關係

總結2

  • 初次渲染過程
  • 更新過程
  • 異步渲染

前端路由原理

  • 稍微複雜一點的SPA,都需要路由

  • vue-router也是vue全家桶的標配之一

  • 屬於“和日常使用相關聯的原理”,面試常考

  • 回顧vue-router的路由模式

  • hash

  • H5 history
    在這裏插入圖片描述
    hash的特點

  • hash變化會觸發網頁跳轉,即瀏覽器的前進,後退

  • hash變化不會刷新頁面,SPA必須的特點

  • hash永遠不會提交到server端(前端自生自滅)

如何使用JS實現hash路由

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>hash test</title>
    </head>
    <body>
        <p>hash test</p>
        <button id="btn1">修改 hash</button>
        <script>
            //hash變化,包括:
            //a.JS修改url
            //b.手動修改url的hash
            //c.瀏覽器前進,後退
            window.onhashchange=(event)=>{
                console.log('old url',event.oldURL)
                console.log('new url',event.newURL)
                console.log('hash',location.hash)
            }

            //頁面初次加載,獲取hash
            document.addEventListener('DOMContentLoaded',()=>{
                console.log('hash:',location.hash)
            })

            document.getElementById('btn1').addEventListener('click',()=>{
               location.href='#/user';
            })
        </script>
    </body>
</html>

H5 history

  • 用url規範的路由,但跳轉時不刷新頁面
  • history.pushState
  • window.onpopstate

正常頁面瀏覽
在這裏插入圖片描述
改造成H5 history模式

在這裏插入圖片描述

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible"  content="ie=edge">
        <title>history API test</title>
    </head>
    <body>
        <p>history API test</p>
        <button id="btn1">修改 url</button>
        <script>
            //頁面初次加載,獲取path
            document.addEventListener('DOMContentLoaded',()=>{
                console.log('load',location.pathname)
            })

            //打開一個新的路由
            //【注意】用pushState方式,瀏覽器不會刷新頁面
            document.getElementById('btn1').addEventListener('click',()=>{
                const state={name:'page1'}
                console.log('切換路由到','page1')
                history.pushState(state,'','page1')
            })

            //監聽瀏覽器前進,後退
            window.onpopstate=(event)=>{
                console.log('onpopstate',event.state,location.pathname);
            }

            //需要server 端配合,可參考
            //https://router.vuejs.org/zh/guide/essentials/history-mo
        </script>
    </body>
</html>

總結

  • hash——window.onhashchange
  • H5 history——history.pushState和window.onpopstate
  • H5 history 需要後端支持

兩者選擇

  • to B的系統推薦用hash,簡單易用,對url規範不敏感
  • to C的系統,可以考慮選擇H5 history,但需要服務端支持
  • 能選擇簡單的,就別用複雜的,要考慮成本和收益

Vue原理——總結

  • 組件化

  • 響應式

  • vdom和diff

  • 模板編譯

  • 渲染過程

  • 前端路由

  • 組件化的歷史

  • 數據驅動視圖

  • MVVM

  • Object.defineProperty

  • 監聽對象(深度),監聽數組

  • Object.defineProperty的缺點(Vue 3用Proxy,後面會講)

vdom和diff

  • 應用背景
  • vnode結構
  • snabbdom使用:vnode h patch

模板編譯

  • with語法
  • 模板編譯爲render函數
  • 執行render函數生成vnode

組件渲染/更新過程

  • 初次渲染過程
  • 更新過程
  • 異步渲染

前端路由原理

  • hash

  • H5 history

  • 兩者對比

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章