Vue.js是一個(gè)流行的JavaScript框架,它使得構(gòu)建復(fù)雜的交互式應(yīng)用程序變得更容易。Vue.js基于MVVM模式設(shè)計(jì),采用了響應(yīng)式數(shù)據(jù)綁定和組件化的架構(gòu)。在Vue.js中,數(shù)據(jù)綁定是非常重要的概念,它通過(guò)使用觀察者模式來(lái)追蹤數(shù)據(jù)變化并自動(dòng)更新DOM。
Vue.js源碼是一個(gè)龐大而復(fù)雜的項(xiàng)目,但通過(guò)掌握其核心概念,我們可以更好地理解其工作原理。在本文中,我們討論了Vue.js的響應(yīng)式數(shù)據(jù)綁定、虛擬DOM、模板編譯、組件、生命周期鉤子、事件處理和指令等關(guān)鍵知識(shí)點(diǎn)。了解這些知識(shí)點(diǎn)可以讓我們更好地編寫Vue.js應(yīng)用程序,并深入了解Vue.js的工作原理
【資料圖】
Vue.js提供了計(jì)算屬性和偵聽(tīng)器來(lái)處理數(shù)據(jù)的變化。計(jì)算屬性是用于計(jì)算和緩存的屬性,而偵聽(tīng)器則允許你監(jiān)聽(tīng)數(shù)據(jù)的變化并執(zhí)行特定的操作。這兩個(gè)概念都是基于Vue.js的響應(yīng)式數(shù)據(jù)綁定實(shí)現(xiàn)的。
計(jì)算屬性的實(shí)現(xiàn)是通過(guò)使用Object.defineProperty()方法來(lái)定義getter和setter方法。當(dāng)計(jì)算屬性依賴的數(shù)據(jù)發(fā)生變化時(shí),計(jì)算屬性會(huì)重新計(jì)算,這樣可以避免重復(fù)計(jì)算。偵聽(tīng)器則是通過(guò)使用Watcher對(duì)象來(lái)實(shí)現(xiàn)的。
Virtual DOM是Vue.js的一個(gè)核心概念,它是一個(gè)“輕量級(jí)”的DOM副本,作為內(nèi)存中的JavaScript對(duì)象存在。每次數(shù)據(jù)發(fā)生變化時(shí),Vue.js會(huì)計(jì)算需要更新的最小DOM子樹,然后只更新這些部分。這種方法比直接操作真實(shí)DOM要快得多。
在Vue.js中,虛擬DOM由VNode類來(lái)表示。VNode類有一些屬性:tag、data、children等。VNode實(shí)例通常通過(guò)createElement()方法創(chuàng)建,該方法返回一個(gè)VNode實(shí)例。
Vue.js使用模板來(lái)描述應(yīng)用程序的界面,而模板編譯是將模板轉(zhuǎn)換為渲染函數(shù)的過(guò)程。在Vue.js中,模板編譯是由template編譯器來(lái)處理的。它將模板解析為AST(抽象語(yǔ)法樹),然后將AST轉(zhuǎn)換為渲染函數(shù)。
Vue.js的模板編譯器是獨(dú)立的,可以在瀏覽器中運(yùn)行。在開發(fā)環(huán)境中,模板編譯器會(huì)被自動(dòng)加載,并且Vue.js還提供了一個(gè)單獨(dú)的運(yùn)行時(shí)構(gòu)建,不包含模板編譯器。這意味著你需要在構(gòu)建工具中對(duì)模板進(jìn)行預(yù)編譯,或者使用手動(dòng)渲染函數(shù)。
組件是Vue.js的另一個(gè)核心概念,它允許你構(gòu)建可重用和可組合的UI組件。在Vue.js中,每個(gè)組件都是一個(gè)Vue實(shí)例,并且可以包含其他組件。組件可以接收屬性(props)和發(fā)射事件,以便與其他組件進(jìn)行通信。
Vue.js中的組件是通過(guò)Vue.extend()方法來(lái)創(chuàng)建的。該方法將基本Vue類與組件定義合并,并返回一個(gè)新的構(gòu)造函數(shù)。然后可以在應(yīng)用程序中使用自定義標(biāo)記(例如
Vue.js的生命周期鉤子是一系列函數(shù),它們定義了Vue實(shí)例在不同階段執(zhí)行的操作。這些階段包括:創(chuàng)建、掛載、更新和銷毀。生命周期鉤子可以在Vue實(shí)例的選項(xiàng)對(duì)象中定義。
在Vue.js中有7個(gè)生命周期鉤子:
created: 在Vue實(shí)例創(chuàng)建后調(diào)用,但在模板渲染之前。
mounted: 在Vue實(shí)例掛載到DOM上后調(diào)用。
updated: 在Vue實(shí)例數(shù)據(jù)被更新后調(diào)用,但在DOM重新渲染之前。
destroyed: 在Vue實(shí)例銷毀之前調(diào)用。
beforeCreate: 在Vue實(shí)例創(chuàng)建之前調(diào)用。
beforeMount: 在Vue實(shí)例掛載到DOM之前調(diào)用。
beforeUpdate: 在Vue實(shí)例數(shù)據(jù)更新之前調(diào)用,但在DOM重新渲染之前。
在Vue.js中,你可以使用v-on指令來(lái)綁定DOM事件。例如,你可以使用v-on:click來(lái)監(jiān)聽(tīng)點(diǎn)擊事件。事件處理程序可以是內(nèi)聯(lián)函數(shù),也可以是Vue.js組件的方法。事件處理程序可以接收一個(gè)事件對(duì)象作為參數(shù)。
在Vue.js的事件處理中,事件是經(jīng)過(guò)封裝的。在組件內(nèi)部使用$emit方法觸發(fā)事件,在組件之間使用$on來(lái)監(jiān)聽(tīng)事件。這樣可以避免直接操作dom元素,使代碼更加清晰和易于維護(hù)。
Vue.js中的指令是特殊的HTML屬性,它們可以用于指定某些特殊行為。例如,v-if和v-for指令用于條件渲染和循環(huán)渲染。指令可以接收表達(dá)式作為參數(shù),并可以在表達(dá)式變化時(shí)進(jìn)行更新。
Vue.js提供了一些內(nèi)置指令,包括v-model、v-bind、v-on等。我們可以自定義指令來(lái)擴(kuò)展Vue.js的功能。自定義指令需要使用Vue.directive()方法來(lái)定義。
MVVM是Model-View-ViewModel縮寫,也就是把MVC中的Controller演變成ViewModel。Model層代表數(shù)據(jù)模型,View代表UI組件,ViewModel是View和Model層的橋梁,數(shù)據(jù)會(huì)綁定到viewModel層并自動(dòng)將數(shù)據(jù)渲染到頁(yè)面中,視圖變化的時(shí)候會(huì)通知viewModel層更新數(shù)據(jù)。
父子組件通信
父->子props,子->父 $on、$emit獲取父子組件實(shí)例 parent、parent、children Ref 獲取實(shí)例的方式調(diào)用組件的屬性或者方法 Provide、inject
官方不推薦使用,但是寫組件庫(kù)時(shí)很常用
兄弟組件通信
Event Bus 實(shí)現(xiàn)跨組件通信 Vue.prototype.$bus = new Vue() Vuex
跨級(jí)組件通信
$attrs、$listeners Provide、inject
對(duì)于Vue 這類漸進(jìn)式前端開發(fā)框架,為了構(gòu)建SPA(單頁(yè)面應(yīng)用),需要引入前端路由系統(tǒng),這也就是Vue-router存在的意義。前端路由的核心,就在于改變視圖的同時(shí)不會(huì)向后端發(fā)出請(qǐng)求。
1、hash ——即地址欄URL中的#符號(hào),它的特點(diǎn)在于:hash 雖然出現(xiàn)URL中,但不會(huì)被包含在HTTP請(qǐng)求中,對(duì)后端完全沒(méi)有影響,因此改變hash不會(huì)重新加載頁(yè)面。
2、history ——利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法。這兩個(gè)方法應(yīng)用于瀏覽器的歷史記錄站,在當(dāng)前已有的back、forward、go 的基礎(chǔ)之上,它們提供了對(duì)歷史記錄進(jìn)行修改的功能。只是當(dāng)它們執(zhí)行修改是,雖然改變了當(dāng)前的URL,但你瀏覽器不會(huì)立即向后端發(fā)送請(qǐng)求。history模式,會(huì)出現(xiàn)404 的情況,需要后臺(tái)配置。
當(dāng)一個(gè)Vue實(shí)例創(chuàng)建時(shí),Vue會(huì)遍歷data選項(xiàng)的屬性,用 Object.defineProperty 將它們轉(zhuǎn)為 getter/setter并且在內(nèi)部追蹤相關(guān)依賴,在屬性被訪問(wèn)和修改時(shí)通知變化。每個(gè)組件實(shí)例都有相應(yīng)的 watcher 程序?qū)嵗?,它?huì)在組件渲染的過(guò)程中把屬性記錄為依賴,之后當(dāng)依賴項(xiàng)的 setter 被調(diào)用時(shí),會(huì)通知 watcher重新計(jì)算,從而致使它關(guān)聯(lián)的組件得以更新。
initProxy:作用域代理,攔截組件內(nèi)訪問(wèn)其它組件的數(shù)據(jù)。
initLifecycle:建立父子組件關(guān)系,在當(dāng)前組件實(shí)例上添加一些屬性和生命周期標(biāo)識(shí)。如Math Processing Errorparent,parent,refs,$children,_isMounted等。
initEvents:對(duì)父組件傳入的事件添加監(jiān)聽(tīng),事件是誰(shuí)創(chuàng)建誰(shuí)監(jiān)聽(tīng),子組件創(chuàng)建事件子組件監(jiān)聽(tīng)
initRender:聲明Math Processing Errorslots和slots和createElement()等。
initInjections:注入數(shù)據(jù),初始化inject,一般用于組件更深層次之間的通信。
initState:重要)數(shù)據(jù)響應(yīng)式:初始化狀態(tài)。很多選項(xiàng)初始化的匯總:data,methods,props,computed和watch。
initProvide:提供數(shù)據(jù)注入。
思考:為什么先注入再提供呢??
1、首先來(lái)自祖輩的數(shù)據(jù)要和當(dāng)前實(shí)例的data,等判重,相結(jié)合,所以注入數(shù)據(jù)的initInjections一定要在InitState的上面。
從上面注入進(jìn)來(lái)的東西在當(dāng)前組件中轉(zhuǎn)了一下又提供給后代了,所以注入數(shù)據(jù)也一定要在上面。keep-alive是Vue的內(nèi)置組件,實(shí)現(xiàn)組件緩存。當(dāng)它包裹動(dòng)態(tài)組件時(shí),會(huì)緩存不活動(dòng)的組件實(shí)例,而不是銷毀。
keep-alive是系統(tǒng)自帶的一個(gè)組件,用來(lái)緩存組件,避免多次加載相同的組件,減少性能消耗,提高用戶體驗(yàn)。
例如我們可以在列表頁(yè)進(jìn)入詳情頁(yè)使用。如果在列表頁(yè)點(diǎn)擊的都是相同的 ,詳情頁(yè)就不用請(qǐng)求多次了,直接緩存起來(lái)就行了,如果點(diǎn)擊的不同,則需要重新請(qǐng)求數(shù)據(jù)
vuex是一個(gè)專門為vue.js應(yīng)用程序開發(fā)的狀態(tài)管理庫(kù)。 核心概念:
state(單一狀態(tài)樹) getter/Mutation顯示提交更改state
Action類似Mutation,提交Mutation,可以包含任意異步操作。
module(當(dāng)應(yīng)用變得龐大復(fù)雜,拆分store為具體的module模塊)
在js中,渲染真實(shí)DOM的開銷是非常大的, 比如我們修改了某個(gè)數(shù)據(jù),如果直接渲染到真實(shí)DOM, 會(huì)引起整個(gè)dom樹的重繪和重排。那么有沒(méi)有可能實(shí)現(xiàn)只更新我們修改的那一小塊dom而不要更新整個(gè)dom呢?此時(shí)我們就需要先根據(jù)真實(shí)dom生成虛擬dom, 當(dāng)虛擬dom某個(gè)節(jié)點(diǎn)的數(shù)據(jù)改變后會(huì)生成有一個(gè)新的Vnode, 然后新的Vnode和舊的Vnode作比較,發(fā)現(xiàn)有不一樣的地方就直接修改在真實(shí)DOM上,然后使舊的Vnode的值為新的Vnode。
diff的過(guò)程就是調(diào)用patch函數(shù),比較新舊節(jié)點(diǎn),一邊比較一邊給真實(shí)的DOM打補(bǔ)丁。在采取diff算法比較新舊節(jié)點(diǎn)的時(shí)候,比較只會(huì)在同層級(jí)進(jìn)行。 在patch方法中,首先進(jìn)行樹級(jí)別的比較 new Vnode不存在就刪除 old Vnodeold Vnode 不存在就增加新的Vnode 都存在就執(zhí)行diff更新 當(dāng)確定需要執(zhí)行diff算法時(shí),比較兩個(gè)Vnode,包括三種類型操作:屬性更新,文本更新,子節(jié)點(diǎn)更新 新老節(jié)點(diǎn)均有子節(jié)點(diǎn),則對(duì)子節(jié)點(diǎn)進(jìn)行diff操作,調(diào)用updatechidren 如果老節(jié)點(diǎn)沒(méi)有子節(jié)點(diǎn)而新節(jié)點(diǎn)有子節(jié)點(diǎn),先清空老節(jié)點(diǎn)的文本內(nèi)容,然后為其新增子節(jié)點(diǎn) 如果新節(jié)點(diǎn)沒(méi)有子節(jié)點(diǎn),而老節(jié)點(diǎn)有子節(jié)點(diǎn)的時(shí)候,則移除該節(jié)點(diǎn)的所有子節(jié)點(diǎn) 老新老節(jié)點(diǎn)都沒(méi)有子節(jié)點(diǎn)的時(shí)候,進(jìn)行文本的替換
updateChildren 將Vnode的子節(jié)點(diǎn)Vch和oldVnode的子節(jié)點(diǎn)oldCh提取出來(lái)。 oldCh和vCh各有兩個(gè)頭尾的變量StartIdx和EndIdx,它們的2個(gè)變量相互比較,一共有4種比較方式。如果4種比較都沒(méi)匹配,如果設(shè)置了key,就會(huì)用key進(jìn)行比較,在比較的過(guò)程中,變量會(huì)往中間靠,一旦StartIdx>EndIdx表明oldCh和vCh至少有一個(gè)已經(jīng)遍歷完了,就會(huì)結(jié)束比較。
關(guān)鍵詞: