Angular 和 Vue
在對Angular的學習中,了解到AngularJS 的兩個主要缺點:
- 對於每一次界面時間,Ajax 或者 timeout,都會進行一個臟檢查,而每一次臟檢查又會在內部循環檢查,當界面綁定的model 很多,就會造成嚴重的性能問題 。
- Angular 混亂的模塊,並不能起到命名空間的作用,因而在大項目中組織模塊又是一件頭疼的事。
但是 Angular 開發中小型的應用是很棒的,也是將 MVC 引入到前端的開始。定一個目標,兩年時間理解 《Build Your Own AngularJS》。
Vue 拋棄了Angular 中臟檢查的方式,而使用 Javascript 中變量的setter 屬性來截獲數據變化,更加巧妙和機智。
在 MVVM 時代,瀏覽器自身屬性變得更加重要起來。getter 和 setter 曾被認為是一個無太大用處的一個屬性。
為了較為深入的理解 Vue, 自己擼了一個簡易版 MVVM 。 先看效果吧 。
<input type="text" v-model="hello" value="">
<div id="template">
{{hello}}
</div>
<div class="template">
{{bind}}
</div>
<script src="app.js"></script>
var vue = new Vue({
ele: '#template',
});
var vue1 = new Vue({
ele: '.template',
data: {
bind: 'hey i am a vue lover'
}
})
開擼開擼
若有關於 getter
和 setter
問題 ,請先移步 MDN 。
申明Vue 對象
function Vue(option) {
this.init(option);
}
初始化
Vue.prototype.init = function(option) {
this._data = option.data || {};
this._method = option.method || {};
this.bingdings = {};
this.elements = typeof option.ele === "string" ? document.querySelectorAll(option.ele) : option.ele;
this.bind();
this.observe();
this.react();
this.initMvvM();
}
將界面上的模型數據綁定到后台
Vue.prototype.bind = function() {
for (var i = 0, length = this.elements.length; i < length; i++) {
var ele = this.elements[i],
html = ele.innerHTML,
spans, span, dataAttr;
html = html.replace(/\{\{(.*?)\}\}/g, function(a, b) {
var span = '<span v-data="' + b + '"></span>';
return span;
})
ele.innerHTML = html;
spans = ele.querySelectorAll('[v-data]');
for (var j = 0, l = spans.length; j < l; j++) {
span = spans[j];
dataAttr = span.getAttribute('v-data');
if (!this.bingdings[dataAttr]) {
this.bingdings[dataAttr] = { value: this._data[dataAttr] || '', ele: [] };
}
this.bingdings[dataAttr].ele.push(span);
span.innerHTML = this.bingdings[dataAttr].value;
span.removeAttribute('v-data');
}
}
}
// 核心部分,使用 setter 觀測 _data
Vue.prototype.observe = function() {
var self = this,
eles;
for (var key in self.bingdings) {
Object.defineProperty(self._data, key, {
get: function() {
return self.bingdings[key].value;
},
set: function(newVal) {
if (newVal !== self.bingdings[key].value) {
self.bingdings[key].value = newVal;
eles = self.bingdings[key].ele;
for (var i = 0, l = eles.length; i < l; i++) {
eles[i].innerHTML = newVal;
}
}
}
})
}
}
到這里,_data_
的賦值已經可以引起界面上dom 的變化。也就是說完成了 model 到view 的數據綁定和響應。
現在來完成 dom 到 view 的事件響應。
Vue.prototype.initMvvM = function() {
var self = this;
var models = document.querySelectorAll('[v-model]'),
model, dataModel;
for (var i = 0, l = models.length; i < l; i++) {
model = models[i];
dataModel = model.getAttribute('v-model');
self._data[dataModel] = model.value;
}
}
// 事件響應
Vue.prototype.react = function() {
var self = this;
var models = document.querySelectorAll('[v-model]'),
model, dataModel;
for (var i = 0, l = models.length; i < l; i++) {
model = models[i];
model.addEventListener('change', function(e) {
dataModel = this.getAttribute('v-model');
self._data[dataModel] = this.value;
})
model.addEventListener('keyup', function(e) {
dataModel = this.getAttribute('v-model');
self._data[dataModel] = this.value;
})
}
}
最后,更新init 函數。
Vue.prototype.init = function(option) {
this._data = option.data || {};
this._method = option.method;
this.bingdings = {};
this.elements = typeof option.ele === "string" ? document.querySelectorAll(option.ele) : option.ele;
this.bind();
this.observe();
this.react();
this.initMvvM();
}
總結
大功告成。
但是相比與真正的Vue還是差了很多,比如 這里的數據綁定僅支持基本類型,函數綁定也沒有完成。在Vue 中,每一個監控的屬性都會設置依賴,從而陷入避免和 Angular 中一樣的循環檢查。
另外,在Vue中,數據模版並不是這樣使用插入一個span,而是創建一個 文本節點,然后append 到當前父元素上。
這只是一個簡易版的,把所有的部分都放在Vue上,非常不合適。 后面會進行重構,把數據監控使用訂閱和發布模式封裝。
在這之前,需要進行模塊化和依賴注入,所以,接下來要完成一個簡易的requirejs,會提供三個api ,define, require 和 use。
代碼托管到了 github(但是今天的網速不好,改天補充.),
如有錯誤,希望不吝賜教。
如果您有合適的前端工資機會,也期待您的郵件。