1. <tt id="5hhch"><source id="5hhch"></source></tt>
    1. <xmp id="5hhch"></xmp>

  2. <xmp id="5hhch"><rt id="5hhch"></rt></xmp>

    <rp id="5hhch"></rp>
        <dfn id="5hhch"></dfn>

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

        時(shí)間:2020-10-28 18:07:01 JavaScript 我要投稿

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

        一連串的名字走馬觀花式的出現(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è)好辦法。但框架之所以是框架,而不是類庫(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ǔ)是觀察者模式,這是實(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)聽的回調(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');

        })();

         

        借助觀察者模式,我們已經(jīng)實(shí)現(xiàn)了在調(diào)用model的set方法改變其值的時(shí)候,模板也同步更新,但這樣的實(shí)現(xiàn)卻很別扭,因?yàn)槲覀冃枰謩?dòng)監(jiān)聽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è)model上綁定多個(gè)view,實(shí)現(xiàn)起來(lái)也很輕松。注意bind是Function類prototype上的一個(gè)原生方法,不過(guò)它和MVC的關(guān)系并不緊密,筆者又實(shí)在太喜歡bind這個(gè)單詞,一語(yǔ)中的,言簡(jiǎn)意賅,所以索性在這里把原生方法覆蓋了,大家可以忽略。言歸正傳,雖然綁定的復(fù)雜度降低了,這一步依然要依賴我們手動(dòng)完成,有沒(méi)有可能把綁定的邏輯從業(yè)務(wù)代碼中徹底解耦呢?

         

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

         

        細(xì)心的朋友可能已經(jīng)注意到,雖然講的是MVC,但是上文中卻只出現(xiàn)了Model類,View類不出現(xiàn)可以理解,畢竟HTML就是現(xiàn)成的View(事實(shí)上本文中從始至終也只是利用HTML作為View,javascript代碼中并沒(méi)有出現(xiàn)過(guò)View類),那Controller類為何也隱身了呢?別急,其實(shí)所謂的”邏輯代碼”就是一個(gè)框架邏輯(姑且將本文的原型玩具稱之為框架)和業(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)單:在Controller中完成業(yè)務(wù)邏輯并對(duì)Model進(jìn)行修改,Model的`變化觸發(fā)View的自動(dòng)更新,怎么樣,算得上一個(gè)有模有樣的MVC吧?當(dāng)然,這樣的”框架”還不足以用于生產(chǎn)環(huán)境,不過(guò)如果它能或多或少地幫助到大家對(duì)于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()).bind(view);

            });

            callback.call(this, models);

        }

         

        4. 一個(gè)簡(jiǎn)單的例子

         

        下面請(qǐng)大家看一個(gè)簡(jiǎn)單例子,如何實(shí)現(xiàn)電子表

         

        // html:

        <span bind="hour"></span> : <span bind="minute"></span> : <span bind="second"></span>

        // controller:

        new Controller(function (models) {

            function setTime() {

                var date = new Date();

                models.hour.set(date.getHours());

                models.minute.set(date.getMinutes());

                models.second.set(date.getSeconds());

            }

            setTime();

            setInterval(setTime, 1000);

        });

         

        可以看出,controller中只負(fù)責(zé)更新model的邏輯,和view完全解耦;而view和model的綁定是通過(guò)view中的屬性和框架中controller的初始化代碼完成的,也沒(méi)有出現(xiàn)在業(yè)務(wù)邏輯中;至于view的更新,也是通過(guò)框架中的觀察者模式實(shí)現(xiàn)的。


        国产高潮无套免费视频_久久九九兔免费精品6_99精品热6080YY久久_国产91久久久久久无码

        1. <tt id="5hhch"><source id="5hhch"></source></tt>
          1. <xmp id="5hhch"></xmp>

        2. <xmp id="5hhch"><rt id="5hhch"></rt></xmp>

          <rp id="5hhch"></rp>
              <dfn id="5hhch"></dfn>