簡單談談js中的MVC

MVC是什么?

MVC是一種架構模式,它將應用抽象為3個部分:模型(數據)、視圖、控制器(分發器)。

本文將用一個經典的例子todoList來展開(代碼在最后)。

?

一個事件發生的過程(通信單向流動):

1、用戶在視圖 V 上與應用程序交互

2、控制器 C 觸發相應的事件,要求模型 M 改變狀態(讀寫數據)

3、模型 M 將數據發送到視圖 V ,更新數據,展現給用戶

?

js傳統開發模式,大多基于事件驅動的

1、hash驅動

2、DOM事件,用來驅動視圖

3模型事件(業務模型事件和數據模型事件),用來驅動模型和模型結合

所以js中的mvc的特點是:單向流動、事件驅動

?

一)模型

模型存放應用的所有數據對象業務數據、數據校驗、增刪改查),比如,例子todoList中的store模型,存放每一條記錄與之有關的邏輯。

數據是面向對象的,當控制器請求模型讀寫數據時,模型就將數據包裝成模型實例。任何定義在這個數據模型上的函數或邏輯都可以直接被調用。在本文的例子中采用localSrorage也是類似道理的。存儲的Todos可以隨時被調用

模型不關心,不包含視圖和控制器的邏輯。它們應該是互相解耦的。這里提一點,模型視圖的耦合顯然是違反MVC架構原則,但往往我們有時候卻因為業務關系而無法完全解耦

模型表現了領域特定的數據當一個模型有所改變的時候它會通知它的觀察者(視圖)

?

二)視圖

視圖是呈現給用戶的,是用戶交互的第一入口。它定義配置管理著每個頁面相應的模板與組件,它表現一個模型的當前狀態視圖通過觀察者模式監視模型,以獲得最新的數據,來呈現最新的頁面所以,頁面首次加載時,往往是從接收模型的數據開始。

?

三)控制器

控制器分發器),是模型和視圖之間的橋梁集中式配置和管理事件分發、模型分發、視圖分發,還用來權限控制、異常處理等。我們的應用中往往是有多個控制器的

頁面加載完成后,控制器監聽視圖的用戶交互按鈕點擊或表單提交一旦用戶發生交互時控制器做出對視圖的選擇觸發控制器的事件處理機制去派發新的事件,通知模型更新數據(這樣就回到了第一步了)

?

Demo-todoList

最后這里是一個用原生js寫的todoLIst,這個demo做的很簡陋,點擊輸入文字點擊確定就添加,刪除是直接點擊該行信息。

?

單獨分離開來舉例子不好講,所以在代碼中進行注釋。首先簡單理下下邊代碼的思路:

1、V層定義配置了一個顯示數據的字符串模板,同時定義一個訂閱者的回調函數render()?用于頁面更新數據。

2、C層監聽用戶的添加與刪除操作,添加是add()?函數 它執行了回調函數render,同時向M層寫入數據,通知M層改變。刪除操作同理。

3、M層是本地存儲localStorage,模擬一個存儲數據對象的后臺模型。

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title>todo</title>
  6 </head>
  7 <body>
  8 <header>
  9     <h3>待定事項</h3>
 10 </header>
 11 <main>
 12     <ul id="todoList"></ul>
 13     <input type="text" id="content">
 14     <button id="confirm">確認</button>
 15 </main>
 16 
 17 <script>
 18   (function () {
 19     const ADD_KEY = '__todoList__'
 20 
 21     const Utils = {
 22       // 模擬 Modal(實體模型)
 23       store(key, data) {
 24         if (arguments.length > 1) {
 25           return localStorage.setItem(key, JSON.stringify(data));
 26         } else {
 27           let storeData = localStorage.getItem(key);
 28           return (storeData && JSON.parse(storeData)) || []; // 這里一定要設置初始值為 []
 29         }
 30       }
 31     }
 32 
 33     class Todo {
 34       constructor(id, text = "") {
 35         this.id = id
 36         this.text = text
 37       }
 38     }
 39 
 40     let App = {
 41       init() {
 42         // this.todos 為一個存儲json對象的數組, 是一個實例化的數據對象,可任意調用
 43         this.todos = Utils.store(ADD_KEY)
 44         this.findDom()
 45         this.bindEvent()
 46         this.render() // 初始化渲染
 47       },
 48 
 49 
 50       findDom() {
 51         this.contentBox = document.querySelector("#content")
 52         this.confirm = document.querySelector("#confirm")
 53         this.todoList = document.querySelector("#todoList")
 54         this.todoListItem = document.getElementsByTagName("li")
 55       },
 56 
 57       // 模擬 Controller (業務邏輯層)
 58       bindEvent() {
 59         this.confirm.addEventListener('click', () => {
 60           // 要求模型 M 改變狀態,add()函數是寫入數據操作
 61           this.add()
 62         }, false)
 63 
 64         this.todoList.addEventListener('click', (item) => {  // 事件委托,優化性能
 65           this.remove(item)
 66         }, false)
 67       },
 68 
 69       // 這里勉強抽象成一個視圖吧!!!
 70       view() {
 71         let fragment = document.createDocumentFragment()   // 減少回流次數
 72         fragment = ''
 73 
 74         for (let i = 0; i < this.todos.length; i++) { // 一次性DOM節點生成
 75           // 這里使用拼接字符串代替視圖的模板,
 76           // *******注意模板并不是一個視圖,模板是由視圖定義配置出來的,并被其管理著*******
 77           // 模板是用一種聲明的方式指定部分甚至所有的視圖對象
 78           fragment += `<li>${this.todos[i].text}</li>`
 79         }
 80         this.todoList.innerHTML = fragment
 81       },
 82 
 83       // render()函數作為一個訂閱者的回調函數,數據的變化會反饋到模型 store
 84       // 換句話說:視圖通過觀察者模式,觀察模型 store,當模型發生改變,觸發視圖更新
 85       render() {
 86         this.view()
 87 
 88         /**
 89          * 這里需要特別提一下,按照 MVC 原則這里本不應該出現下面的代碼的
 90          * 因為業務邏輯關系(我本地存儲使用的是同一個key值,再次寫入數據會覆蓋原來的數據,),
 91          * 所以必須通知模型 M 保存數據, V 層處理了不該它處理的邏輯,導致 M 與 V 耦合
 92          *
 93          * 解決辦法是:將其抽象出來編寫一個 視圖助手 helper
 94          */
 95         Utils.store(ADD_KEY, this.todos)
 96       },
 97 
 98       getItemIndex(item) {
 99         let itemIndex
100         if (item.target.tagName.toLowerCase() === 'li') {
101           let arr = Array.prototype.slice.call(this.todoListItem)
102           let index = arr.indexOf(item.target)
103           return itemIndex = index
104         }
105       },
106 
107       add(e) {
108         let id = Number(new Date())
109         let text = this.contentBox.value
110         let addTodo = new Todo(id, text)
111         this.todos.unshift(addTodo) // 模型發生改變
112         this.render()  // 當模型發生改變,觸發視圖更新
113       },
114 
115       remove(item) {
116         let index = this.getItemIndex(item)
117         this.todos.splice(index, 1)
118         this.render()
119       }
120     }
121 
122     App.init()
123   })()
124 </script>
125 </body>
126 </html>

隨著界面和邏輯的復雜,用js或者jq去控制DOM不現實的。上邊例子只是用原生js模擬mvc的思想實現過程。真正地項目往往會依賴一些封裝好的優秀庫進行高效開發。

?

mvc模式的優點

mvc編程把所有精力放在數據處理,盡可能減少對網頁元素的處理。對于一定數量功能的網頁,Mvc模式下強制規范代碼簡化減少重復代碼,使代碼易于擴充

?

mvc模式的弊端

1、清晰的構架以代碼的復雜性為代價, 對小項目反而降低開發效率。?(如果本文的例子todoList用面條式代碼編寫,那得多簡單啊!!!)
2、控制層和視圖層耦合,導致沒有真正分離和重用?

3、在同一業務邏輯下,如果存在多種視圖呈現,需要視圖定義配置多個模板引擎、數據解析,多次處理數據與頁面更新。代碼就充滿了各種選擇器與事件回調,隨著業務的膨脹,變得難以維護。

?

總結:其實,現在MVC在前端用得比較少了,因為它的局限性,催生了MVVM模式的流行與廣泛使用,在下篇文章我會談談我對MVVM的理解,以及為何我使用基于MVVM模式的vue框架來高效開發。

轉載于:https://www.cnblogs.com/LIUYANZUO/p/7231703.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371654.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371654.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371654.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

BTrace:Java開發人員工具箱中的隱藏寶石

這篇文章是關于BTrace的 &#xff0c;我正在考慮將其作為Java開發人員的隱藏寶藏。 BTrace是用于Java平臺的安全&#xff0c;動態跟蹤工具。 BTrace可用于動態跟蹤正在運行的Java程序&#xff08;類似于DTrace&#xff0c;適用于OpenSolaris應用程序和OS&#xff09;。 不久&am…

python 圖片轉視頻ffmpeg_python圖片轉視頻(opencv),ffmpeg壓縮視頻

要注意&#xff1a;1. 圖片傳視頻要自己設置幀率和分辨率2.讀取圖片后分辨率要resize為和視頻分辨率一樣才可以3.寫完.avi視頻后視頻比較大&#xff0c;用ffmpeg將avi視頻壓縮為mp4import cv2from cv2 import VideoWriter, VideoWriter_fourcc, imread, resizeimport osfrom su…

門面模式

門面模式的定義 門面模式&#xff08;Facade Pattern&#xff09;也叫做外觀模式&#xff0c;是一種比較常用的封裝模式&#xff0c;其定義如 下&#xff1a; Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level interface tha…

Mysql數據庫申請

前段時間大部門下新成立了一個推廣百度OCR、文字識別、圖像識別等科技能力在金融領域應用的子部門。因為部門剛成立&#xff0c;基礎設施和人力都是欠缺的。當時分到我們部門的任務是抽調一個人做新部門主站前端開發工作。本來說的是只負責頁面的開發工作。當我參加過需求品審會…

Spring–添加SpringMVC –第2部分

在上一部分中&#xff0c;我們為經理和員工實現了控制器。 既然我們知道了解決方法&#xff0c;我們將做很少&#xff08;但僅做很少&#xff09;更復雜的事情–任務和時間表的控制器。 因此&#xff0c;讓我們從org.timesheet.web開始。 TaskController 。 首先創建一個類&…

php 正則分隔_探討PHP函數split()如何使用正則表達式切割字符串

對于初學者來說&#xff0c;掌握PHP中常用函數的用法&#xff0c;是其繼續學習的基礎。今天我們就為大家詳細介紹有關PHP函數split()的一些使用方法&#xff0c;希望大家能通過這篇文章介紹的內容增加自己的知識庫。說明array split ( string $pattern, string $string [, int …

通用的ProtostuffSerializer for Java

以前使用 protobuf或protostuff的時候覺得很麻煩&#xff0c;每個類都要單獨定制&#xff0c;于是封裝了一個類。 同事測試過&#xff0c;性能和壓縮率都很好&#xff0c;尤其是相比json的序列化。 需注意&#xff1a;只支持Pojo類&#xff08;即需要有get/set方法&#xff09;…

SAS筆記(6) PROC MEANS和PROC FREQ

PROC MEANS和PRC FREQ在做描述性分析的時候很常用&#xff0c;用法也比較簡單&#xff0c;不過這兩個過程步的某些選項容易忘記&#xff0c;本文就梳理一下。 在進入正文前&#xff0c;我們先創建所需的數據集TEST_SCORES&#xff1a; DATA TEST_SCORES; INPUT COUNTY : $9. SC…

休眠:保存vs持久并保存或更新

save和saveOrUpdate之間的區別是什么或save和persist之間的區別是任何Hibernate面試中常見的面試問題&#xff0c;就像Hibernate中get和load方法之間的區別一樣。 Hibernate Session類提供了幾種通過save &#xff0c; saveOrUpdate和persist等方法將對象保存到數據庫中的方法。…

php搜索數據庫設計,PHP數據庫搜索功能設計

其實搜索功能的設計很簡單&#xff0c;幾行代碼就可以完成。下面是form表單。從表單發出的數據名為search&#xff0c;然后發送到../admin/article_SearchResult.php這個文件處理。下面講下article_SearchResult.php這個文件如何實現搜索。$searchs $_POST[‘search‘];?>…

2016 Android Top 10 Library

過去的 2016 年&#xff0c;開源社區異常活躍&#xff0c;很多個人與公司爭相開源自己的項目&#xff0c;讓人眼花繚亂&#xff0c;然而有些項目只是曇花一現&#xff0c;有些項目卻持久創造價值&#xff0c;為開發者提供了極大的便利&#xff0c;這些終究由時間來判斷。今天&a…

集成JavaFX和Swing

我剛剛完成了對使用Swing的應用程序組件的重寫&#xff0c;現在正在使用JavaFX&#xff0c;最后得到了與更大的swing應用程序集成的JavaFX組件。 這是一個很大的應用程序&#xff0c;重寫花了我一段時間&#xff0c;最后一切都很好&#xff0c;我很高興自己做到了。 您可能想在…

提示錯誤:“應為“providerInvariantName”參數的非空字符串。”

我在調試Petapoco的T4模版的時候&#xff0c;鏈接一直報如題那個錯誤。在定性問題為配置文件后找的原因如下&#xff1a; <connectionStrings><add name"這個不行" connectionString"Data Sourcexxx;Initial Catalog數據庫名;User ID帳號;Password密碼…

php oop面試題,PHP面試題 - 對面向對象的理解

具體的題目應該是&#xff1a;什么是面向對象&#xff1f;主要的特征是什么&#xff1f;當然還有很多類似的題目&#xff0c;如果你說一下你對面向對象的理解&#xff0c;或者是你對比一下面向過程等等&#xff0c;諸如此類吧&#xff1f;如果我來回答這個問題&#xff0c;我會…

NOIP2014自測(晚自習兩節+上午兩節 共5個小時)

昨天剛剛考完試然后就翹晚自習跟今天上午兩節課的語文和英語做做noip2014的題目。然后去評測了一番。首先day1day2的t1基本都是模擬&#xff0c;一看就出思路那種&#xff0c;直接ac掉。代碼如下 day1t1&#xff1a;#include<iostream>#define maxn 209using namespace s…

您在eXo平臺上的第一個Juzu Portlet

菊珠是佛教的佛珠。 一句話&#xff0c;我相信您已經學到了什么&#xff0c;印象深刻嗎&#xff1f; 好的&#xff0c;我在這里不談論佛教。 Juzu還是一個用于快速開發Portlet&#xff08;以及即將推出的獨立應用程序&#xff09;的新框架。 您可以在Juzu網站上找到所需的所有…

Spring注入方式及注解配置

一&#xff1a;基于xml的DI&#xff08;Dependency Injection&#xff09; 注入類型&#xff1a; 定義學生Student實體類和小汽車Car實體類&#xff1a;進行封裝和生成ToString(),并自定義屬性Car Student 123456789101112131415161718192021222324252627282930313233343536373…

java 切面 不執行,解決springboot的aop切面不起作用問題(失效的排查)

檢查下springboot的啟動類是否開啟掃描springbootapplicationcomponentscan(basepackages {"com.zhangpu.springboot"})另外springboot默認開啟的enableaspectjautoproxy為true如果不放心可以增加&#xff1a;enableaspectjautoproxy(proxytargetclasstrue)第二種可…

修改readonly屬性的值

一般情況下&#xff0c;readonly屬性的值是無法修改的&#xff0c;但可以通過特殊方式修改。定義一個student的類&#xff0c;其中name屬性為readonly類型的變量 interface JFStudent : NSObjectproperty(nonatomic,copy,readonly) NSString *hisName;property(nonatomic,copy)…

VisualVM:通過SSH監視遠程JVM(是否為JMX)

VisualVM是用于監視JVM&#xff08;5.0&#xff09;的有關內存使用情況&#xff0c;線程&#xff0c;GC&#xff0c;MBeans等的出色工具。讓我們看看如何通過SSH使用它來監視&#xff08;甚至使用JMX對其進行采樣&#xff0c;對帶有JMX的遠程JVM進行監視&#xff09;它。 這篇文…