欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

僅30行代碼實(shí)現(xiàn)Javascript中的MVC

 更新時(shí)間:2016年02月15日 16:16:01   作者:ralph_zhu  
這篇文章主要介紹了僅30行代碼實(shí)現(xiàn)Javascript中的MVC的方法,MVC的基礎(chǔ)是觀(guān)察者模式,這是實(shí)現(xiàn)model和view同步的關(guān)鍵,想要深入了解的朋友可以參考本文

從09年左右開(kāi)始,MVC逐漸在前端領(lǐng)域大放異彩,并終于在剛剛過(guò)去的2015年隨著React Native的推出而迎來(lái)大爆發(fā):AngularJS、EmberJS、Backbone、ReactJS、RiotJS、VueJS…… 一連串的名字走馬觀(guān)花式的出現(xiàn)和更迭,它們中一些已經(jīng)漸漸淡出了大家的視野,一些還在迅速茁壯成長(zhǎng),一些則已經(jīng)在特定的生態(tài)環(huán)境中獨(dú)當(dāng)一面舍我其誰(shuí)。但不論如何,MVC已經(jīng)并將持續(xù)深刻地影響前端工程師們的思維方式和工作方法。

很多講解MVC的例子都從一個(gè)具體的框架的某個(gè)概念入手,比如Backbone的collection或AngularJS中model,這當(dāng)然不失為一個(gè)好辦法。但框架之所以是框架,而不是類(lèi)庫(kù)(jQuery)或者工具集(Underscore),就是因?yàn)樗鼈兊谋澈笥兄姸鄡?yōu)秀的設(shè)計(jì)理念和最佳實(shí)踐,這些設(shè)計(jì)精髓相輔相成,環(huán)環(huán)相扣,缺一不可,要想在短時(shí)間內(nèi)透過(guò)復(fù)雜的框架而看到某一種設(shè)計(jì)模式的本質(zhì)并非是一件容易的事。

這便是這篇隨筆的由來(lái)——為了幫助大家理解概念而生的原型代碼,應(yīng)該越簡(jiǎn)單越好,簡(jiǎn)單到剛剛足以大家理解這個(gè)概念就夠了。

1. MVC的基礎(chǔ)是觀(guān)察者模式,這是實(shí)現(xiàn)model和view同步的關(guān)鍵
為了簡(jiǎn)單起見(jiàn),每個(gè)model實(shí)例中只包含一個(gè)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中的值改變時(shí),應(yīng)通知注冊(cè)過(guò)的回調(diào)函數(shù)
  // 按照J(rèn)avascript事件處理的一般機(jī)制,我們異步地調(diào)用回調(diào)函數(shù)
  // 如果覺(jué)得setTimeout影響性能,也可以采用requestAnimationFrame
  setTimeout(function () {
    self._listeners.forEach(function (listener) {
      listener.call(self, value);
    });
  });
};
Model.prototype.watch = function (listener) {
  // 注冊(cè)監(jiān)聽(tīng)的回調(diào)函數(shù)
  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');
})();

借助觀(guān)察者模式,我們已經(jīng)實(shí)現(xiàn)了在調(diào)用model的set方法改變其值的時(shí)候,模板也同步更新,但這樣的實(shí)現(xiàn)卻很別扭,因?yàn)槲覀冃枰謩?dòng)監(jiān)聽(tīng)model值的改變(通過(guò)watch方法)并傳入一個(gè)回調(diào)函數(shù),有沒(méi)有辦法讓view(一個(gè)或多個(gè)dom node)和model更簡(jiǎn)單的綁定呢?

2. 實(shí)現(xiàn)bind方法,綁定model和view

Model.prototype.bind = function (node) {
  // 將watch的邏輯和通用的回調(diào)函數(shù)放到這里
  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');
})();

通過(guò)一個(gè)簡(jiǎn)單的封裝,view和model之間的綁定已經(jīng)初見(jiàn)雛形,即使需要綁定多個(gè)view,實(shí)現(xiàn)起來(lái)也很輕松。注意bind是Function類(lèi)prototype上的一個(gè)原生方法,不過(guò)它和MVC的關(guān)系并不緊密,筆者又實(shí)在太喜歡bind這個(gè)單詞,一語(yǔ)中的,言簡(jiǎn)意賅,所以索性在這里把原生方法覆蓋了,大家可以忽略。言歸正傳,雖然綁定的復(fù)雜度降低了,這一步依然要依賴(lài)我們手動(dòng)完成,有沒(méi)有可能把綁定的邏輯從業(yè)務(wù)代碼中徹底解耦呢?

3. 實(shí)現(xiàn)controller,將綁定從邏輯代碼中解耦

細(xì)心的朋友可能已經(jīng)注意到,雖然講的是MVC,但是上文中卻只出現(xiàn)了Model類(lèi),View類(lèi)不出現(xiàn)可以理解,畢竟HTML就是現(xiàn)成的View(事實(shí)上本文中從始至終也只是利用HTML作為View,javascript代碼中并沒(méi)有出現(xiàn)過(guò)View類(lèi)),那Controller類(lèi)為何也隱身了呢?別急,其實(shí)所謂的"邏輯代碼"就是一個(gè)框架邏輯(姑且將本文的原型玩具稱(chēng)之為框架)和業(yè)務(wù)邏輯耦合度很高的代碼段,現(xiàn)在我們就來(lái)將它分解一下。
如果要將綁定的邏輯交給框架完成,那么就需要告訴框架如何來(lái)完成綁定。由于JS中較難完成annotation(注解),我們可以在view中做這層標(biāo)記——使用html的標(biāo)簽屬性就是一個(gè)簡(jiǎn)單有效的辦法。

function Controller(callback) {
  var models = {};
  // 找到所有有bind屬性的元素
  var views = document.querySelectorAll('[bind]');
  // 將views處理為普通數(shù)組
  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);
  });
  // 調(diào)用controller的具體邏輯,將models傳入,方便業(yè)務(wù)處理
  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');
});


就這么簡(jiǎn)單嗎?就這么簡(jiǎn)單。MVC的本質(zhì)就是在controller中完成業(yè)務(wù)邏輯,并對(duì)model進(jìn)行修改,同時(shí)model的改變引起view的自動(dòng)更新,這些邏輯在上面的代碼中都有所體現(xiàn),并且支持多個(gè)view、多個(gè)model。雖然不足以用于生產(chǎn)項(xiàng)目,但是希望對(duì)大家的MVC學(xué)習(xí)多少有些幫助。

整理后去掉注釋的"框架"代碼:

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);
}

后記:

筆者在學(xué)習(xí)flux和redux的過(guò)程中,雖然掌握了工具的使用方法,但只是知其然而不知其所以然,對(duì)ReactJS官方文檔中一直強(qiáng)調(diào)的 "Flux eschews MVC in favor of a unidirectional data flow" 不甚理解,始終覺(jué)得單向數(shù)據(jù)流和MVC并不沖突,不明白為什么在ReactJS的文檔中這二者會(huì)被對(duì)立起來(lái),有他無(wú)我,有我無(wú)他(eschew,避開(kāi))。終于下定決心,回到MVC的定義上重新研究,雖然平日工作里大大咧咧復(fù)制粘貼,但是咱們偶爾也得任性一把,咬文嚼字一番,對(duì)吧?這樣的方式也的確幫助了我對(duì)于這句話(huà)的理解,這里可以把自己的思考分享給大家:之所以覺(jué)得MVC和flux中的單向數(shù)據(jù)流相似,可能是因?yàn)闆](méi)有區(qū)分清楚MVC和觀(guān)察者模式的關(guān)系造成的——MVC是基于觀(guān)察者模式的,flux也是,因此這種相似性的由來(lái)是觀(guān)察者模式,而不是MVC和flux本身。這樣的理解也在四人組的設(shè)計(jì)模式原著中得到了印證:"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. "。

如果讀者有興趣在這樣一個(gè)原型玩具的基礎(chǔ)上繼續(xù)拓展,可以參考下面的一些方向:

  • 1. 實(shí)現(xiàn)對(duì)input類(lèi)標(biāo)簽的雙向綁定
  • 2. 實(shí)現(xiàn)對(duì)controller所控制的scope的精準(zhǔn)控制,這里一個(gè)controller就控制了整個(gè)dom樹(shù)
  • 3. 實(shí)現(xiàn)view層有關(guān)dom node隱藏/顯示、創(chuàng)建/銷(xiāo)毀的邏輯
  • 4. 集成virtual dom,增加dom diff的功能,提高渲染效率
  • 5. 提供依賴(lài)注入功能,實(shí)現(xiàn)控制反轉(zhuǎn)
  • 6. 對(duì)innerHTML的賦值內(nèi)容進(jìn)行安全檢查,防止惡意注入
  • 7. 實(shí)現(xiàn)model collection的邏輯,這里每個(gè)model只有一個(gè)值
  • 8. 利用es5中的setter改變set方法的實(shí)現(xiàn),使得對(duì)model的修改更加簡(jiǎn)單
  • 9. 在view層中增加對(duì)屬性和css的控制
  • 10.支持類(lèi)似AngularJS中雙大括號(hào)的語(yǔ)法,只綁定部分html
  • ……

一個(gè)完善的框架要經(jīng)過(guò)無(wú)數(shù)的提煉和修改,這里只是最初最初的第一步,道路還很漫長(zhǎng),希望大家再接再厲。

相關(guān)文章

  • js禁止表單重復(fù)提交

    js禁止表單重復(fù)提交

    這篇文章主要介紹了js禁止表單重復(fù)提交的方法,避免重復(fù)記錄帶來(lái)的問(wèn)題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • javascript變量提升和閉包理解

    javascript變量提升和閉包理解

    本篇文章給大家詳細(xì)分析了javascript變量提升和閉包的相關(guān)知識(shí)點(diǎn),對(duì)此有興趣的朋友可以參考下。
    2018-03-03
  • Webpack處理樣式資源的配置詳情

    Webpack處理樣式資源的配置詳情

    Webpack 本身是不能識(shí)別樣式資源的,所以我們需要借助 Loader 來(lái)幫助 Webpack 解析樣式資源,本文就來(lái)介紹一下Webpack處理樣式資源的配置詳情,感興趣的可以了解一下
    2023-12-12
  • Bootstrap3 datetimepicker控件使用實(shí)例

    Bootstrap3 datetimepicker控件使用實(shí)例

    這篇文章主要為大家詳細(xì)介紹了Bootstrap3 datetimepicker控件使用實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • JS如何設(shè)置滾動(dòng)屬性默認(rèn)自動(dòng)滾動(dòng)到底部(overflow:scroll;)

    JS如何設(shè)置滾動(dòng)屬性默認(rèn)自動(dòng)滾動(dòng)到底部(overflow:scroll;)

    這篇文章主要給大家介紹了關(guān)于JS如何設(shè)置滾動(dòng)屬性默認(rèn)自動(dòng)滾動(dòng)到底部(overflow:scroll;)的相關(guān)資料,通過(guò)本文介紹的的JavaScript代碼示例,你可以實(shí)現(xiàn)滾動(dòng)條默認(rèn)在最底部的效果,需要的朋友可以參考下
    2023-10-10
  • 在點(diǎn)擊div中的p時(shí),如何阻止事件冒泡

    在點(diǎn)擊div中的p時(shí),如何阻止事件冒泡

    本文主要介紹了在點(diǎn)擊div中的p時(shí),如何阻止事件冒泡的方法,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02
  • javascript ajax 仿百度分頁(yè)函數(shù)

    javascript ajax 仿百度分頁(yè)函數(shù)

    百度分頁(yè)想必大家都知道吧,瀏覽網(wǎng)頁(yè)的朋友都應(yīng)該知道,下面有個(gè)小例子使用到了js、ajax等來(lái)模仿百度的分頁(yè),感興趣的朋友可以參考下
    2013-10-10
  • JavaScript Object的extend是一個(gè)常用的功能

    JavaScript Object的extend是一個(gè)常用的功能

    對(duì)Object的extend是一個(gè)常用的功能。舉一個(gè)例子,由于javascript 沒(méi)有重載(overload),而且函數(shù)的參數(shù)類(lèi)型是沒(méi)有定義的,所以很多時(shí)候我們都傳入一個(gè)對(duì)象來(lái)作為參數(shù)已方便控制。
    2009-12-12
  • element?UI中在?el-select?與?el-tree?結(jié)合組件實(shí)現(xiàn)過(guò)程

    element?UI中在?el-select?與?el-tree?結(jié)合組件實(shí)現(xiàn)過(guò)程

    項(xiàng)目上實(shí)現(xiàn)某個(gè)功能,使用到了?el-select?和?el-tree?組合實(shí)現(xiàn),記錄下兩者結(jié)合的實(shí)現(xiàn)過(guò)程,對(duì)?el-select?與?el-tree?結(jié)合組件實(shí)現(xiàn)過(guò)程感興趣的朋友跟隨小編一起看看吧
    2023-02-02
  • js removeChild 方法深入理解

    js removeChild 方法深入理解

    下面小編就為大家?guī)?lái)一篇js removeChild 方法深入理解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08

最新評(píng)論