從09年左右開始,MVC逐漸在前端領域大放異彩,並終於在剛剛過去的2015年隨著React Native的推出而迎來大爆發:AngularJS、EmberJS、Backbone、ReactJS、RiotJS、VueJS… … 一連串的名字走馬觀花式的出現和更迭,它們中一些已經漸漸淡出了大家的視野,一些還在迅速茁壯成長,一些則已經在特定的生態環境中獨當一面舍我其誰。但不論如何,MVC已經並將持續深刻影響前端工程師們的思維方式與工作方法。
許多講解MVC的例子都從一個具體的框架的某個概念入手,比如Backbone的collection或AngularJS中model,這當然不失為一個好辦法。但框架之所以是框架,而不是類別庫(jQuery)或工具集(Underscore),就是因為它們的背後有著眾多優秀的設計理念和最佳實踐,這些設計精髓相輔相成,環環相扣,缺一不可,要想在短時間內透過複雜的框架而看到某一種設計模式的本質並非是一件容易的事。
這便是這篇隨筆的由來-為了幫助大家理解概念而生的原型程式碼,應該越簡單越好,簡單到剛好足以大家理解這個概念就夠了。
1. MVC的基礎是觀察者模式,這是實現model和view同步的關鍵
為了簡單起見,每個model實例只包含一個primitive value值。
function Model(value) { this._value = typeof value === 'undefined' ? '' : value; this._listeners = []; } Model.prototype.set = function (value) { var self = this; self._value = value; // model中的值改变时,应通知注册过的回调函数 // 按照Javascript事件处理的一般机制,我们异步地调用回调函数 // 如果觉得setTimeout影响性能,也可以采用requestAnimationFrame setTimeout(function () { self._listeners.forEach(function (listener) { listener.call(self, value); }); }); }; Model.prototype.watch = function (listener) { // 注册监听的回调函数 this._listeners.push(listener); };
// html代码: <div id="div1"></div> // 逻辑代码: (function () { var model = new Model(); var div1 = document.getElementById('div1'); model.watch(function (value) { div1.innerHTML = value; }); model.set('hello, this is a div'); })();
借助觀察者模式,我們已經實現了在調用model的set方法改變其值的時候,模板也同步更新,但這樣的實作卻很彆扭,因為我們需要手動監聽model值的改變(透過watch方法)並傳入一個回呼函數,有沒有辦法讓view(一個或多個dom node)和model更簡單的綁定呢?
2. 實作bind方法,綁定model和view
Model.prototype.bind = function (node) { // 将watch的逻辑和通用的回调函数放到这里 this.watch(function (value) { node.innerHTML = value; }); };
// html代码: <div id="div1"></div> <div id="div2"></div> // 逻辑代码: (function () { var model = new Model(); model.bind(document.getElementById('div1')); model.bind(document.getElementById('div2')); model.set('this is a div'); })();
透過一個簡單的封裝,view和model之間的綁定已經初見雛形,即使需要綁定多個view,實現起來也很輕鬆。注意bind是Function類prototype上的一個原生方法,不過它和MVC的關係並不緊密,筆者又實在太喜歡bind這個單詞,一語中的,言簡意賅,所以索性在這裡把原生方法覆蓋了,大家可以忽略。言歸正傳,雖然綁定的複雜度降低了,這一步依然要依賴我們手動完成,有沒有可能把綁定的邏輯從業務程式碼中徹底解耦呢?
3. 實作controller,將綁定從邏輯程式碼解耦
細心的朋友可能已經注意到,雖然講的是MVC,但是上文中卻只出現了Model類,View類不出現可以理解,畢竟HTML就是現成的View(事實上本文中從頭到尾也只是利用HTML作為View,javascript程式碼中並沒有出現過View類別),那麼Controller類別為何也隱身了呢?別急,其實所謂的"邏輯代碼"就是一個框架邏輯(姑且將本文的原型玩具稱之為框架)和業務邏輯耦合度很高的代碼段,現在我們就來將它分解一下。
如果要將綁定的邏輯交給框架完成,那麼就需要告訴框架如何完成綁定。由於JS中較難完成annotation(註解),我們可以在view中做這層標記-使用html的標籤屬性就是一個簡單有效的方法。
function Controller(callback) { var models = {}; // 找到所有有bind属性的元素 var views = document.querySelectorAll('[bind]'); // 将views处理为普通数组 views = Array.prototype.slice.call(views, 0); views.forEach(function (view) { var modelName = view.getAttribute('bind'); // 取出或新建该元素所绑定的model models[modelName] = models[modelName] || new Model(); // 完成该元素和指定model的绑定 models[modelName].bind(view); }); // 调用controller的具体逻辑,将models传入,方便业务处理 callback.call(this, models); }
// html: <div id="div1" bind="model1"></div> <div id="div2" bind="model1"></div> // 逻辑代码: new Controller(function (models) { var model1 = models.model1; model1.set('this is a div'); });
就這麼簡單嗎?就這麼簡單。 MVC的本質就是在controller中完成業務邏輯,並對model進行修改,同時model的改變引起view的自動更新,這些邏輯在上面的程式碼中都有所體現,並且支援多個view、多個model。雖然不足以用於生產項目,但希望對大家的MVC學習多少有些幫助。
整理後去掉註解的"框架"代碼:
function Model(value) { this._value = typeof value === 'undefined' ? '' : value; this._listeners = []; } Model.prototype.set = function (value) { var self = this; self._value = value; setTimeout(function () { self._listeners.forEach(function (listener) { listener.call(self, value); }); }); }; Model.prototype.watch = function (listener) { this._listeners.push(listener); }; Model.prototype.bind = function (node) { this.watch(function (value) { node.innerHTML = value; }); }; function Controller(callback) { var models = {}; var views = Array.prototype.slice.call(document.querySelectorAll('[bind]'), 0); views.forEach(function (view) { var modelName = view.getAttribute('bind'); models[modelName] = models[modelName] || new Model(); models[modelName].bind(view); }); callback.call(this, models); }
後記:
筆者在學習flux和redux的過程中,雖然掌握了工具的使用方法,但只是知其然而不知其所以然,對ReactJS官方文件中一直強調的"Flux eschews MVC in favor of a unidirectional data flow"不甚理解,始終覺得單向資料流和MVC並不衝突,不明白為什麼在ReactJS的文檔中這二者會被對立起來,有他無我,有我無他(eschew,避開)。終於下定決心,回到MVC的定義上重新研究,雖然平日工作里大大咧咧複製粘貼,但是咱們偶爾也得任性一把,咬文嚼字一番,對吧?這樣的方式也的確幫助了我對這句話的理解,這裡可以把自己的思考分享給大家:之所以覺得MVC和flux中的單向資料流相似,可能是因為沒有區分清楚MVC和觀察者模式的關係所造成的-MVC是基於觀察者模式的,flux也是,因此這種相似性的由來是觀察者模式,而不是MVC和flux本身。這樣的理解也在四人組的設計模式原著中得到了印證:"The first and perhaps best-known example of the Observer pattern appears in Smalltalk Model/View/Controller (MVC), the user interface framework in the Smalltalk environment [KP88]. MVC's Model class plays the role of Subject, while View is the base class for observers. "。
如果讀者有興趣在這樣一個原型玩具的基礎上繼續拓展,可以參考下面的一些方向:
- 1. 實作對input類別標籤的雙向綁定
- 2. 實現對controller所控制的scope的精準控制,這裡一個controller就控制了整個dom樹
- 3. 實作view層有關dom node隱藏/顯示、建立/銷毀的邏輯
- 4. 整合virtual dom,增加dom diff的功能,提高渲染效率
- 5. 提供依賴注入功能,實現控制反轉
- 6. 對innerHTML的賦值內容進行安全檢查,防止惡意注入
- 7. 實作model collection的邏輯,這裡每個model只有一個值
- 8. 利用es5中的setter改變set方法的實現,使得model的修改更加簡單
- 9. 在view層中增加對屬性和css的控制
- 10.支援類似AngularJS中雙大括號的語法,只綁定部分html
- ……
一個完善的框架要經過無數的提煉和修改,這裡只是最初最初的第一步,道路還很漫長,希望大家再接再厲。

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

記事本++7.3.1
好用且免費的程式碼編輯器

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。