Vue 雙向綁定原理
?????? mvvm 雙向綁定,采用數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式,通過 Object.defineProperty()來劫持各個屬性的
setter、getter,在數(shù)據(jù)變動時(shí)發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)。
幾個要點(diǎn):
1、實(shí)現(xiàn)一個數(shù)據(jù)監(jiān)聽器 Observer,能夠?qū)?shù)據(jù)對象的所有屬性進(jìn)行監(jiān)聽,如有變動可拿到最新值并通知訂閱者
2、實(shí)現(xiàn)一個指令解析器 Compile,對每個元素節(jié)點(diǎn)的指令進(jìn)行掃描和解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)
3、實(shí)現(xiàn)一個 Watcher,作為連接 Observer 和 Compile
的橋梁,能夠訂閱并收到每個屬性變動的通知,執(zhí)行指令綁定的相應(yīng)回調(diào)函數(shù),從而更新視圖
4、mvvm 入口函數(shù),整合以上三者
具體步驟:
* 需要 observe 的數(shù)據(jù)對象進(jìn)行遞歸遍歷,包括子屬性對象的屬性,都加上 setter 和 getter
這樣的話,給這個對象的某個值賦值,就會觸發(fā) setter,那么就能監(jiān)聽到了數(shù)據(jù)變化
* compile
解析模板指令,將模板中的變量替換成數(shù)據(jù),然后初始化渲染頁面視圖,并將每個指令對應(yīng)的節(jié)點(diǎn)綁定更新函數(shù),添加監(jiān)聽數(shù)據(jù)的訂閱者,一旦數(shù)據(jù)有變動,收到通知,更新視圖
* Watcher 訂閱者是 Observer 和 Compile 之間通信的橋梁,主要做的事情是:
* 在自身實(shí)例化時(shí)往屬性訂閱器(dep)里面添加自己
* 自身必須有一個 update() 方法
* 待屬性變動 dep.notice() 通知時(shí),能調(diào)用自身的 update() 方法,并觸發(fā) Compile 中綁定的回調(diào),則功成身退。
* MVVM 作為數(shù)據(jù)綁定的入口,整合 Observer、Compile 和 Watcher 三者,通過Observer來監(jiān)聽自己的 model
數(shù)據(jù)變化,通過 Compile 來解析編譯模板指令,最終利用 Watcher 搭起 Observer 和 Compile 之間的通信橋梁,達(dá)到數(shù)據(jù)變化 ->
視圖更新;視圖交互變化(input) -> 數(shù)據(jù) model 變更的雙向綁定效果。
描述下 vue 從初始化頁面--修改數(shù)據(jù)--刷新頁面 UI 的過程?
??????當(dāng) Vue 進(jìn)入初始化階段時(shí),一方面 Vue 會遍歷 data 中的屬性,并用 Object.defineProperty 將它轉(zhuǎn)化成
getter/setter 的形式,實(shí)現(xiàn)數(shù)據(jù)劫持(暫不談 Vue3.0 的 Proxy);另一方面,Vue 的指令編譯器 Compiler
對元素節(jié)點(diǎn)的各個指令進(jìn)行解析,初始化視圖,并訂閱 Watcher 來更新試圖,此時(shí) Watcher 會將自己添加到消息訂閱器 Dep 中,此時(shí)初始化完畢。
??????當(dāng)數(shù)據(jù)發(fā)生變化時(shí),觸發(fā) Observer 中 setter 方法,立即調(diào)用 Dep.notify(),Dep
這個數(shù)組開始遍歷所有的訂閱者,并調(diào)用其 update 方法,Vue 內(nèi)部再通過 diff 算法,patch 相應(yīng)的更新完成對訂閱者視圖的改變。
你是如何理解 Vue 的響應(yīng)式系統(tǒng)的?
響應(yīng)式系統(tǒng)簡述:
* 任何一個 Vue Component 都有一個與之對應(yīng)的 Watcher 實(shí)例
* Vue 的 data 上的屬性會被添加 getter 和 setter 屬性
* 當(dāng) Vue Component render 函數(shù)被執(zhí)行的時(shí)候, data 上會被 觸碰(touch), 即被讀, getter 方法會被調(diào)用, 此時(shí)
Vue 會去記錄此 Vue component 所依賴的所有 data。(這一過程被稱為依賴收集)
* data 被改動時(shí)(主要是用戶操作), 即被寫, setter 方法會被調(diào)用, 此時(shí) Vue 會去通知所有依賴于此 data 的組件去調(diào)用他們的
render 函數(shù)進(jìn)行更新
虛擬 DOM 實(shí)現(xiàn)原理
* 虛擬DOM本質(zhì)上是JavaScript對象,是對真實(shí)DOM的抽象
* 狀態(tài)變更時(shí),記錄新樹和舊樹的差異
*
最后把差異更新到真正的dom中
詳細(xì)實(shí)現(xiàn)見面試官: 你對虛擬DOM原理的理解?
<https://user-gold-cdn.xitu.io/2019/8/1/16c49afec13e0416>
既然 Vue 通過數(shù)據(jù)劫持可以精準(zhǔn)探測數(shù)據(jù)變化,為什么還需要虛擬 DOM 進(jìn)行 diff 檢測差異?
考點(diǎn): Vue 的變化偵測原理
前置知識: 依賴收集、虛擬 DOM、響應(yīng)式系統(tǒng)
現(xiàn)代前端框架有兩種方式偵測變化,一種是pull,一種是push
pull:
其代表為React,我們可以回憶一下React是如何偵測到變化的,我們通常會用setStateAPI顯式更新,然后React會進(jìn)行一層層的Virtual
Dom
Diff操作找出差異,然后Patch到DOM上,React從一開始就不知道到底是哪發(fā)生了變化,只是知道「有變化了」,然后再進(jìn)行比較暴力的Diff操作查找「哪發(fā)生變化了」,另外一個代表就是Angular的臟檢查操作。
push:
Vue的響應(yīng)式系統(tǒng)則是push的代表,當(dāng)Vue程序初始化的時(shí)候就會對數(shù)據(jù)data進(jìn)行依賴的收集,一但數(shù)據(jù)發(fā)生變化,響應(yīng)式系統(tǒng)就會立刻得知。因此Vue是一開始就知道是「在哪發(fā)生變化了」,但是這又會產(chǎn)生一個問題,如果你熟悉Vue的響應(yīng)式系統(tǒng)就知道,通常一個綁定一個數(shù)據(jù)就需要一個Watcher,一但我們的綁定細(xì)粒度過高就會產(chǎn)生大量的Watcher,這會帶來內(nèi)存以及依賴追蹤的開銷,而細(xì)粒度過低會無法精準(zhǔn)偵測變化,因此Vue的設(shè)計(jì)是選擇中等細(xì)粒度的方案,在組件級別進(jìn)行push偵測的方式,也就是那套響應(yīng)式系統(tǒng),通常我們會第一時(shí)間偵測到發(fā)生變化的組件,然后在組件內(nèi)部進(jìn)行Virtual
Dom Diff獲取更加具體的差異,而Virtual Dom Diff則是pull操作,Vue是push+pull結(jié)合的方式進(jìn)行變化偵測的。
Vue和React的視圖更新機(jī)制對比 <https://blog.csdn.net/csdn_haow/article/details/89915908>
Vue 中 key 值的作用?
??????當(dāng) Vue.js 用 v-for 正在更新已渲染過的元素列表時(shí),它默認(rèn)用“就地復(fù)用”策略。如果數(shù)據(jù)項(xiàng)的順序被改變,Vue 將不會移動 DOM
元素來匹配數(shù)據(jù)項(xiàng)的順序, 而是簡單復(fù)用此處每個元素,并且確保它在特定索引下顯示已被渲染過的每個元素。key 的作用主要是為了高效的更新虛擬DOM。
Vue 的生命周期
vue生命周期詳解 <https://juejin.im/post/5c6d48e36fb9a049eb3c84ff>
* beforeCreate和created
* beforeMount和mounted
* beforeUpdate和updated
* beforeDestory和destoryed
* activated和deactivated
Vue 組件間通信有哪些方式?
Vue 組件間通信六種方式 <https://juejin.im/post/5cde0b43f265da03867e78d3>
* props/$emit
* $emit/$on
* vuex
* $attrs/$listeners
* provide/inject
* $parent/$children 與 ref
watch、methods 和 computed 的區(qū)別?
* watch 為了監(jiān)聽某個響應(yīng)數(shù)據(jù)的變化。computed 是自動監(jiān)聽依賴值
的變化,從而動態(tài)返回內(nèi)容,主要目的是簡化模板內(nèi)的復(fù)雜運(yùn)算。所以區(qū)別來源于用法,只是需要動態(tài)值,那就用 computed ;需要知道值的改變后執(zhí)行業(yè)務(wù)邏輯,才用
watch。
* methods是一個方法,它可以接受參數(shù),而computed 不能,computed 是可以緩存的,methods 不會。computed
可以依賴其他 computed,甚至是其他組件的 data。
vue 中怎么重置 data?
使用Object.assign(),vm.$data可以獲取當(dāng)前狀態(tài)下的data,vm.$options.data可以獲取到組件初始化狀態(tài)下的data。
Object.assign(this.$data, this.$options.data())
組件中寫 name 選項(xiàng)有什么作用?
* 項(xiàng)目使用 keep-alive 時(shí),可搭配組件 name 進(jìn)行緩存過濾
* DOM 做遞歸組件時(shí)需要調(diào)用自身 name
* vue-devtools 調(diào)試工具里顯示的組見名稱是由vue中組件name決定的
vue-router 有哪些鉤子函數(shù)?
官方文檔:vue-router鉤子函數(shù)
<https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD%AE%E5%AE%88%E5%8D%AB>
* 全局前置守衛(wèi) router.beforeEach
* 全局解析守衛(wèi) router.beforeResolve
* 全局后置鉤子 router.afterEach
* 路由獨(dú)享的守衛(wèi) beforeEnter
* 組件內(nèi)的守衛(wèi) beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
前端路由簡介以及vue-router實(shí)現(xiàn)原理 <https://juejin.im/post/5b10b46df265da6e2a08a724>
route 和 router 的區(qū)別是什么?
route是“路由信息對象”,包括path,params,hash,query,fullPath,matched,name等路由信息參數(shù)。
router是“路由實(shí)例對象”,包括了路由的跳轉(zhuǎn)方法(push、replace),鉤子函數(shù)等。
說一下 Vue 和 React 的認(rèn)識,做一個簡單的對比
1.監(jiān)聽數(shù)據(jù)變化的實(shí)現(xiàn)原理不同
* Vue 通過 getter/setter 以及一些函數(shù)的劫持,能精確快速的計(jì)算出 Virtual DOM
的差異。這是由于它在渲染過程中,會跟蹤每一個組件的依賴關(guān)系,不需要重新渲染整個組件樹。
* React 默認(rèn)是通過比較引用的方式進(jìn)行的,如果不優(yōu)化,每當(dāng)應(yīng)用的狀態(tài)被改變時(shí),全部子組件都會重新渲染,可能導(dǎo)致大量不必要的 VDOM 的重新渲染。
??????Vue 不需要特別的優(yōu)化就能達(dá)到很好的性能,而對于 React 而言,需要通過
PureComponent/shouldComponentUpdate 這個生命周期方法來進(jìn)行控制。如果你的應(yīng)用中,交互復(fù)雜,需要處理大量的 UI
變化,那么使用 Virtual DOM 是一個好主意。如果你更新元素并不頻繁,那么 Virtual DOM 并不一定適用,性能很可能還不如直接操控 DOM。
??????為什么 React 不精確監(jiān)聽數(shù)據(jù)變化呢?這是因?yàn)?Vue 和 React 設(shè)計(jì)理念上的區(qū)別,Vue 使用的是可變數(shù)據(jù),而 React
更強(qiáng)調(diào)數(shù)據(jù)的不可變。
2.數(shù)據(jù)流的不同
* Vue 中默認(rèn)支持雙向綁定,組件與 DOM 之間可以通過 v-model 雙向綁定。但是,父子組件之間,props 在 2.x 版本是單向數(shù)據(jù)流
* React 一直提倡的是單向數(shù)據(jù)流,他稱之為 onChange/setState()模式。
??????不過由于我們一般都會用 Vuex 以及 Redux 等單向數(shù)據(jù)流的狀態(tài)管理框架,因此很多時(shí)候我們感受不到這一點(diǎn)的區(qū)別了。
3.模板渲染方式的不同
在表層上,模板的語法不同
* React 是通過 JSX 渲染模板
* 而 Vue 是通過一種拓展的 HTML 語法進(jìn)行渲染
在深層上,模板的原理不同,這才是他們的本質(zhì)區(qū)別:
* React 是在組件 JS 代碼中,通過原生 JS 實(shí)現(xiàn)模板中的常見語法,比如插值,條件,循環(huán)等,都是通過 JS 語法實(shí)現(xiàn)的
* Vue 是在和組件 JS 代碼分離的單獨(dú)的模板中,通過指令來實(shí)現(xiàn)的,比如條件語句就需要 v-if 來實(shí)現(xiàn)
??????對這一點(diǎn),我個人比較喜歡 React 的做法,因?yàn)樗蛹兇飧釉?,?Vue 的做法顯得有些獨(dú)特,會把 HTML 弄得很亂。舉個例子,說明
React 的好處:react 中 render 函數(shù)是支持閉包特性的,所以我們 import 的組件在 render 中可以直接調(diào)用。但是在 Vue
中,由于模板中使用的數(shù)據(jù)都必須掛在 this 上進(jìn)行一次中轉(zhuǎn),所以我們 import 一個組件完了之后,還需要在 components
中再聲明下,這樣顯然是很奇怪但又不得不這樣的做法。
Vue 的 nextTick 的原理是什么?
1. 為什么需要 nextTick
??????Vue 是異步修改 DOM 的并且不鼓勵開發(fā)者直接接觸 DOM,但有時(shí)候業(yè)務(wù)需要必須對數(shù)據(jù)更改--刷新后的 DOM
做相應(yīng)的處理,這時(shí)候就可以使用 Vue.nextTick(callback)這個 api 了。
2. 理解原理前的準(zhǔn)備
??????首先需要知道事件循環(huán)中宏任務(wù)和微任務(wù)這兩個概念(這其實(shí)也是面試??键c(diǎn))。請閱大佬文章--徹底搞懂瀏覽器 Event-loop
<https://juejin.im/post/5c947bca5188257de704121d>
常見的宏任務(wù)有 script, setTimeout, setInterval, setImmediate, I/O, UI rendering
常見的微任務(wù)有 process.nextTick(Nodejs),Promise.then(), MutationObserver;
3. 理解 nextTick
??????而 nextTick 的原理正是 vue 通過異步隊(duì)列控制 DOM 更新和 nextTick
回調(diào)函數(shù)先后執(zhí)行的方式。如果大家看過這部分的源碼,會發(fā)現(xiàn)其中做了很多 isNative()的判斷,因?yàn)檫@里還存在兼容性優(yōu)雅降級的問題。可見 Vue
開發(fā)團(tuán)隊(duì)的深思熟慮,對性能的良苦用心。
如果你比較了解了前面的事件循環(huán)原理,推薦你看看這篇文章 請閱大佬文章--全面解析 Vue.nextTick 實(shí)現(xiàn)原理
<https://mp.weixin.qq.com/s/mCcW4OYj3p3471ghMBylBw>
Vuex 有哪幾種屬性?
??????有五種,分別是 State、Getter、Mutation、Action、Module
Vue 3.0有沒有過了解?
??????關(guān)于Vue 3.0有幸看過尤大的關(guān)于3.0版本的RFC Vue Function-based API RFC
<https://zhuanlan.zhihu.com/p/68477600>。大致說了三個點(diǎn),第一個是關(guān)于提出的新API setup()
函數(shù),第二個說了對于Typescript的支持,最后說了關(guān)于替換Object.defineProperty為Proxy的支持。
??????詳細(xì)說了下關(guān)于Proxy代替帶來的性能上的提升,因?yàn)閭鹘y(tǒng)的原型鏈攔截的方法,無法檢測對象及數(shù)組的一些更新操作,但使用Proxy又帶來了瀏覽器兼容問題。
vue-cli 替我們做了哪些工作?
首先需要知道 vue-cli 是什么?它是基于 Vue.js 進(jìn)行快速開發(fā)的完整系統(tǒng),也可以理解成是很多 npm 包的集合。其次,vue-cli
完成的功能有哪些?
.vue 文件 --> .js 文件
ES6 語法 --> ES5 語法
Sass,Less,Stylus --> CSS
對 jpg,png,font 等靜態(tài)資源的處理
熱更新
定義環(huán)境變量,區(qū)分 dev 和 production 模式
...
如果開發(fā)者需要補(bǔ)充或修改默認(rèn)設(shè)置,需要在 package.json 同級下新建一個 vue.config.js 文件
更多vue面試題:
面試必備的13道可以舉一反三的Vue面試題 <https://juejin.im/post/5d41eec26fb9a06ae439d29f>
vue 248個知識點(diǎn)(面試題)為你保駕護(hù)航
<https://juejin.im/post/5d153267e51d4510624f9809?tdsourcetag=s_pctim_aiomsg>
2019前端面試系列——CSS面試題 <https://www.cnblogs.com/chenwenhao/p/11217590.html>
2019前端面試系列——JS面試題 <https://www.cnblogs.com/chenwenhao/p/11253403.html>
2019前端面試系列——JS高頻手寫代碼題 <https://www.cnblogs.com/chenwenhao/p/11294541.html>
2019前端面試系列——Vue面試題 <https://www.cnblogs.com/chenwenhao/p/11258895.html>
2019前端面試系列——HTTP、瀏覽器面試題 <https://www.cnblogs.com/chenwenhao/p/11267238.html>
熱門工具 換一換