如何理解mvvm原理


MVVM框架理解

說起這個MVVM模型,就不得不說MVC框架。

bg2015020105.png

將整個前端頁面分成View,Controller,Modal,視圖上發生變化,通過Controller(控件)將響應傳入到Model(數據源),由數據源改變View上面的數據。

整個過程看起來是行雲流水,業務邏輯放在Model當中,頁面渲染邏輯放在View當中,但在實際運用上卻存在一個問題:那就是MVC框架允許View和Model直接進行通信!!

換句話說,View和Model之間隨着業務量的不斷龐大,會出現蜘蛛網一樣難以處理的依賴關系,完全背離了開發所應該遵循的“開放封閉原則”。

面對這個問題,MVVM框架就出現了,它與MVC框架的主要區別有兩點:
1、實現數據與視圖的分離
2、通過數據來驅動視圖,開發者只需要關心數據變化,DOM操作被封裝了。

hEQAEJACAgBISAEhIAQEAJCQAgIASHgigT+Dy+Ux4EFPXIkAAAAAElFTkSuQmCC

可以看到MVVM分別指View,Model,View-Model,View通過View-Model的DOM Listeners將事件綁定到Model上,而Model則通過Data Bindings來管理View中的數據,View-Model從中起到一個連接橋的作用。

MVVM的實現原理:

MVVM的實現主要是三個核心點:

  1. 響應式:vue如何監聽data的屬性變化
  2. 模板解析:vue的模板是如何被解析的
  3. 渲染:vue模板是如何被渲染成HTML的

響應式:

對於MVVM來說,data一般是放在一個對象當中,就比如這樣:

         var obj = { name: 'zhangsan', age: 25 }

當我們訪問或修改obj的屬性的時候,比如:

         console.log(obj.name) //訪問 obj.age = 22 //修改

但是這樣的操作vue本身是沒有辦法感知到的,那么應該如何讓vue知道我們進行了訪問或是修改的操作呢?
那就要使用Object.defineProperty

        var vm = {} var data = { name: 'zhangsan', age: 20 } var key, value for (key in data) { (function (key) { Object.defineProperty(vm, key, { get: function () { console.log('get', data[key]) // 監聽 return data[key] }, set: function (newVal) { console.log('set', newVal) // 監聽 data[key] = newVal } }) })(key) } 

通過Object.defineProperty將data里的每一個屬性的訪問與修改都變成了一個函數,在函數get和set中我們即可監聽到data的屬性發生了改變。

模板解析:

首先模板是什么?

模板本質上是一串字符串,它看起來和html的格式很相像,實際上有很大的區別,因為模板本身還帶有邏輯運算,比如v-if,v-for等等,但它最后還是要轉換為html來顯示。

    <div id="app"> <div> <input v-model="title"> <button v-on:click="add">submit</button> </div> <div> <ul> <li v-for="item in list">{{item}}</li> </ul> </div> </div>

模板在vue中必須轉換為JS代碼,原因在於:在前端環境下,只有JS才是一個圖靈完備語言,才能實現邏輯運算,以及渲染為html頁面。

這里就引出了vue中一個特別重要的函數——render

render函數中的核心就是with函數。

with函數將某個對象添加到作用域鏈的頂部,如果在 statement中有某個未使用命名空間的變量,跟作用域鏈中的某個屬性同名,則這個變量將指向這個屬性值。

舉個例子:

       var obj = { name: 'zhangsan', age: 20, getAddress: function () { alert('beijing') } } function fn1() { with(obj) { alert(age) alert(name) getAddress() } } fn1()

with將obj這個對象放在了自己函數的作用域鏈的頂部,當執行下列函數時,就會自動到obj這個對象去尋找同名的屬性。

而在render函數中,with的用法是這樣:

    <div id="app"> <div> <input v-model="title"> <button v-on:click="add">submit</button> </div> <div> <ul> <li v-for="item in list">{{item}}</li> </ul> </div> </div>
        // 對應的js文件 var data = { title: '', list: [] } // 初始化 Vue 實例 var vm = new Vue({ el: '#app', data: data, methods: { add: function () { this.list.push(this.title) this.title = '' } } }) with(this){ // this 就是 vm return _c( 'div', { attrs:{"id":"app"} }, [ _c( 'div', [ _c( 'input', { directives:[ { name:"model", rawName:"v-model", value:(title), expression:"title" } ], domProps:{ "value":(title) }, on:{ "input":function($event){ if($event.target.composing)return; title=$event.target.value } } } ), _v(" "), _c( 'button', { on:{ "click":add } }, [_v("submit")] ) ] ), _v(" "), _c('div', [ _c( 'ul', _l((list),function(item){return _c('li',[_v(_s(item))])}) ) ] ) ] ) }

在一開始,因為new操作符,所以this指向了vm,通過with我們將vm這個對象放在作用域鏈的頂部,因為在函數內部我們會多次調用vm內部的屬性,所以使用with可以縮短變量長度,提供系統運行效率。

其中的_c函數表示的是創建一個新的html元素,其基本用法為:

_c(element,{attrs},[children...])

其中的element表示所要創建的html元素類型,attrs表示所要創建的元素的屬性,children表示該html元素的子元素。

_v函數表示創建一個文本節點,_l函數表示創建一個數組。

最終render函數返回的是一個虛擬DOM。

如何將模板渲染為html

模板渲染為html分為兩種情況,第一種是初次渲染的時候,第二種是渲染之后數據發生改變的時候,它們都需要調用updateComponent,其形式如下:

vm._update(vnode){
  const prevVnode = vm._vnode vm._vnode = vnode if (!prevVnode){ vm.$el = vm.__patch__(vm.$el,vnode) } else { vm.$el = vm.__patch__(prevVnode,vnode) } } function updateComponent(){ vm._update(vm._render()) }

首先讀取當前的虛擬DOM——vm._vnode,判斷其是否為空,若為空,則為初次渲染,將虛擬DOM全部渲染到所對應的容器當中(vm.$el),若不為空,則是數據發生了修改,通過響應式我們可以監聽到這一情況,使用diff算法完成新舊對比並修改。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM