在前面的章節中咱們已經學習了Vue.js
的基礎內容而且瞭解了Vue.js
的源碼實現,包括:Vue的生命週期、Vue的數據響應、Vue的渲染流程等等,在這一章節咱們會和你們一塊兒去實現一個響應式的框架 -- MVue
,MVue
會遵循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.msg
的setter、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>
複製代碼
在上面的代碼中,咱們經過監聽input
的input事件
來去改變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
來去通知觀察者Watcher
,Watcher
訂閱Dep
,Dep
持有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
和{{*}}
等指令進行解析,這個解析的時機應該在 **遍歷出全部的元素以後,添加fragment
到el
以前。**咱們看一下解析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
方法中進行,這個方法首先獲取到fragment
的childNodes
,而後對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
當前值,而後咱們監聽了node
的input
事件,獲取當前用戶輸入的最新值,而後經過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
對象以後,來遍歷其中的全部數據,並分別爲它們添加上getter
和setter
方法。
// 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
對象,其中傳入的value
爲data: {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
用於保存訂閱了Dep
的watcher
實例,而後給每一個Dep
實例建立了一個id
,而後咱們爲Dep
聲明瞭三個原型方法,當調用notify
的時候,Dep
回去遍歷全部的subs
而後調用他的update()
方法,當調用depend
的時候會調用watcher
的addDep
方法使Dep
與Watcher
互相持有。其中的Dep.target
和sub
都爲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)
,使Dep
與Watcher
互相持有,相互依賴。
而後咱們看一下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
構建出的項目裏的各類配置有什麼意義? 項目實戰:在這一部分將會經過一個有意思的自動對話系統來進行項目實戰,爭取經過這個小項目把學到的知識點進行一個整合。