天天看點

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

授權轉載,作者:子弈,來自掘金社群。
c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

如果你對于MVVM的形成不是特别清晰,則可以先閱讀以下部分。

  • 了解MV*的演變曆史以及特性(可跳過)
  • 了解觀察者模式(可跳過)

本文可以幫助你了解什麼?

  • 了解Vue的運作機制
  • 參考Vue的運作機制,将觀察者模式改用中介者模式實作一個簡易的MVVM
    • MVVM的實作示範
    • MVVM的流程設計
    • 中介者模式的實作
    • 資料劫持的實作
    • 資料雙向綁定的實作
    • 簡易視圖指令的編譯過程的實作
    • ViewModel的實作
    • MVVM的實作

MV*設計模式的演變曆史

我們先來花點時間想想,如果你是一個前端架構(Vue、React或者Angular)的開發者,你是有多麼頻繁的聽到“MVVM”這個詞,但你真正明白它的含義嗎?

MV*設計模式的起源

起初「計算機科學家(現在的我們是小菜雞)「在設計GUI(圖形使用者界面)應用程式的時候,代碼是雜亂無章的,通常難以管理和維護。GUI的設計結構一般包括」視圖」(View)、「模型」(Model)、「邏輯」(Application Logic、Business Logic以及Sync Logic),例如:

  • 使用者在「視圖」(View)上的鍵盤、滑鼠等行為執行「應用邏輯」(Application Logic),「應用邏輯」會觸發「業務邏輯」(Business Logic),進而變更「模型」(Model)
  • 「模型」(Model)變更後需要「同步邏輯」(Sync Logic)将變化回報到「視圖」(View)上供使用者感覺

可以發現在GUI中「視圖」和「模型」是天然可以進行分層的,雜亂無章的部分主要是「邏輯」。于是我們的程式員們不斷的絞盡腦汁在想辦法優化GUI設計的「邏輯」,然後就出現了MVC、MVP以及MVVM等設計模式。

MV*設計模式在B/S架構中的思考

在B/S架構的應用開發中,MV*設計模式概述并封裝了應用程式及其環境中需要關注的地方,盡管JavaScript已經變成一門同構語言,但是在浏覽器和伺服器之間這些關注點可能不一樣:

  • 視圖能否跨案例或場景使用?
  • 業務邏輯應該放在哪裡處理?(在「Model」中還是「Controller」中)
  • 應用的狀态應該如何持久化和通路?

MVC(Model-View-Controller)

早在上個世紀70年代,美國的施樂公司(Xerox)的工程師研發了Smalltalk程式設計語言,并且開始用它編寫GUI。而在Smalltalk-80版本的時候,一位叫Trygve Reenskaug的工程師設計了MVC的架構模式,極大地降低了GUI的管理難度。

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

如圖所示,MVC把GUI分成「View」(視圖)、「Model」(模型)、「Controller」(控制 器)(可熱插拔,主要進行「Model」和「View」之間的協作,包括路由、輸入預處理等業務邏輯)三個子產品:

  • 「View」:檢測使用者的鍵盤、滑鼠等行為,傳遞調用「Controller」執行應用邏輯。「View」更新需要重新擷取「Model」的資料。
  • 「Controller」:「View」和「Model」之間協作的應用邏輯或業務邏輯處理。
  • 「Model」:「Model」變更後,通過觀察者模式通知「View」更新視圖。
「Model」的更新通過觀察者模式,可以實作多視圖共享同一個「Model」。

傳統的MVC設計對于Web前端開發而言是一種十分有利的模式,因為「View」是持續性的,并且「View」可以對應不同的「Model」。Backbone.js就是一種稍微變種的MVC模式實作(和經典MVC較大的差別在于「View」可以直接操作「Model」,是以這個模式不能同構)。這裡總結一下MVC設計模式可能帶來的好處以及不夠完美的地方:

優點:

  • 職責分離:子產品化程度高、「Controller」可替換、可複用性、可擴充性強。
  • 多視圖更新:使用觀察者模式可以做到單「Model」通知多視圖實作資料更新。

缺點:

  • 測試困難:「View」需要UI環境,是以依賴「View」的「Controller」測試相對比較困難(現在Web前端的很多測試架構都已經解決了該問題)。
  • 依賴強烈:「View」強依賴「Model」(特定業務場景),是以「View」無法元件化設計。

####服務端MVC

經典MVC隻用于解決GUI問題,但是随着B/S架構的不斷發展,Web服務端也衍生出了MVC設計模式。

JSP Model1和JSP Model2的演變過程

JSP Model1是早期的Java動态Web應用技術,它的結構如下所示:

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

在Model1中,「JSP」同時包含了「Controller」和「View」,而「JavaBean」包含了「Controller」和「Model」,子產品的職責相對混亂。在JSP Model1的基礎上,Govind Seshadri借鑒了MVC設計模式提出了JSP Model2模式(具體可檢視文章Understanding JavaServer Pages Model 2 architecture),它的結構如下所示:

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

在JSP Model2中,「Controller」、「View」和「Model」分工明确,「Model」的資料變更,通常通過「JavaBean」修改「View」然後進行前端實時渲染,這樣從Web前端發起請求到資料回顯路線非常明确。不過這裡專門詢問了相應的後端開發人員,也可能通過「JavaBean」到「Controller」(「Controller」主要識别目前資料對應的JSP)再到「JSP」,是以在服務端MVC中,也可能産生這樣的流程「View」 -> 「Controller」 -> 「Model」 -> 「Controller」 -> 「View」。

在JSP Model2模式中,沒有做到前後端分離,前端的開發大大受到了限制。
Model2的衍生
c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

對于Web前端開發而言,最直覺的感受就是在Node服務中衍生Model2模式(例如結合Express以及EJS模闆引擎等)。

服務端MVC和經典MVC的差別

在服務端的MVC模式設計中采用了HTTP協定通信(HTTP是單工無狀态協定),是以「View」在不同的請求中都不保持狀态(狀态的保持需要額外通過Cookie存儲),并且經典MVC中「Model」通過觀察者模式告知「View」的環節被破壞(例如難以實作服務端推送)。當然在經典MVC中,「Controller」需要監聽「View」并對輸入做出反應,邏輯會變得很繁重,而在Model2中, 「Controller」隻關注路由處理等,而「Model」則更多的處理業務邏輯。

MVP(Model-View-Presenter)

在上個世紀90年代,IBM旗下的子公司Taligent在用C/C++開發一個叫CommonPoint的圖形界面應用系統的時候提出了MVP的概念。

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

如上圖所示,MVP是MVC的模式的一種改良,打破了「View」對于「Model」的依賴,其餘的依賴關系和MVC保持不變。

  • 「Passive View」:「View」不再處理同步邏輯,對「Presenter」提供接口調用。由于不再依賴「Model」,可以讓「View」從特定的業務場景中抽離,完全可以做到元件化。
  • 「Presenter」(「Supervising Controller」):和經典MVC的「Controller」相比,任務更加繁重,不僅要處理應用業務邏輯,還要處理同步邏輯(高層次複雜的UI操作)。
  • 「Model」:「Model」變更後,通過觀察者模式通知「Presenter」,如果有視圖更新,「Presenter」又可能調用「View」的接口更新視圖。

MVP模式可能産生的優缺點如下:

  • 「Presenter」便于測試、「View」可元件化設計
  • 「Presenter」厚、維護困難

MVVM(Model-View-ViewModel)

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

如上圖所示:MVVM模式是在MVP模式的基礎上進行了改良,将「Presenter」改良成「ViewModel」(抽象視圖):

  • 「ViewModel」:内部內建了「Binder」(Data-binding Engine,資料綁定引擎),在MVP中派發器「View」或「Model」的更新都需要通過「Presenter」手動設定,而「Binder」則會實作「View」和「Model」的雙向綁定,進而實作「View」或「Model」的自動更新。
  • 「View」:可元件化,例如目前各種流行的UI元件架構,「View」的變化會通過「Binder」自動更新相應的「Model」。
  • 「Model」:「Model」的變化會被「Binder」監聽(仍然是通過觀察者模式),一旦監聽到變化,「Binder」就會自動實作視圖的更新。

可以發現,MVVM在MVP的基礎上帶來了大量的好處,例如:

  • 提升了可維護性,解決了MVP大量的手動同步的問題,提供雙向綁定機制。
  • 簡化了測試,同步邏輯是交由「Binder」處理,「View」跟着「Model」同時變更,是以隻需要保證「Model」的正确性,「View」就正确。

當然也帶來了一些額外的問題:

  • 産生性能問題,對于簡單的應用會造成額外的性能消耗。
  • 對于複雜的應用,視圖狀态較多,視圖狀态的維護成本增加,「ViewModel」建構和維護成本高。

對前端開發而言MVVM是非常好的一種設計模式。在浏覽器中,路由層可以将控制權交由适當的「ViewModel」,後者又可以更新并響應持續的View,并且通過一些小修改MVVM模式可以很好的運作在伺服器端,其中的原因就在于「Model」與「View」已經完全沒有了依賴關系(通過View與Model的去耦合,可以允許短暫「View」與持續「View」的并存),這允許「View」經由給定的「ViewModel」進行渲染。

目前流行的架構Vue、React以及Angular都是MVVM設計模式的一種實作,并且都可以實作服務端渲染。需要注意目前的Web前端開發和傳統Model2需要模闆引擎渲染的方式不同,通過Node啟動服務進行頁面渲染,并且通過代理的方式轉發請求後端資料,完全可以從後端的苦海中脫離,這樣一來也可以大大的解放Web前端的生産力。

觀察者模式和釋出/訂閱模式

觀察者模式

觀察者模式是使用一個subject目标對象維持一系列依賴于它的observer觀察者對象,将有關狀态的任何變更自動通知給這一系列觀察者對象。當subject目标對象需要告訴觀察者發生了什麼事情時,它會向觀察者對象們廣播一個通知。

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

如上圖所示:一個或多個觀察者對目标對象的狀态感興趣時,可以将自己依附在目标對象上以便注冊感興趣的目标對象的狀态變化,目标對象的狀态發生改變就會發送一個通知消息,調用每個觀察者的更新方法。如果觀察者對目标對象的狀态不感興趣,也可以将自己從中分離。

釋出/訂閱模式

釋出/訂閱模式使用一個事件通道,這個通道介于訂閱者和釋出者之間,該設計模式允許代碼定義應用程式的特定事件,這些事件可以傳遞自定義參數,自定義參數包含訂閱者需要的資訊,采用事件通道可以避免釋出者和訂閱者之間産生依賴關系。

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM
學生時期很長一段時間内用過Redis的釋出/訂閱機制,具體可檢視zigbee-door/zigbee-tcp,但是慚愧的是沒有好好閱讀過這一塊的源碼。

兩者的差別

觀察者模式:允許觀察者執行個體對象(訂閱者)執行适當的事件處理程式來注冊和接收目标執行個體對象(釋出者)發出的通知(即在觀察者執行個體對象上注冊

update

方法),使訂閱者和釋出者之間産生了依賴關系,且沒有事件通道。不存在封裝限制的單一對象,目标對象和觀察者對象必須合作才能維持限制。觀察者對象向訂閱它們的對象釋出其感興趣的事件。通信隻能是單向的。

釋出/訂閱模式:單一目标通常有很多觀察者,有時一個目标的觀察者是另一個觀察者的目标。通信可以實作雙向。該模式存在不穩定性,釋出者無法感覺訂閱者的狀态。

Vue的運作機制簡述

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

這裡簡單的描述一下Vue的運作機制(需要注意分析的是 Runtime + Compiler 的 Vue.js)。

初始化流程

  • 建立Vue執行個體對象
  • init

    過程會初始化生命周期,初始化事件中心,初始化渲染、執行

    beforeCreate

    周期函數、初始化

    data

    props

    computed

    watcher

    、執行

    created

    周期函數等。
  • 初始化後,調用

    $mount

    方法對Vue執行個體進行挂載(挂載的核心過程包括「模闆編譯」、「渲染」以及「更新」三個過程)。
  • 如果沒有在Vue執行個體上定義

    render

    方法而是定義了

    template

    ,那麼需要經曆編譯階段。需要先将

    template

    字元串編譯成

    render function

    template

    字元串編譯步驟如下 :
    • parse

      正則解析

      template

      字元串形成AST(抽象文法樹,是源代碼的抽象文法結構的樹狀表現形式)
    • optimize

      标記靜态節點跳過diff算法(diff算法是逐層進行比對,隻有同層級的節點進行比對,是以時間的複雜度隻有O(n)。如果對于時間複雜度不是很清晰的,可以檢視我寫的文章ziyi2/algorithms-javascript/漸進記号)
    • generate

      将AST轉化成

      render function

      字元串
  • 編譯成

    render function

    後,調用

    $mount

    mountComponent

    方法,先執行

    beforeMount

    鈎子函數,然後核心是執行個體化一個渲染

    Watcher

    ,在它的回調函數(初始化的時候執行,以及元件執行個體中監測到資料發生變化時執行)中調用

    updateComponent

    方法(此方法調用

    render

    方法生成虛拟Node,最終調用

    update

    方法更新DOM)。
  • 調用

    render

    方法将

    render function

    渲染成虛拟的Node(真正的 DOM 元素是非常龐大的,因為浏覽器的标準就把 DOM 設計的非常複雜。如果頻繁的去做 DOM 更新,會産生一定的性能問題,而 Virtual DOM 就是用一個原生的 JavaScript 對象去描述一個 DOM 節點,是以它比建立一個 DOM 的代價要小很多,而且修改屬性也很輕松,還可以做到跨平台相容),

    render

    方法的第一個參數是

    createElement

    (或者說是

    h

    函數),這個在官方文檔也有說明。
  • 生成虛拟DOM樹後,需要将虛拟DOM樹轉化成真實的DOM節點,此時需要調用

    update

    方法,

    update

    方法又會調用

    pacth

    方法把虛拟DOM轉換成真正的DOM節點。需要注意在圖中忽略了建立真實DOM的情況(如果沒有舊的虛拟Node,那麼可以直接通過

    createElm

    建立真實DOM節點),這裡重點分析在已有虛拟Node的情況下,會通過

    sameVnode

    判斷目前需要更新的Node節點是否和舊的Node節點相同(例如我們設定的

    key

    屬性發生了變化,那麼節點顯然不同),如果節點不同那麼将舊節點采用新節點替換即可,如果相同且存在子節點,需要調用

    patchVNode

    方法執行diff算法更新DOM,進而提升DOM操作的性能。
需要注意在初始化階段,沒有較長的描述資料的響應式過程,這個在響應式流程裡做說明。

響應式流程

  • init

    的時候會利用

    Object.defineProperty

    方法(不相容IE8)監聽Vue執行個體的響應式資料的變化進而實作資料劫持能力(利用了JavaScript對象的通路器屬性

    get

    set

    ,在未來的Vue3中會使用ES6的

    Proxy

    來優化響應式原理)。在初始化流程中的編譯階段,當

    render function

    被渲染的時候,會讀取Vue執行個體中和視圖相關的響應式資料,此時會觸發

    getter

    函數進行「依賴收集」(将觀察者

    Watcher

    對象存放到目前閉包的訂閱者

    Dep

    subs

    中),此時的資料劫持功能和觀察者模式就實作了一個MVVM模式中的「Binder」,之後就是正常的渲染和更新流程。
  • 當資料發生變化或者視圖導緻的資料發生了變化時,會觸發資料劫持的

    setter

    函數,

    setter

    會通知初始化「依賴收集」中的

    Dep

    中的和視圖相應的

    Watcher

    ,告知需要重新渲染視圖,

    Wather

    就會再次通過

    update

    方法來更新視圖。
可以發現隻要視圖中添加監聽事件,自動變更對應的資料變化時,就可以實作資料和視圖的雙向綁定了。

基于Vue機制的簡易MVVM實作

了解了MV*設計模式、觀察者模式以及Vue運作機制之後,可能對于整個MVVM模式有了一個感性的認知,是以可以來手動實作一下,這裡實作過程包括如下幾個步驟:

  • MVVM的實作示範
  • MVVM的流程設計
  • 中介者模式的實作
  • 資料劫持的實作
  • 資料雙向綁定的實作
  • 簡易視圖指令的編譯過程的實作
  • ViewModel的實作
  • MVVM的實作

MVVM的實作示範

MVVM示例的使用如下所示,包括

browser.js

(View視圖的更新)、

mediator.js

(中介者)、

binder.js

(MVVM的資料綁定引擎)、

view.js

(視圖)、

hijack.js

(資料劫持)以及

mvvm.js

(MVVM執行個體)。本示例相關的代碼可檢視github的ziyi2/mvvm:

"app"><input type="text" b-value="input.message" b-on-input="handlerInput"><div>{{ input.message }}div><div b-text="text">div><div>{{ text }}div><div b-html="htmlMessage">div>div><script src="./browser.js">script><script src="./mediator.js">script><script src="./binder.js">script><script src="./view.js">script><script src="./hijack.js">script><script src="./mvvm.js">script><script>let vm = new Mvvm({el: '#app',data: {input: {message: 'Hello Input!'       },text: 'ziyi2',htmlMessage: `送出`     },methods: {       handlerInput(e) {this.text = e.target.value       }     }   })script>

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

MVVM的流程設計

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

這裡簡單的描述一下MVVM實作的運作機制。

初始化流程

建立MVVM執行個體對象,初始化執行個體對象的

options

參數

proxyData

将MVVM執行個體對象的

data

資料代理到MVVM執行個體對象上

Hijack

類實作資料劫持功能(對MVVM執行個體跟視圖對應的響應式資料進行監聽,這裡和Vue運作機制不同,幹掉了

getter

依賴搜集功能)解析視圖指令,對MVVM執行個體與視圖關聯的DOM元素轉化成文檔碎片并進行綁定指令解析(

b-value

b-on-input

b-html

等,其實是Vue編譯的超級簡化版),添加資料訂閱和使用者監聽事件,将視圖指令對應的資料挂載到「Binder」資料綁定引擎上(資料變化時通過Pub/Sub模式通知「Binder」綁定器更新視圖)使用Pub/Sub模式代替Vue中的Observer模式「Binder」采用了指令模式解析視圖指令,調用

update

方法對View解析綁定指令後的文檔碎片進行更新視圖處理

Browser

采用了外觀模式對浏覽器進行了簡單的相容性處理

響應式流程

監聽使用者輸入事件,對使用者的輸入事件進行監聽調用MVVM執行個體對象的資料設定方法更新資料資料劫持觸發

setter

方法通過釋出機制釋出資料變化訂閱器接收資料變更通知,更新資料對應的視圖

中介者模式的實作

最簡單的中介者模式隻需要實作釋出、訂閱和取消訂閱的功能。釋出和訂閱之間通過事件通道(channels)進行資訊傳遞,可以避免觀察者模式中産生依賴的情況。中介者模式的代碼如下:

在每一個MVVM執行個體中,都需要執行個體化一個中介者執行個體對象,中介者執行個體對象的使用方法如下:

資料劫持的實作

對象的屬性

對象的屬性可分為資料屬性(特性包括

[[Value]]

[[Writable]]

[[Enumerable]]

[[Configurable]]

)和存儲器/通路器屬性(特性包括

[[ Get ]]

[[ Set ]]

[[Enumerable]]

[[Configurable]]

),對象的屬性隻能是資料屬性或通路器屬性的其中一種,這些屬性的含義:

[[Configurable]]

: 表示能否通過

delete

删除屬性進而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為通路器屬性。

[[Enumerable]]

:  對象屬性的可枚舉性。

[[Value]]

: 屬性的值,讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值儲存在這個位置。這個特性的預設值為

undefined

[[Writable]]

: 表示能否修改屬性的值。

[[ Get ]]

: 在讀取屬性時調用的函數。預設值為

undefined

[[ Set ]]

: 在寫入屬性時調用的函數。預設值為

undefined

資料劫持就是使用了

[[ Get ]]

[[ Set ]]

的特性,在通路對象的屬性和寫入對象的屬性時能夠自動觸發屬性特性的調用函數,進而做到監聽資料變化的目的。

對象的屬性可以通過ES5的設定特性方法

Object.defineProperty(data, key, descriptor)

改變屬性的特性,其中

descriptor

傳入的就是以上所描述的特性集合。

資料劫持

注意Vue3.0将不産用

Object.defineProperty

方式進行資料監聽,原因在于

無法監聽數組的變化(目前的數組監聽都基于對原生數組的一些方法進行

hack

,是以如果要使數組響應化,需要注意使用Vue官方推薦的一些數組方法)無法深層次監聽對象屬性

在Vue3.0中将産用

Proxy

解決以上痛點問題,當然會産生浏覽器相容性問題(例如萬惡的IE,具體可檢視Can I use proxy)。

需要注意是的在

hijack

中隻進行了一層屬性的周遊,如果要做到對象深層次屬性的監聽,需要繼續對

data[key]

進行

hijack

操作,進而可以達到屬性的深層次周遊監聽,具體可檢視mvvm/mvvm/hijack.js,

資料雙向綁定的實作

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

如上圖所示,資料雙向綁定主要包括資料的變化引起視圖的變化(「Model」 -> 監聽資料變化 -> 「View」)、視圖的變化又改變資料(「View」 -> 使用者輸入監聽事件 -> 「Model」),進而實作資料和視圖之間的強聯系。

在實作了資料監聽的基礎上,加上使用者輸入事件以及視圖更新,就可以簡單實作資料的雙向綁定(其實就是一個最簡單的「Binder」,隻是這裡的代碼耦合嚴重):

資料雙向綁定的demo源碼。

簡易視圖指令的編譯過程實作

在MVVM的實作示範中,可以發現使用了

b-value

b-text

b-on-input

b-html

等綁定屬性(這些屬性在該MVVM示例中自行定義的,并不是html标簽原生的屬性,類似于vue的

v-html

v-model

v-text

指令等),這些指令隻是友善使用者進行Model和View的同步綁定操作而建立的,需要MVVM執行個體對象去識别這些指令并重新渲染出最終需要的DOM元素,例如

"app"><input type="text" b-value="message">div>

最終需要轉化成真實的DOM

"app"><input type="text" value='Hello World' />div>

那麼實作以上指令解析的步驟主要如下:

擷取對應的

#app

元素轉換成文檔碎片(從DOM中移出

#app

下的所有子元素)識别出文檔碎片中的綁定指令并重新修改該指令對應的DOM元素處理完文檔碎片後重新渲染

#app

元素

HTML代碼如下:

首先來看示例的使用

view.js

中實作了

#app

下的元素轉化成文檔碎片以及對所有子元素進行屬性周遊操作(用于

binder.js

的綁定屬性解析)

接下來檢視

binder.js

如何處理綁定指令,這裡以

b-value

的解析為示例

browser.js

中使用外觀模式對浏覽器原生的事件以及DOM操作進行了再封裝,進而可以做到浏覽器的相容處理等,這裡隻對

b-value

需要的DOM操作進行了封裝處理,友善閱讀

至此MVVM示例中簡化的「Model」 -> 「ViewModel」 (未實作資料監聽功能)-> 「View」路走通,可以檢視視圖綁定指令的解析的demo。

ViewModel的實作

「ViewModel」(内部綁定器「Binder」)的作用不僅僅是實作了「Model」到「View」的自動同步(Sync Logic)邏輯(以上視圖綁定指令的解析的實作隻是實作了一個視圖的綁定指令初始化,一旦「Model」變化,視圖要更新的功能并沒有實作),還實作了「View」到「Model」的自動同步邏輯,進而最終實作了資料的雙向綁定。

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

是以隻要在視圖綁定指令的解析的基礎上增加「Model」的資料監聽功能(資料變化更新視圖)和「View」視圖的

input

事件監聽功能(監聽視圖進而更新相應的「Model」資料,注意「Model」的變化又會因為資料監聽進而更新和「Model」相關的視圖)就可以實作「View」和「Model」的雙向綁定。同時需要注意的是,資料變化更新視圖的過程需要使用釋出/訂閱模式,如果對流程不清晰,可以繼續回看MVVM的結構設計。

在「簡易視圖指令的編譯過程實作」的基礎上進行修改,首先是HTML代碼

mediator.js

不再叙述,具體回看「中介者模式的實作」,

view.js

browser.js

也不再叙述,具體回看「簡易視圖指令的編譯過程實作」。

示例的使用:

首先看下資料劫持,在** 資料劫持的實作「的基礎上,增加了中介者對象的釋出資料變化功能(在抽象視圖的」Binder**中會訂閱這個資料變化)

接着重點來看

binder.js

中的實作

最終實作了具有「viewModel」的MVVM簡單執行個體,具體檢視ViewModel的實作的demo。

MVVM的實作

在「ViewModel的實作」的基礎上:

新增了

b-text

b-html

b-on-*

(事件監聽)指令的解析代碼封裝更優雅,新增了MVVM類用于限制管理之前示例中零散的執行個體對象(建造者模式)

hijack.js

實作了對「Model」資料的深層次監聽

hijack.js

中的釋出和訂閱的

channel

采用HTML屬性中綁定的指令對應的值進行處理(例如

b-value="a.b.c.d"

,那麼

channel

就是

'a.b.c.d'

,這裡是将Vue的觀察者模式改成中介者模式後的一種嘗試,隻是一種實作方式,當然采用觀察者模式關聯性更強,而采用中介者模式會更解耦)。

browser.js

中新增了事件監聽的相容處理、

b-html

b-text

等指令的DOM操作api等

由于篇幅太長了,這裡就不過多做說明了,感興趣的童鞋可以直接檢視 https://github.com/ziyi2/mvvm/tree/master/mvvm,需要注意該示例中還存在一定的缺陷,例如「Model」的屬性是一個對象,且該對象被重寫時,釋出和訂閱維護的

channels

中未将舊的屬性監聽的

channel

移除處理。

結束語

歡迎關注公衆号「前端試煉」,公衆号平時會分享一些實用或者有意思的東西,發現代碼之美。專注深度和最佳實踐,希望打造一個高品質的公衆号。偶爾還會分享一些攝影 ~

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

也可以掃碼加我微信,拉你進交流劃水聊天群,有看到好文章/代碼都會發在群裡。

c# mvvm模式擷取目前視窗_基于 Vue 實作一個履歷 MVVM

繼續閱讀