推薦博客:
https://blog.csdn.net/jia12216/article/details/55520426
https://www.cnblogs.com/sunny_z/p/7093663.html html
1、MVVM是Model-View-ViewModel的簡寫。它本質上就是MVC 的改進版。MVVM 就是將其中的View 的狀態和行爲抽象化,讓咱們將視圖 UI 和業務邏輯分開。固然這些事 ViewModel 已經幫咱們作了,它能夠取出 Model 的數據同時幫忙處理 View 中因爲須要展現內容而涉及的業務邏輯。微軟的WPF帶來了新的技術體驗,如Silverlight、音頻、視頻、3D、動畫……,這致使了軟件UI層更加細節化、可定製化。同時,在技術層面,WPF也帶來了 諸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新特性。MVVM(Model-View-ViewModel)框架的由來即是MVP(Model-View-Presenter)模式與WPF結合的應用方式時發展演變過來的一種新型架構框架。它立足於原有MVP框架而且把WPF的新特性糅合進去,以應對客戶日益複雜的需求變化。
前端
MVVM優勢編輯
MVVM模式和MVC模式同樣,主要目的是分離視圖(View)和模型(Model),有幾大優勢
1.
低耦合。視圖(View)能夠獨立於Model變化和修改,一個ViewModel能夠綁定到不一樣的"View"上,當View變化的時候Model能夠不變,當Model變化的時候View也能夠不變。
2.
可重用性。你能夠把一些視圖邏輯放在一個ViewModel裏面,讓不少view重用這段視圖邏輯。
3.
獨立開發。開發人員能夠專一於業務邏輯和數據的開發(ViewModel),設計人員能夠專一於頁面設計,使用Expression Blending能夠很容易設計界面並生成xaml代碼。
4.
可測試。界面素來是比較難於測試的,而如今測試能夠針對ViewModel來寫。
MVVM設計模式的缺點
第一點:數據綁定使得 Bug 很難被調試。你看到界面異常了,有多是你 View 的代碼有 Bug,也多是 Model 的代碼有問題。數據綁定使得一個位置的 Bug 被快速傳遞到別的位置,要定位原始出問題的地方就變得不那麼容易了。
第二點:一個大的模塊中,model也會很大,雖然使用方便了也很容易保證了數據的一致性,當時長期持有,不釋放內存,就形成了花費更多的內存。
第三點:數據雙向綁定不利於代碼重用。客戶端開發最經常使用的重用是View,可是數據雙向綁定技術,讓你在一個View都綁定了一個model,不一樣模塊的model都不一樣。那就不能簡單重用View了。
MVVM框架的主要應用場景vue
1)針對具備複雜交互邏輯的前端應用
2)提供基礎的架構抽象
3)經過Ajax數據持久化,保證前端用戶體驗
好處就是當先後端進行一些數據交互的時候,前端能夠經過Ajax請求對後端作數據持久化,不須要刷新整個頁面,只須要改動DOM裏須要改動的那部分數據和內容,特別是對於移動端應用場景,刷新頁面的代價太昂貴,會從新加載不少資源,雖然有些資源會被緩存,可是頁面的DOM、JS、CSS都會被瀏覽器從新解析一遍,所以,移動端頁面常常會作成SPA單頁應用,在這個基礎上就誕生了不少MVVM框架,如
Angular、React、Vue
2、MVC(Model View Controller)架構開發,它是蘋果推薦的開發模式,它把頁面分紅三部分:數據模型,頁面視圖,頁面控制器。一個頁面被分紅多個小視圖,一個頁面共享一個數據模型,只是這個數據模型只被控制器操做,不被各個子視圖處理。這個架構看起來整潔多了,控制器的複雜度下降的不少,頁面的顯示單元被分配到各個視圖去顯示,控制器專一與數據的加工與處理,部分解放了控制器,使它的功能更集中,更緊湊,更小。數據模型,頁面視圖,頁面控制器各個獨立,各司其職。這就是重量級視圖控制的特色。它看起來簡單,更容易理解,全部的邏輯在視圖控制器裏處理。react
一個頁面通常分三個文件夾:頁面文件夾(存放頁面控制器和其子文件夾),頁面文件夾下的model文件夾,頁面文件夾下的view文件夾。想找頁面間的邏輯就去頁面控制器裏找,想看頁面顯示元素就在view文件夾下尋找。你發現數據文件很小,只是數據存儲和轉換,這符合數據單一性原則;視圖部分也很小,只是數據的顯示,徹底不自主;控制器部分仍然擺脫不了過於龐大的弊病。他們就像一我的同樣頭重並且龐大,腳輕,身子小。看到這些你就知道這種開發架構須要進化的方向:強化控制器的重要性,減輕它的體積,把不屬於它的非核心部分遷移到相似視圖的那一部分去,在保證數據的定義性上增長它的功能和做用。angularjs
取消使用單例組裝http請求的作法(單例像類同樣常駐內存,無形中增長類內存的開銷。雖然它作到了統一管理http請求的做用,可是不符合那個頁面的請求那個頁面管理請求的原則)。讓從控制器分化出來的這一部分功能給View,http請求封裝爲一個新操做類。 後端
優勢:
1. 可定製性
2. 代碼清晰,便於維護
3. 測試友好性
4. 輕量級
5. 開源
主要缺點有兩個:
1. View對Model的依賴,會致使View也包含了業務邏輯;
2. Controller會變得很厚很複雜。
前端mvc框架,如angularjs,backbone: 設計模式
3、MVP:Model-View-Presenter,MVC的一個演變模式,將Controller換成了Presenter,主要爲了解決上述第一個缺點,將View和Model解耦
4、瞭解一下Vue雙數據綁定原理數組
vue.js
是採用
數據劫持
結合
發佈者-訂閱者模式
的方式,經過
Object.defineProperty()
來劫持各個屬性的
setter
,
getter
,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。
具體步驟:
瀏覽器
第一步:須要observe的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上 setter和getter
緩存
這樣的話,給這個對象的某個值賦值,就會觸發setter,那麼就能監聽到了數據變化
第二步:compile解析模板指令,將模板中的變量替換成數據,而後初始化渲染頁面視圖,並將每一個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變更,收到通知,更新視圖
第三步:Watcher訂閱者是Observer和Compile之間通訊的橋樑,主要作的事情是:
1、在自身實例化時往屬性訂閱器(dep)裏面添加本身
2、自身必須有一個update()方法
3、待屬性變更dep.notice()通知時,能調用自身的update()方法,並觸發Compile中綁定的回調,則功成身退。
第四步:MVVM做爲數據綁定的入口,整合Observer、Compile和Watcher三者,經過Observer來監聽本身的model數據變化,經過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通訊橋樑,達到數據變化 -> 視圖更新;視圖交互變化(input) -> 數據model變動的雙向綁定效果。
5、什麼是數據劫持:
首先咱們應該搞清楚什麼是數據劫持,說白了就是經過Object.defineProperty()來劫持對象屬性的setter和getter操做,在數據變更時作你想要作的事情,舉個栗子:
var
data = {
name:
'lhl'
}
Object.
keys(
data).
forEach(
function(
key){
Object.
defineProperty(
data,
key,{
enumerable:
true,
configurable:
true,
get
:
function(){
console.
log(
'get');
},
set
:
function(){
console.
log(
'監聽到數據發生了變化');
}
})
});
data.
name
//控制檯會打印出 「get」
data.
name =
'hxx'
//控制檯會打印出 "監聽到數據發生了變化"
上面的這個栗子能夠看出,咱們徹底能夠控制對象屬性的設置和讀取。在Vue中,做者在不少地方都很是巧妙的運用了defineProperty這個方法,具體用在哪裏而且它又解決了哪些問題,下面作詳細的介紹:
監聽對象屬性的變化
這個應該是Vue很是重要的一塊,其主要思想是observer每一個對象的屬性,添加到訂閱器dep中,當數據發生變化的時候發出notice通知。 相關源代碼以下:(做者採用的是ES6+flow寫的,代碼在src/core/observer/index.js模塊裏面)
export
function
defineReactive (
obj:
Object,
key:
string,
val:
any,
customSetter?:
Function
) {
const
dep =
new
Dep()
//建立訂閱對象
const
property =
Object.
getOwnPropertyDescriptor(
obj,
key)
if (
property &&
property.
configurable ===
false) {
return
}
// cater for pre-defined getter/setters
const
getter =
property &&
property.
get
const
setter =
property &&
property.
set
let
childOb =
observe(
val)
//建立一個觀察者對象
Object.
defineProperty(
obj,
key, {
enumerable:
true,
configurable:
true,
get
:
function
reactiveGetter () {
const
value =
getter ?
getter.
call(
obj) :
val
//這裏也是做者一個巧妙設計,在建立watcher實例的時候,經過調用對象的get方法往訂閱器 dep上添加這個建立的watcher實例
if (
Dep.
target) {
dep.
depend()
if (
childOb) {
childOb.
dep.
depend()
}
if (
Array.
isArray(
value)) {
dependArray(
value)
}
}
return
value
},
set
:
function
reactiveSetter (
newVal) {
const
value =
getter ?
getter.
call(
obj) :
val
if (
newVal ===
value) {
return
}
if (
process.
env.
NODE_ENV !==
'production' &&
customSetter) {
customSetter()
}
if (
setter) {
setter.
call(
obj,
newVal)
}
else {
val =
newVal
}
childOb =
observe(
newVal)
//繼續監聽新的屬性值
dep.
notify()
//這個是真正劫持的目的,要對訂閱者發通知了
}
})
}
以上是監聽對象屬性的變化,那麼下面再看看如何監聽數組的變化:
監聽數組的變化
const
arrayProto =
Array.
prototype
export
const
arrayMethods =
Object.
create(
arrayProto)
;[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
.
forEach(
function (
method) {
// cache original method
const
original =
arrayProto[
method]
def(
arrayMethods,
method,
function
mutator () {
// avoid leaking arguments:
// http://jsperf.com/closure-with-arguments
let
i =
arguments.
length
const
args =
new
Array(
i)
while (
i--) {
args[
i] =
arguments[
i]
}
const
result =
original.
apply(
this,
args)
const
ob =
this.
__ob__
let
inserted
switch (
method) {
case
'push':
inserted =
args
break
case
'unshift':
inserted =
args
break
case
'splice':
inserted =
args.
slice(
2)
break
}
if (
inserted)
ob.
observeArray(
inserted)
// notify change
ob.
dep.
notify()
return
result
})
})
...
/**
* Define a property.
*/
function
def (
obj,
key,
val,
enumerable) {
Object.
defineProperty(
obj,
key, {
value:
val,
enumerable: !!
enumerable,
writable:
true,
configurable:
true
});
}
經過上面的代碼能夠看出Vue是經過修改了數組的幾個操做的原型來實現的。
原文自: https://www.cnblogs.com/haohaoday/p/6375149.html
Vue框架很好的利用了Object.defineProperty()這個方法來實現了數據的監聽和修改,同時也達到了很好的模塊間解耦,在平常開發用好這個方法說不定會達到使人意想不到的結果。
發佈訂閱者模式:
https://www.sohu.com/a/207062452_464084