深刻Vue實現原理,實現一個響應式框架

歡迎你們訪問個人我的網站 - Sunday俱樂部html


在前面的章節中咱們已經學習了Vue.js的基礎內容而且瞭解了Vue.js的源碼實現,包括:Vue的生命週期、Vue的數據響應、Vue的渲染流程等等,在這一章節咱們會和你們一塊兒去實現一個響應式的框架 -- MVueMVue 會遵循Vue的代碼邏輯和實現思路,咱們但願可以藉助MVue來讓你們更好的理解整個Vue的核心思想:響應式數據渲染。前端

在開始咱們的MVue開發以前,咱們須要先了解一些必備的知識。首先是Object.defineProperty(obj, prop, descriptor),這個方法能夠用來定義對象的屬性描述符咱們能夠點擊這裏來查看這個方法的詳細定義。咱們這裏主要使用到的是get、set描述符,咱們可使用get、set來監聽對象的屬性setter、getter的調用事件。咱們看一下下面的這段代碼:vue

<div>
    <input type="text" id="input-msg">
    <p id="output-msg"></p>
</div>

<script>
    var obj = {
        msg: 'hello'
    };

    var key = 'msg';
    
    var val = obj[key];

    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        set: function (newValue) {
            val = newValue;
            console.log('setter');
        },

        get: function () {
            console.log('getter');
            return val;
        }
    })
    
</script>
複製代碼

在上面的代碼中咱們利用Object.defineProperty監聽了obj.msgsetter、getter事件。全部當咱們在控制檯去調用obj.msg的時候就會調用console.log('getter');,當咱們調用obj.msg = '123' 的時候就會調用console.log('setter');,由此咱們就成功的監聽了obj.msg的數據變化。那麼這有什麼意義呢?咱們能夠利用這個功能來作些什麼呢?咱們看一下下面的代碼:node

<div>
    <input type="text" id="input-msg">
    <p id="output-msg"></p>
</div>

<script>
    var inputMsg = document.getElementById('input-msg'),
        outputMsg = document.getElementById('output-msg');


    var obj = {
        msg: 'hello'
    };

    var key = 'msg';
    
    var val = obj[key];
    

    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        set: function (newValue) {
            val = newValue;

            outputMsg.innerText = obj[key];
        },

        get: function () {
            console.log('getter');
            return val;
        }
    });


    inputMsg.addEventListener('input', function (event) {
        var newVal = event.target.value;
        obj[key] = newVal;
    });
    
</script>
複製代碼

在上面的代碼中,咱們經過監聽inputinput事件來去改變obj[key]的值,使obj[key]的值始終等於用戶輸入的值,當obj[key]的值由於用戶的輸入而發生了改變的時候,會激活Object.defineProperty中的setter事件,而後咱們獲取到最新的obj[key]的值並把它賦值給outputMsg。這樣當咱們在input中進行輸入的時候,<p>中的值也會跟隨咱們的輸入變化。這種經過Object.defineProperty來監聽數據變化的方式就是Vue數據響應的核心思想。ios

其次你們須要瞭解的就是觀察者模式,你們能夠點擊這裏來查看觀察者模式的詳細解釋,相信這裏會比我解釋的更加清楚。git

當你們瞭解完觀察者模式以後咱們就能夠正式開始咱們的MVue的開發工做。github

思路整理

整個框架的思路被分紅三大塊。vue-router

首先就是**視圖渲染,**咱們在html或者<template></template>中進行html內容編寫的時候,每每是這樣:vue-cli

<div id="app">
    <input type="text" v-model='msg'>
    <div>
        <p>{{msg}}</p>
    </div>
</div>
複製代碼

其中的v-model='msg'{{msg}} 瀏覽器是沒法解析的,那麼咱們就須要把 瀏覽器不認識的內容轉化爲瀏覽器能夠解析的內容,在Vue中,Vue經過**虛擬DOM(VNode)**描述真實DOM,而後經過_update來進行具體渲染。咱們這裏不去描述這個VNode直接經過_update方法來對DOM進行渲染操做,這個動做是發生在Compile中。Compile會解析咱們的具體指令,並從新渲染DOM編程

其次是監聽咱們的數據變化,在最初的例子中咱們已經知道咱們能夠經過Object.defineProperty(obj, prop, descriptor)來實現數據的監聽,那麼就須要一個Observer類來進行數據劫持的工做,這時Observer承擔的就是發佈者的工做。當咱們經過Observer來監聽到數據變化以後,咱們須要通知咱們的觀察者,可是對於咱們的發佈者來講,它並不知道誰是這個觀察者,這個觀察者是一個仍是多個?因此這個時候,就須要有一我的來負責去收集這些依賴的工做,這我的就是Dep(Dependency),咱們經過Dep來去通知觀察者WatcherWatcher訂閱DepDep持有Watcher,二者互相依賴造成一個消息中轉站。Watcher接收到消息,須要更改視圖的時候,那麼就會發布具體的消息根據具體指令的不一樣(Directive來執行具體的操做Patch。這就是咱們的整個從監聽到渲染的過程,以下圖:

這裏寫圖片描述

最後咱們須要把全部的東西整合起來造成一個入口函數,輸出給用戶方便用戶進行調用,就好像Vue中的new Vue({})操做,這裏咱們叫它MVue

綜合以上的內容,咱們須要完成的代碼內容包括

├── compile.js	渲染DOM,解析指令
├── dep.js	收集依賴
├── directive.js	全部支持到的指令
├── mvue.js	入口函數
├── observer.js	數據劫持
├── patch.js	根據具體的指令來修改渲染的內容
└── watcher.js	觀察者。訂閱Dep,發佈消息
複製代碼

咱們預期的完成效果應該是這樣

<div id="app">
    <input type="text" v-model='msg'>
    <div>
        <p>{{msg}}</p>
    </div>
</div>

<script>
    var vm = new MVue({
        el: '#app',
        data: {
            msg: 'hello'
        }
    });
</script>
複製代碼

入口函數

首先咱們須要先生成MVue的入口函數,咱們仿照Vue的寫法,建立一個MVue的類,並獲取傳入的options

function MVue (options) {
    this.$options = options;
    this._data = options.data || {};
}

MVue.prototype = {
    _getVal: function (exp) {
        return this._data[exp];
    },

    _setVal: function (exp, newVal) {
        this._data[exp] = newVal;
    }
}
複製代碼

首先咱們實現一個MVue的構造函數,併爲它提供了兩個私有的原型方法_getVal_setVal用於獲取和設置data中對應key的值。這時咱們就能夠經過下面的代碼來建立對應的MVue實例。

var vm = new MVue({
    el: '#app',
    data: {
        msg: 'hello'
    }
});
複製代碼

而後咱們就能夠在MVue的構造函數之中去進行咱們的 視圖渲染數據監聽 的操做。

視圖渲染

而後咱們進行咱們的視圖渲染,咱們再來回顧一下咱們須要解析的視圖結構

<div id="app">
    <input type="text" v-model='msg'>
    <div>
        <p>{{msg}}</p>
    </div>
</div>
複製代碼

在這段html之中v-model{{msg}}是咱們MVue中的自定義指令,這些指令咱們的瀏覽器是沒法解析的,因此須要咱們把這些指令解析爲瀏覽器能夠解析的html代碼。以<p>{{msg}}</p>爲例,當咱們聲明data: {msg: 'hello'}的時候,應解析爲<p>hello</p>

咱們的模板解析的操做是經過compile.js來完成的。

function Compile (vm, el) {
    this.$vm = vm;
    el = this.$el = this.isElementNode(el) ? el : document.querySelector(el);

    if (!el) {
        return;
    }

    this._update(el);
};

Compile.prototype = {

    /**
     * Vue中使用vm._render先根據真實DOM建立了虛擬DOM,而後在vm._update把虛擬DOM轉化爲真實DOM並渲染,
     * 咱們這裏沒有虛擬DOM,因此直接經過createElm方法建立一個fragment用以渲染
     */
    _update: function (el) {
        this.$fragment = document.createDocumentFragment();
        // 複製el的內容到建立的fragment
        this.createElm(el);
        // 把解析以後的fragment放入el中,此時fragment中的全部指令已經被解析爲具體數據
        el.appendChild(this.$fragment);
    },

    /**
     * 建立新的DOM 用來替換 原DOM
     */
    createElm: function (node) {
        var childNode = node.firstChild;
        if (childNode) {
            this.$fragment.appendChild(childNode);
            this.createElm(node);
        }
    }
} 

複製代碼

咱們聲明瞭一個Compile的構造方法,並調用了它的_update原型函數,在_update中咱們聲明瞭一個fragment用於承載解析以後的模板內容,經過createElm的遞歸調用獲取el中的元素,並把獲取出的元素放入fragment中,最後把fragment添加到el裏面。至此咱們已經成功的獲取到了el中的元素,並把這些元素從新規制。

接下來咱們就須要對獲取出來的元素進行解析操做,其實就是對v-model{{*}}等指令進行解析,這個解析的時機應該在 **遍歷出全部的元素以後,添加fragmentel以前。**咱們看一下解析DOM的代碼:

Compile.prototype = {
	_update: function (el) {
	    ...
	    // 解析被建立完成的fragment,此時fragment已經擁有了el內全部的元素
	    this.compileElm();
	    ...
	},
	...
	/**
	 * 對DOM進行解析
	 */
	compileElm: function (childNodes) {
	    var reg = /\{\{(.*)\}\}/;
	    if (!childNodes) {
	        childNodes = this.$fragment.childNodes;
	    }
	    
	    [].slice.call(childNodes).forEach(node => {
	        if (node.childNodes.length > 0) {
	            // 迭代全部的節點
	            this.compileElm(node.childNodes);
	        }
	
	        // 獲取elementNode節點
	        if (this.isElementNode(node)) {
	            if (reg.test(node.textContent)) {
	                // 匹配 {{*}}
	                this.compileTextNode(node, RegExp.$1);
	            } 
	            // 匹配elementNode
	            this.compileElmNode(node);
	            
	        } 
	    });
	},
	/**
	 * 解析elementNode,獲取elm的全部屬性而後便利,檢查屬性是否屬於已經註冊的指令,
	 * 若是不是咱們的自定義指令,那麼就不須要去處理它了
	 * 若是是已註冊的指令,咱們就交給directive去處理。(演示只有一個v-model)
	 */
	compileElmNode: function (node) {
	    var attrs = [].slice.call(node.attributes),
	        $this = this;
	
	    attrs.forEach(function (attr) {
	        if (!$this.isDirective(attr.nodeName)) {
	            return;
	        }
	
	        var exp = attr.value;
	        // 匹配v-model指令
	        directives.model($this.$vm, node, exp);
	        // 去掉自定義指令
	        node.removeAttribute(attr.name);
	    });
	},
	/**
	 * 解析{{*}}
	 */
	compileTextNode: function (node, exp) {
	    directives.text(this.$vm, node, exp);
	},
	/**
	 * 判斷是不是已註冊的指令,這裏就判斷是否包含 v-
	 */
	isDirective: function (attrNodeName) {
	    return attrNodeName.indexOf('v-') === 0;
	},
	/**
	 * 判斷elmNode節點
	 */
	isElementNode: function (node) {
	    return node.nodeType === 1;
	}
}
複製代碼

由上面的代碼能夠看出,解析的操做主要在compileElm方法中進行,這個方法首先獲取到fragmentchildNodes,而後對childNodes進行了forEach操做,若是其中的node還有子節點的話,則會再次調用compileElm方法,而後解析這個node,若是是一個ElementNode節點,則再去判斷是否爲{{*}}雙大括號結構,若是是則會執行compileTextNode來解析{{*}},而後經過compileElmNode來解析ElmNode中的指令。

compileTextNode中的實現比較簡單,主要是調用了directives.text(vm, node, exp)進行解析,這裏咱們稍後再看,咱們先主要來看下compileElmNode作了什麼。

compileElmNode首先把node中全部的屬性轉成了數組並拷貝給了attrs,而後對attrs進行遍歷獲取其中的指令,由於咱們目前只有一個v-model指令,因此咱們不須要在對指令進行判斷,能夠直接調用directives.model(vm, node, exp)來進行v-model的指令解析,最後在DOM中刪除咱們的自定義指令。

至此咱們就複製了el的全部元素,並根據不一樣的指令把它們交由directives中對應的指令解析方法進行解析,這就是咱們compile.js中所作的全部事情。接下來咱們看一下directives是如何進行指令解析操做的,代碼以下:

// directives.js

/**
 * 指令集和
 * 
 * v-model
 */
var directives = {
    /**
     * 連接patch方法,將指令轉化爲真實的數據並展現
     */
    _link: function (vm, node, exp, dir) {
        var patchFn = patch(vm, node, exp, dir);
        patchFn  && patchFn(node, vm._getVal(exp));
    },

    /**
     * v-model事件處理,這裏的v-model只針對了<input type='text'> 
     */
    model: function (vm, node, exp) {
        this._link(vm, node, exp, 'model');

        var val = vm._getVal(exp);
        node.addEventListener('input', function (e) {
            var newVal = e.target.value;
            if (newVal === val) return;
            vm._setVal(exp,newVal);
            val = newVal;
        });
    },

    /**
     * {{}}事件處理
     */
    text: function (vm, node, exp) {
        this._link(vm, node, exp, 'text');
    }
}
複製代碼

由上面的代碼咱們能夠看出,咱們首先定義了一個directives變量,它包含了_link、model、text三個指令方法,其中_link爲私有方法,model、text爲公開的指令方法,關於_link咱們最後在分析,咱們先來看一下model

model指令方法對應的爲v-model指令,它接受三個參數,vm爲咱們的MVue實例,node爲綁定該指令的對應節點,exp爲綁定數據的key。咱們先不去管this._link的調用,你們先來想一下咱們在index.html中對於v-model的使用,咱們把v-model='msg'綁定到了咱們的input標籤上,意爲當咱們在input上進行輸入的時候msg始終等於咱們輸入的值。那麼咱們在model指令方法中所要作的事情就很明確了,首先咱們經過vm._getVal(exp);獲取到msg當前值,而後咱們監聽了nodeinput事件,獲取當前用戶輸入的最新值,而後經過vm._setVal(exp,newVal)配置到vm._data中,最後經過val = newVal從新設置val的值。

而後是text指令方法,這個方法直接調用了this._link,而且咱們還記得在model指令方法中也調用了this._link,那麼咱們來看一下_link的實現。

_link中,他接收四個參數,其中dir爲咱們的指令代碼,而後它調用了一個patch方法,獲取到了一個patchFn的變量,這個patch方法位於patch.js中。

// patch.js

/**
 * 更改node value,在編譯以前,替換 v-model  {{*}} 爲真實數據
 * @param {*} vm 
 * @param {*} node 
 * @param {*} exp 
 * @param {*} dir 
 */
function patch (vm, node, exp, dir) {

    switch (dir) {
        case 'model':
        /**
         * input / textear
         */
        return function (node , val) {
            node.value = typeof val === 'undefined' ? '' : val;
        }
        case 'text':
        /**
         * {{*}}
         */
        return function (node , val) {
            node.textContent = typeof val === 'undefined' ? '' : val;
        }
    }

}
複製代碼

patch的方法實現比較簡單,它首先去判斷了傳入的指令,而後根據不一樣的指令返回了不一樣的函數。好比在model指令方法中,由於咱們只支持input、 textear,因此咱們接收到的node只會是它們兩個中的一個,而後咱們經過node.value = val來改變node中的value

咱們在directives.js中獲取到了patch的返回函數patchFn,而後執行patchFn。至此咱們的模板已經被解析爲瀏覽器能夠讀懂的html代碼。

<div id="app">
   <input type="text">
   <div>
       <p>hello</p>
   </div>
</div>
複製代碼

數據監聽實現

而後咱們來看一下 數據監聽模塊的實現 ,咱們根據上面的 思路整理 想一下這個數據監聽應該如何去實現?咱們知道了咱們應該在observer裏面去實現它,可是具體應該怎麼作呢?

再來明確一下咱們的目標,咱們但願 **經過observer可以監聽到咱們數據data的變化,當咱們調用data.msg或者data.msg = '123'的時候,會分別激活getter或者setter方法。**那麼咱們就須要對整個data進行監聽,當咱們獲取到data對象以後,來遍歷其中的全部數據,並分別爲它們添加上gettersetter方法。

// observer.js

function observer (value) {
    if (typeof value !== 'object') {
        return;
    }

    var ob = new Observer(value);
}


function Observer (data) {
    this.data = data;
    this.walk();
}

Observer.prototype = {

    walk: function () {
        var $this = this;
        var keys = Object.keys(this.data);
        keys.forEach(function (key) {
            $this.defineReactive(key, $this.data[key]);
        });
    },

    defineReactive: function (key, value) {
        var dep = new Dep();
        Object.defineProperty(this.data, key, {
            enumerable: true,
            configurable: true,
            set: function (newValue) {
                if (value === newValue) {
                    return;
                }
                value = newValue;
                dep.notify();
            },

            get: function () {
                dep.depend();
                return value;
            }
        });
    },
}
複製代碼

observer.js中咱們經過observer (value)方法來生成Observer對象,其中傳入的valuedata: {msg: 'hello'}。而後調用Observer的原型方法walk,遍歷data調用defineReactive,經過Object.defineProperty爲每條數據都添加上setter、getter監聽,同時咱們聲明瞭一個Dep對象,這個Dep對象會負責收集依賴而且派發更新。你們結合咱們的思路整理想一下,咱們應該在何時去收集依賴?何時去派發更新

當用戶經過input進行輸入修改數據的時候,咱們是否是應該及時更新視圖?因此在setter方法被激活的時候,咱們應該調用dep.notify()方法,用於派發更新事件

當咱們的數據被展現出來的時候,也就是在getter事件被激活的時候,咱們應該去收集依賴,也就是調用dep.depend()方法。

而後咱們來看一下Dep方法的實現,在Dep.js中。

// Dep.js

var uid = 0;
function Dep () {
    // 持有的watcher訂閱者
    this.subs = [];
    this.id = uid++;
}

Dep.prototype = {
    // 使dep與watcher互相持有
    depend () {
        // Dep.target爲watcher實例
        if (Dep.target) {
            Dep.target.addDep(this)
        }
    },
    // 添加watcher
    addSub: function (sub) {
        this.subs.push(sub);
    },
    // 通知全部的watcher進行更新
    notify: function () {
        this.subs && this.subs.forEach(function (sub) {
            sub.update();
        });
    }
}
複製代碼

Dep.js的實現比較簡單,它主要是就負責收集依賴(watcher)而且派發更新(watcher.update(),咱們能夠看到Dep首先聲明瞭subs用於保存訂閱了Depwatcher實例,而後給每一個Dep實例建立了一個id,而後咱們爲Dep聲明瞭三個原型方法,當調用notify的時候,Dep回去遍歷全部的subs而後調用他的update()方法,當調用depend的時候會調用watcheraddDep方法使DepWatcher互相持有。其中的Dep.targetsub都爲Watcher實例。

而後咱們來看一下Watcher.js的代碼實現。

// watcher

function Watcher (vm, exp, patchFn) {
    this.depIds = {};
    this.$patchFn = patchFn;
    this.$vm = vm;
    this.getter = this.parsePath(exp)
    this.value = this.get();
}

Watcher.prototype = {
    // 更新
    update: function () {
        this.run();
    },
    // 執行更新操做
    run: function () {
        var oldVal = this.value;
        var newVal = this.get();
        if (oldVal === newVal) {
            return;
        }
        this.$patchFn.call(this.$vm, newVal);
    },
    // 訂閱Dep
    addDep: function (dep) {
        if (this.depIds.hasOwnProperty(dep.id)) {
            return;
        }
        dep.addSub(this);
        this.depIds[dep.id] = dep;
    },
    // 獲取exp對應值,這時會激活observer中的get事件
    get: function () {
        Dep.target = this;
        var value = this.getter.call(this.$vm, this.$vm._data);
        Dep.target = null;
        return value;
    },
    /**
     * 獲取exp的對應值,應對a.b.c
     */
    parsePath: function (path) {
        var segments = path.split('.');

        return function (obj) {
          for (let i = 0; i < segments.length; i++) {
            if (!obj) return
            obj = obj[segments[i]]
          }
          return obj
        }
      }
}
複製代碼

Watcher.js中它直接接收了patchFn,你們還記得這個方法是幹什麼的吧?patchFn是更改node value,在編譯以前,替換 v-model 、 {{*}} 爲真實數據的方法,在Watcher.js接收了patchFn,並把它賦值給this.$patchFn,當咱們調用this.$patchFn的時候,就會改變咱們的DOM渲染。

而後咱們調用parsePath用於解析對象數據,並返回一個解析函數,而後把它賦值給this.getter。最後咱們調用get()方法,在get()中咱們給Dep.target持有了Watcher,並激活了一次getter方法,使咱們在observer中監聽的getter事件被激活,會調用dep.depend()方法,而後調用watcher.addDep(dep),使DepWatcher互相持有,相互依賴。

而後咱們看一下update方法的實現,咱們知道當數據的setter事件被激活的時候,會調用dep.notify(),dep.notify()又會遍歷全部的訂閱watcher執行update方法,那麼在upadte方法中,直接執行了this.run,在run()方法中,首先獲取了 當前watcher所觀察的exp的改變前值oldVal和修改後值newVal,而後經過patchFn去修改DOM

以上就是咱們整個數據監聽的流程,它首先經過observer來監聽數據的變化,而後當數據的getter事件被激活的時候,調用dep.depend()來進行依賴收集,當數據的setter事件被激活的時候,調用dep.notify()來進行派發更新,這些的具體操做都是在咱們的觀察者watcher中完成的。

整合MVue

最後咱們就須要把咱們的 視圖渲染數據監聽 連接起來,那麼這個鏈接的節點應該在哪裏呢?咱們再來捋一下咱們的流程。

當用戶編寫了咱們的指令代碼

<div id="app">
    <inputtype="text" v-model='msg'>
    <div>
        <p>{{msg}}</p>
    </div>
</div>
複製代碼

的時候,咱們經過Compile進行解析,當發現了咱們的自定義指令v-model、{{*}}的時候,會進行directives進行指令解析,其中監聽的用戶的輸入事件,並調用了vm._setVal()方法,從而會激活在observer中定義的setter事件,setter會進行派發更新的操做,調用dep.notify()方法,而後便利subs調用update方法。

結合上面的描述,咱們應該在兩個地方去完成鏈接節點。首先是在調用vm._setVal()方法的時候,咱們須要保證observer中的setter事件能夠被激活,那麼咱們最好在入口函數中去聲明這個observer

function MVue (options) {
    this.$options = options;
    this._data = options.data || {};

    observer(this._data);

    new Compile(this, this.$options.el);
}

MVue.prototype = {
    _getVal: function (exp) {
        return this._data[exp];
    },

    _setVal: function (exp, newVal) {
        this._data[exp] = newVal;
    }
}
複製代碼

而後當setter事件被激活以前,咱們須要初始化完成watcher使其擁有vm、exp、patchFn,那麼最好的時機應該在獲取到patchFn這個返回函數的時候,因此應該在:

var directives = {

    _bind: function (vm, exp, patchFn) {
        new Watcher(vm,exp, patchFn);
    },

    /**
     * 連接patch方法,將指令轉化爲真實的數據並展現
     */
    _link: function (vm, node, exp, dir) {
        var patchFn = patch(vm, node, exp, dir);
        patchFn  && patchFn(node, vm._getVal(exp));

        this._bind(vm, exp, function (value) {
            patchFn  && patchFn(node, value);
        });
    },
    ...
}
複製代碼

經過_bind方法來去初始化watcher

使用與擴展

至此咱們的MVue框架就已經被開發完成了,咱們能夠點擊這裏來獲取本課程中全部的代碼,當咱們須要使用咱們的MVue的時候,咱們能夠這麼作:

<script src="./mvue3/patch.js"></script>
<script src="./mvue3/dep.js"></script>
<script src="./mvue3/directive.js"></script>
<script src="./mvue3/watcher.js"></script>
<script src="./mvue3/observer.js"></script>
<script src="./mvue3/compile.js"></script> 
<script src="./mvue3/mvue.js"></script>

<div id="app">
    <input type="text" v-model='msg'>
    <div>
        <p>{{msg}}</p>
    </div>
</div>

<script>
    var vm = new MVue({
        el: '#app',
        data: {
            msg: 'hello'
        }
    });
</script>
複製代碼

由於咱們的MVue並無進行模塊化,因此須要把全部的JS所有引入才能使用。你們也能夠嘗試一下把MVue進行模塊化,這樣就能夠只經過引入<script src="./mvue3/mvue.js"></script>來使用MVue了。

如今咱們的MVue還很是的簡單,你們能夠想一下如何爲咱們的MVue增長更多的功能,好比說更多的指令或者添加v-on:click的事件處理?這裏給你們留下三個問題,目的是但願你們可以親自寫一下這個項目,可能會讓你們有更多的理解。

一、實現v-show指令 二、實現v-on:click事件監聽 三、如何和Vue同樣能夠直接經過this.msg來獲取咱們在data中定義的數據

這三個問題的解決方案都在咱們的代碼中,你們能夠做爲參考。

這一章爲Vue.js的最後一章,從下一章開始咱們就會進入Vue周邊生態的學習,但願你們必定要親自實現一下MVue的代碼。


前端技術突飛猛進,每一種新的思想出現,都表明了一種技術的躍進、架構的變化,那麼對於目前的前端技術而言,MVVM 的思想已經能夠表明當今前端領域的前沿思想理念,Angular、React、Vue 等基於 MVVM 思想的具體實現框架,也成爲了人們爭相學習的一個熱點。而 Vue 做爲其中惟一沒有大公司支持但卻能與它們並駕齊驅而且隱隱有超越同類的趨勢,不得不說這種增加讓人感到驚奇。

本系列課程內容將會帶領你們由淺入深的學習 Vue 的基礎知識,瞭解 Vue 的源碼設計和實現原理,和你們一塊兒看一下尤雨溪先生的編程思想、架構設計以及如何進行代碼實現。本系列課程內容主要分爲三大部分:

Vue 的基礎知識:在這一部分將學習 Vue 的基礎語法及其源碼的實現。例如,Vue 的生命週期鉤子如何設計?當聲明瞭一個 directive 時,Vue 究竟執行了什麼?爲何只有經過 vue.set 函數才能爲響應式對象添加響應式屬性?若是咱們本身要實現一個響應式的框架的話,應該如何下手、如何思考等。 Vue的周邊生態:在這一部分將學習 Vue 的周邊生態圈,包括有哪些 UI 庫能夠和 Vue 配合快速構建界面、如何使用 vue-router構建前端路由、如何使用 Vuex 進行狀態管理、如何使用 Axios 進行網絡請求、如何使用 Webpack、使用 vue-cli 構建出的項目裏的各類配置有什麼意義? 項目實戰:在這一部分將會經過一個有意思的自動對話系統來進行項目實戰,爭取經過這個小項目把學到的知識點進行一個整合。

這裏寫圖片描述
相關文章
相關標籤/搜索