摘要:而使用的中方法來實現和層的關聯,他可以精確的將數據的變化映射到對應視圖上,和的復雜度沒有正向關系。在版本中會看到下面的代碼類為每個的每個屬性添加方法,并且實現當對象屬性值發生變化時,會發出一個通知。
1,為什么要用vue
大前端目前已經到一個空前的繁榮階段,各種框架類庫層出不窮,我想每選擇一個框架,肯定都能找到高度同質化的兩到三個框架。那么在目前mvvm盛行的前端架構下,我們為什么選擇了vue,而不去用react,不用angular呢?
首先他們都是非常典型的前端mvvm框架,很好的解決了業務邏輯中view和model之間的關系,用到這些框架之后,我們不會再像之前使用jQuery一樣,頁面的展示和數據有高度的耦合性,甚至不在需要選擇器了。
而vue相比于react、angular,首先他是一位我們國內的開發者開發的,有很好的API文檔、樣例等。國外的技術性文檔在翻譯的過程中對譯者還是有很高要求,否則對于大部分開發者通過簡單閱讀之后還是很難有較深的理解;
其次他有一個很好的入門起點,對于不是那么熟練node,npm,webpack和ES6的開發者來講,也可以看著文檔demo很快的入手,進入到項目開發中,而且vue組件的模板寫法對于使用過傳統模板的開發者來說更易于理解上手。
雖然react的jsx能夠允許把設計師提供的HTML原型直接黏貼過去,但依然有大量的改造工作,而這部分的改造對于開發者來說就像是需要掌握一門新的模板語言一樣,比如開發者要手動把class和for屬性替換成className和htmlFor,還要把內聯的style樣式從css語法改成JSON語法等。
在數據的雙向綁定方面,angular采用了臟檢查的方式,當有數據發生變化時,對所有的數據和視圖的綁定關系做一次檢查,當隨著ng-app根節點下的DOM變得越發復雜的時候,臟檢查的效率會變得越來越低,這就要求我們在寫業務邏輯的過程中,需要不斷的去考慮怎么解決框架所帶來的的性能瓶頸。而vue使用的ES5中Object.defineProperty()方法來實現model和view層的關聯,他可以精確的將數據的變化映射到對應視圖上,和DOM的復雜度沒有正向關系。(當然vue在這里的劣勢就是不能夠在不支持ES5的瀏覽器上使用)
2,vue的數據觀察者模式,先從源碼開始
var data = { a: 1 }; var vm = new Vue({ data: data }) vm.$watch("a", function() { console.log("the data a value changed"); }); vm.a = 2;
這個實例中,當每次改變了data中a屬性的值,都會輸出 the data a value changeed,我們看看這個過程中到底發生了什么。
在Vue 2.2.4版本中會看到下面的代碼:
/** * Observer class that are attached to each observed * object. Once attached, the observer converts target * object"s property keys into getter/setters that * collect dependencies and dispatches updates. */ var Observer = function Observer (value) { this.value = value; this.dep = new Dep(); this.vmCount = 0; def(value, "__ob__", this); if (Array.isArray(value)) { var augment = hasProto ? protoAugment : copyAugment; augment(value, arrayMethods, arrayKeys); this.observeArray(value); } else { this.walk(value); } };
Observer類為每個object的每個屬性添加getter、setter方法,并且實現當對象屬性值發生變化時,會發出一個通知。
def( value, ‘__ob__’, this );
def方法是通過ES5的Object.defineProperty()來注冊對象,
/** * Define a property. */ function def (obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }); }
this.observeArray 和 this.walk 方法中通過遞歸實現了對object的深度遍歷并且對每個屬性綁定setter、getter方法
/** * Walk through each property and convert them into * getter/setters. This method should only be called when * value type is Object. */ Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { defineReactive$$1(obj, keys[i], obj[keys[i]]); } }; /** * Observe a list of Array items. */ Observer.prototype.observeArray = function observeArray (items) { for (var i = 0, l = items.length; i < l; i++) { observe(items[i]); } }; /** * Attempt to create an observer instance for a value, * returns the new observer if successfully observed, * or the existing observer if the value already has one. */ function observe (value, asRootData) { if (!isObject(value)) { return } var ob; if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) { ob = value.__ob__; } else if ( observerState.shouldConvert && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { ob = new Observer(value); } if (asRootData && ob) { ob.vmCount++; } return ob } /** * Define a reactive property on an Object. */ function defineReactive$$1 ( obj, key, val, customSetter ) { var dep = new Dep(); var property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return } // cater for pre-defined getter/setters var getter = property && property.get; var setter = property && property.set; var childOb = observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); } if (Array.isArray(value)) { dependArray(value); } } return value }, set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if ("development" !== "production" && customSetter) { customSetter(); } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = observe(newVal); dep.notify(); } }); }
注意看set里面的dep.notify方法,這個就是每次對象屬性值發生改變的時候,發出一個通知,看看notify里面都干了哪些事情:
/** * A dep is an observable that can have multiple * directives subscribing to it. */ var Dep = function Dep () { this.id = uid$1++; this.subs = []; }; Dep.prototype.addSub = function addSub (sub) { this.subs.push(sub); }; Dep.prototype.removeSub = function removeSub (sub) { remove(this.subs, sub); }; Dep.prototype.depend = function depend () { if (Dep.target) { Dep.target.addDep(this); } }; Dep.prototype.notify = function notify () { // stabilize the subscriber list first var subs = this.subs.slice(); for (var i = 0, l = subs.length; i < l; i++) { subs[i].update(); } };
notify是Dep中的一個方法,Dep主要實現了一個消息列表的管理,每一條消息會通過addSub方法push進來,當觸發notify時,會把所有消息update一次(在update中會做diff判斷,沒有發生改變的狀態,不會被做邏輯處理)
接下來看看這個通知是怎么被接收到的,類庫里面有一個Watcher類專門處理被接受到的消息,Watcher的構造函數如下:
/** * A watcher parses an expression, collects dependencies, * and fires callback when the expression value changes. * This is used for both the $watch() api and directives. */ var Watcher = function Watcher ( vm, expOrFn, cb, options ) { this.vm = vm; vm._watchers.push(this); // options if (options) { this.deep = !!options.deep; this.user = !!options.user; this.lazy = !!options.lazy; this.sync = !!options.sync; } else { this.deep = this.user = this.lazy = this.sync = false; } this.cb = cb; this.id = ++uid$2; // uid for batching this.active = true; this.dirty = this.lazy; // for lazy watchers this.deps = []; this.newDeps = []; this.depIds = new _Set(); this.newDepIds = new _Set(); this.expression = expOrFn.toString(); // parse expression for getter if (typeof expOrFn === "function") { this.getter = expOrFn; } else { this.getter = parsePath(expOrFn); if (!this.getter) { this.getter = function () {}; "development" !== "production" && warn( "Failed watching path: "" + expOrFn + "" " + "Watcher only accepts simple dot-delimited paths. " + "For full control, use a function instead.", vm ); } } this.value = this.lazy ? undefined : this.get(); }; /** * Scheduler job interface. * Will be called by the scheduler. */ Watcher.prototype.run = function run () { if (this.active) { var value = this.get(); if ( value !== this.value || // Deep watchers and watchers on Object/Arrays should fire even // when the value is the same, because the value may // have mutated. isObject(value) || this.deep ) { // set new value var oldValue = this.value; this.value = value; if (this.user) { try { this.cb.call(this.vm, value, oldValue); } catch (e) { handleError(e, this.vm, ("callback for watcher "" + (this.expression) + """)); } } else { this.cb.call(this.vm, value, oldValue); } } } };
當object屬性的值發生變化時,會觸發watcher通過構造函數傳入的callback方法,最終實現對數據變化的監聽。
3,項目中的vue
在傳統的團隊協作開發中,通常會按照頁面的粒度來分工合作,團隊每個成員負責一個頁面或者多各頁面,基本上細化到一個頁面的邏輯至少由一位成員來負責完成;
Vue按照組件化的方式開發,能夠把粒度化拆解的更細:
頁面中可以按照功能拆接出若干個模塊,其中每個模塊的邏輯都相對獨立。當前頁面的所有數據邏輯在一個model里面管理, 以map-phono-cp為例:
var mapPhotoCp = Vue.extend({ extends: baseCp, template:template, created:function(){ }, methods:{ onOrderDetailReady:function(data){ }, initMapLink:function(){ }, statusInArray:function(status,array){ }, tap:function(){ } }, data:function(){ } }); Vue.component("map-photo-cp", mapPhotoCp);
實例化出來一個組件,通過Vue.component來注冊成Vue的全局組件,這樣在初始化Vue的時候,會將頁面DOM中對應的所有組件解析。
每一個組件都可以直接操作頁面的model,當model發生改變,vue會將數據的變化直接映射到view上。這樣每個組件的開發者都會有一個更清晰的開發思路,不會再有復雜的DOM操作,不會再有各種獲取DOM節點綁定事件的行為,讓開發變得更順暢起來。
最后也來展望一下vue的未來,vue作為mvvm的后起之秀,有著非常高的關注度,這不光是前面提到的一些特點,主要也繼承了之前框架的大部分優點,比如在vue2.0中也支持了virtual DOM,使DOM的操作有更高的效率,并且支持SSR(server side render),對有首屏渲染加速有更好的支持。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82054.html
摘要:業務背景是,在的前端項目中加入作為組件。但隨著需要登錄的頁面的增多,多個頁面都需要添加相同的,,以及前端登錄邏輯,所以在原先的項目中添加了,將重復的添加的代碼加入到了文件中,然后通過方法將對象掛載到某個的節點上。 業務背景是,在jq的前端項目中加入Vue作為組件。 原本的登錄功能是每個頁面加一個登錄彈窗(手機號+驗證碼驗證登錄),然后發ajax請求到后端,登錄成功后再進行一些操作。 但...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:另外,單頁應用因為數據前置到了前端,不利于搜索引擎的抓取。所以我們需要對自己的單頁應用進行一些優化。 前言 最近秋招之余空出時間來按自己的興趣動手做了一個項目,一個基于vue-cli3.0, vue,typescript的移動端pwa,現在趁熱打鐵,將這個項目從開發到部署整個過程記錄下來,并將從這個項目中學習到的東西分享出來,如果大家有什么意見或補充也可以在評論區提出。先介紹一下這個項...
摘要:為了便于您更清晰的理解的體系架構,在這里我將為您展示年開發者知識圖譜,它包含了所有開發過程中的關鍵部分。在數據展示前端導入導出圖表面板數據綁定等場景無需大量代碼開發和測試,可極大節省企業研發成本并降低交付風險。 作為 Vue 的初學者,您或許已經聽過很多關于它的專業術語了,例如:單頁面應用程序、異步組件、服務器端呈現等,您可能還聽過和Vue經常一起被提到的工具和庫,如Vuex、Webp...
閱讀 3422·2023-04-25 22:44
閱讀 926·2021-11-15 11:37
閱讀 1632·2019-08-30 15:55
閱讀 2639·2019-08-30 15:54
閱讀 1080·2019-08-30 13:45
閱讀 1430·2019-08-29 17:14
閱讀 1853·2019-08-29 13:50
閱讀 3402·2019-08-26 11:39