在開發過程中如何應用mvvm思想(非現有的框架)?

提出這個問題,旨在學習、理解mvvm的思想,所以希望答主能講解一下如何一步一步把一個需求按照mvvm的方式解決掉,有個小例子(比如說一個組件)就最好了。

提前感謝各位的不吝賜教,謝謝!!!


謝邀。。。

我們寫界面的時候,經常會碰到一些功能,比如,從某些數據動態生成界面,然後從輸入控制項採集數據,經過變換,更新另外的界面。

整個這個過程,其實歸納來看,就兩件事:

- 根據數據的變動,生成或者變更界面

- 根據界面的操作,變更數據

所有的界面相關操作流程,都可以化為若干這兩種操作的組合。寫多了之後,我們就可以總結出,兩者又都是包含一些模式的,比如說,數據的變動,它包含:

- 簡單類型的創建賦值

- 引用類型的創建和賦值

- 數組元素的添加,移除,交換、賦值

- 對象屬性的添加、刪除、賦值

界面的變更,包含:

- 元素的創建、交換、移位、移除

- 屬性的創建、賦值、移除

再總結下去,就會發現,數據和界面的變更之間,一般都是存在對應關係的。

比如說,一個數組跟界面一個列表對應,兩者始終是同步的,一個變了,另外一個也要跟著變。一個變數或者對象的鍵值,對應到一個元素的屬性上,一個變了,另外一個也要跟著變。

既然這樣,如果我們能夠引入一種綁定關係,經過一系列的配置過程,使得以後每次數據發生變更,界面都會自動跟著作對應變動;界面上的操作,也會自動更新到數據,那開發過程就會非常省事了,絕大部分此類操作都會轉化為配置,供綁定框架用來建立數據和界面之間的關聯關係。

所以,數據綁定是一個非常廣泛而迫切的需求,任何一個現代界面開發體系,如果不提供某種數據綁定機制,其開發過程就必定是低效的。

那麼,界面,很好理解,肯定是View,數據,如果說是Model,那ViewModel又是什麼?

以購物車結算頁面為例,包含的內容有:

- 已選商品列表(每種商品可以添加或者移除數量,並且有每種商品的總價)

- 商品總數和總價

從View的角度,一般不會有太多疑問,看到的東西,可以視業務場景做分塊,每塊單獨實現,但是Model呢?

我們知道Model是一種數據,但並非每個數據都屬於Model,一般來說,在頁面上的數據中,用於跟服務端交互的數據,可以大致視為Model,回到我們這個例子中看,那就是每種商品的已選數量。

但另外還有一些東西是要用於展示或者中間步驟的,那就是每種商品的總價,還有商品總數、總價,這些東西由於不提交,僅供視圖使用,所以可以視為ViewModel。

ViewModel是Model和View之間的橋樑,它的設計原則是:

- 為Model和View提供適配

- 如果有需要轉換的過程,儘可能在ViewModel中做,保持Model的純潔,View的清晰。

我們上面這個例子中,數據的轉換並不明顯。大家都見過行政區劃樹吧,省市縣鄉村,可能Model給的數據都是平級,然後根據展示的需要,轉換成樹狀的,轉換之後的數據,就是專供展示用的ViewModel了。

綜上,MVVM所包含的Model,View,ViewModel三層,在實踐的時候,主要包含幾個要點:

- 以不同的角色分別考慮每個層次

- 先考慮Model和View,最後考慮ViewModel

這個ViewModel,責任可不輕,就像中年男人,是婆媳關係的紐帶、橋樑,要想兩頭滿意,就得自己多干,臟活累活全都我來,你們歇著……

--------------------

欠兩個例子代碼,閑了用Vue補個


class ConcreteComponent {
constructor() {
this.element = document.createElement("div");

this.state = {
status: "loading"
content: "",
disabled: false
};
}

render() {
let classNames = ["wrapper"];

if (this.state.status === "loaded" !this.state.disabled) {
classNames.push("enabled");
}

let content;

switch (this.state.status) {
case "loading":
content = "Loading...";
break;
case "loaded":
content = this.state.content;
break;
case "error":
default:
content = "Something wrong happened.";
break;
}

this.element = `
&Hello, Whatever!& &
${content}
& `;
}
}

let component = new ConcreteComponent();

document.body.appendChild(component.element);

component.state.content = "Useless content.";
component.render();

component.state.disabled = true;
component.render();

上面的代碼示例中, 最重要的就是 state 的具體變化和 render 的分離. render 不關心到底是什麼具體的操作或事件使得 state 發生了變化, 只負責根據變化後的 state 重新渲染. 當然這會產生一些不必要的開銷, 但這部分的調優應該交給框架來搞定.


推薦閱讀:

隨著各種前端MVVM,MVC框架的流行,jQuery等傳統JS庫是否有走向邊緣的趨勢?
mvvm中 viewmodel該如何設計?
如何正確使用Vue.js的組件?
AngularJS 有沒有缺點?MVVM 框架中有比它更好的嗎?
React中的virtual dom是否可以理解為當前組件的view model?

TAG:前端開發 | JavaScript | MVVM | AngularJS | Angular? |