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