MVC框架
將整個前端頁面分成View,Controller,Modal,視圖上發生變化,通過Controller(控件)將響應傳入到Model(數據源),由數據源改變View上面的數據。
整個過程看起來是行雲流水,業務邏輯放在Model當中,頁面渲染邏輯放在View當中,但在實際運用上卻存在一個問題:那就是MVC框架允許View和Model直接進行通信!!
換句話說,View和Model之間隨着業務量的不斷龐大,會出現蜘蛛網一樣難以處理的依賴關系,完全背離了開發所應該遵循的“開放封閉原則”。
面對這個問題,MVVM框架就出現了,它與MVC框架的主要區別有兩點:
1、實現數據與視圖的分離
2、通過數據來驅動視圖,開發者只需要關心數據變化,DOM操作被封裝了。
可以看到MVVM分別指View,Model,View-Model,View通過View-Model的DOM Listeners將事件綁定到Model上,而Model則通過Data Bindings來管理View中的數據,View-Model從中起到一個連接橋的作用。
MVVM的實現原理:
MVVM的實現主要是三個核心點:
- 響應式:vue如何監聽data的屬性變化
- 模板解析:vue的模板是如何被解析的
- 渲染:vue模板是如何被渲染成HTML的
小白一枚,一直使用的是React,想要多了解一些其它的框架,正好最近Vue越來越火熱,Github上的Star數已經超過了React。而其背后蘊含的MVVM框架思想也一直跟React的組件化開發思想並駕齊驅,在這里也是本着兼收並蓄的思想,多了解一種開發模式。因此通過一些學習資料,寫一些自己對MVVM開發思想的理解。
廢話不多說,咱們進入正題。
MVVM框架理解
說起這個MVVM模型,就不得不說MVC框架。
將整個前端頁面分成View,Controller,Modal,視圖上發生變化,通過Controller(控件)將響應傳入到Model(數據源),由數據源改變View上面的數據。
整個過程看起來是行雲流水,業務邏輯放在Model當中,頁面渲染邏輯放在View當中,但在實際運用上卻存在一個問題:那就是MVC框架允許View和Model直接進行通信!!
換句話說,View和Model之間隨着業務量的不斷龐大,會出現蜘蛛網一樣難以處理的依賴關系,完全背離了開發所應該遵循的“開放封閉原則”。
面對這個問題,MVVM框架就出現了,它與MVC框架的主要區別有兩點:
1、實現數據與視圖的分離
2、通過數據來驅動視圖,開發者只需要關心數據變化,DOM操作被封裝了。
可以看到MVVM分別指View,Model,View-Model,View通過View-Model的DOM Listeners將事件綁定到Model上,而Model則通過Data Bindings來管理View中的數據,View-Model從中起到一個連接橋的作用。
MVVM的實現原理:
MVVM的實現主要是三個核心點:
- 響應式:vue如何監聽data的屬性變化
- 模板解析:vue的模板是如何被解析的
- 渲染:vue模板是如何被渲染成HTML的
響應式:
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>
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))])})
)
]
)
]
)
}
如何將模板渲染為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算法完成新舊對比並修改。
轉載:https://segmentfault.com/a/1190000015895017?utm_source=tag-newest