摘要:而使用的中方法來(lái)實(shí)現(xiàn)和層的關(guān)聯(lián),他可以精確的將數(shù)據(jù)的變化映射到對(duì)應(yīng)視圖上,和的復(fù)雜度沒(méi)有正向關(guān)系。在版本中會(huì)看到下面的代碼類為每個(gè)的每個(gè)屬性添加方法,并且實(shí)現(xiàn)當(dāng)對(duì)象屬性值發(fā)生變化時(shí),會(huì)發(fā)出一個(gè)通知。
1,為什么要用vue
大前端目前已經(jīng)到一個(gè)空前的繁榮階段,各種框架類庫(kù)層出不窮,我想每選擇一個(gè)框架,肯定都能找到高度同質(zhì)化的兩到三個(gè)框架。那么在目前mvvm盛行的前端架構(gòu)下,我們?yōu)槭裁催x擇了vue,而不去用react,不用angular呢?
首先他們都是非常典型的前端mvvm框架,很好的解決了業(yè)務(wù)邏輯中view和model之間的關(guān)系,用到這些框架之后,我們不會(huì)再像之前使用jQuery一樣,頁(yè)面的展示和數(shù)據(jù)有高度的耦合性,甚至不在需要選擇器了。
而vue相比于react、angular,首先他是一位我們國(guó)內(nèi)的開(kāi)發(fā)者開(kāi)發(fā)的,有很好的API文檔、樣例等。國(guó)外的技術(shù)性文檔在翻譯的過(guò)程中對(duì)譯者還是有很高要求,否則對(duì)于大部分開(kāi)發(fā)者通過(guò)簡(jiǎn)單閱讀之后還是很難有較深的理解;
其次他有一個(gè)很好的入門(mén)起點(diǎn),對(duì)于不是那么熟練node,npm,webpack和ES6的開(kāi)發(fā)者來(lái)講,也可以看著文檔demo很快的入手,進(jìn)入到項(xiàng)目開(kāi)發(fā)中,而且vue組件的模板寫(xiě)法對(duì)于使用過(guò)傳統(tǒng)模板的開(kāi)發(fā)者來(lái)說(shuō)更易于理解上手。
雖然react的jsx能夠允許把設(shè)計(jì)師提供的HTML原型直接黏貼過(guò)去,但依然有大量的改造工作,而這部分的改造對(duì)于開(kāi)發(fā)者來(lái)說(shuō)就像是需要掌握一門(mén)新的模板語(yǔ)言一樣,比如開(kāi)發(fā)者要手動(dòng)把class和for屬性替換成className和htmlFor,還要把內(nèi)聯(lián)的style樣式從css語(yǔ)法改成JSON語(yǔ)法等。
在數(shù)據(jù)的雙向綁定方面,angular采用了臟檢查的方式,當(dāng)有數(shù)據(jù)發(fā)生變化時(shí),對(duì)所有的數(shù)據(jù)和視圖的綁定關(guān)系做一次檢查,當(dāng)隨著ng-app根節(jié)點(diǎn)下的DOM變得越發(fā)復(fù)雜的時(shí)候,臟檢查的效率會(huì)變得越來(lái)越低,這就要求我們?cè)趯?xiě)業(yè)務(wù)邏輯的過(guò)程中,需要不斷的去考慮怎么解決框架所帶來(lái)的的性能瓶頸。而vue使用的ES5中Object.defineProperty()方法來(lái)實(shí)現(xiàn)model和view層的關(guān)聯(lián),他可以精確的將數(shù)據(jù)的變化映射到對(duì)應(yīng)視圖上,和DOM的復(fù)雜度沒(méi)有正向關(guān)系。(當(dāng)然vue在這里的劣勢(shì)就是不能夠在不支持ES5的瀏覽器上使用)
2,vue的數(shù)據(jù)觀察者模式,先從源碼開(kāi)始
var data = { a: 1 }; var vm = new Vue({ data: data }) vm.$watch("a", function() { console.log("the data a value changed"); }); vm.a = 2;
這個(gè)實(shí)例中,當(dāng)每次改變了data中a屬性的值,都會(huì)輸出 the data a value changeed,我們看看這個(gè)過(guò)程中到底發(fā)生了什么。
在Vue 2.2.4版本中會(huì)看到下面的代碼:
/** * 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類為每個(gè)object的每個(gè)屬性添加getter、setter方法,并且實(shí)現(xiàn)當(dāng)對(duì)象屬性值發(fā)生變化時(shí),會(huì)發(fā)出一個(gè)通知。
def( value, ‘__ob__’, this );
def方法是通過(guò)ES5的Object.defineProperty()來(lái)注冊(cè)對(duì)象,
/** * 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 方法中通過(guò)遞歸實(shí)現(xiàn)了對(duì)object的深度遍歷并且對(duì)每個(gè)屬性綁定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方法,這個(gè)就是每次對(duì)象屬性值發(fā)生改變的時(shí)候,發(fā)出一個(gè)通知,看看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中的一個(gè)方法,Dep主要實(shí)現(xiàn)了一個(gè)消息列表的管理,每一條消息會(huì)通過(guò)addSub方法push進(jìn)來(lái),當(dāng)觸發(fā)notify時(shí),會(huì)把所有消息update一次(在update中會(huì)做diff判斷,沒(méi)有發(fā)生改變的狀態(tài),不會(huì)被做邏輯處理)
接下來(lái)看看這個(gè)通知是怎么被接收到的,類庫(kù)里面有一個(gè)Watcher類專門(mén)處理被接受到的消息,Watcher的構(gòu)造函數(shù)如下:
/** * 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); } } } };
當(dāng)object屬性的值發(fā)生變化時(shí),會(huì)觸發(fā)watcher通過(guò)構(gòu)造函數(shù)傳入的callback方法,最終實(shí)現(xiàn)對(duì)數(shù)據(jù)變化的監(jiān)聽(tīng)。
3,項(xiàng)目中的vue
在傳統(tǒng)的團(tuán)隊(duì)協(xié)作開(kāi)發(fā)中,通常會(huì)按照頁(yè)面的粒度來(lái)分工合作,團(tuán)隊(duì)每個(gè)成員負(fù)責(zé)一個(gè)頁(yè)面或者多各頁(yè)面,基本上細(xì)化到一個(gè)頁(yè)面的邏輯至少由一位成員來(lái)負(fù)責(zé)完成;
Vue按照組件化的方式開(kāi)發(fā),能夠把粒度化拆解的更細(xì):
頁(yè)面中可以按照功能拆接出若干個(gè)模塊,其中每個(gè)模塊的邏輯都相對(duì)獨(dú)立。當(dāng)前頁(yè)面的所有數(shù)據(jù)邏輯在一個(gè)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);
實(shí)例化出來(lái)一個(gè)組件,通過(guò)Vue.component來(lái)注冊(cè)成Vue的全局組件,這樣在初始化Vue的時(shí)候,會(huì)將頁(yè)面DOM中對(duì)應(yīng)的所有組件解析。
每一個(gè)組件都可以直接操作頁(yè)面的model,當(dāng)model發(fā)生改變,vue會(huì)將數(shù)據(jù)的變化直接映射到view上。這樣每個(gè)組件的開(kāi)發(fā)者都會(huì)有一個(gè)更清晰的開(kāi)發(fā)思路,不會(huì)再有復(fù)雜的DOM操作,不會(huì)再有各種獲取DOM節(jié)點(diǎn)綁定事件的行為,讓開(kāi)發(fā)變得更順暢起來(lái)。
最后也來(lái)展望一下vue的未來(lái),vue作為mvvm的后起之秀,有著非常高的關(guān)注度,這不光是前面提到的一些特點(diǎn),主要也繼承了之前框架的大部分優(yōu)點(diǎn),比如在vue2.0中也支持了virtual DOM,使DOM的操作有更高的效率,并且支持SSR(server side render),對(duì)有首屏渲染加速有更好的支持。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/82054.html
摘要:業(yè)務(wù)背景是,在的前端項(xiàng)目中加入作為組件。但隨著需要登錄的頁(yè)面的增多,多個(gè)頁(yè)面都需要添加相同的,,以及前端登錄邏輯,所以在原先的項(xiàng)目中添加了,將重復(fù)的添加的代碼加入到了文件中,然后通過(guò)方法將對(duì)象掛載到某個(gè)的節(jié)點(diǎn)上。 業(yè)務(wù)背景是,在jq的前端項(xiàng)目中加入Vue作為組件。 原本的登錄功能是每個(gè)頁(yè)面加一個(gè)登錄彈窗(手機(jī)號(hào)+驗(yàn)證碼驗(yàn)證登錄),然后發(fā)ajax請(qǐng)求到后端,登錄成功后再進(jìn)行一些操作。 但...
摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥(niǎo)雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門(mén),久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥(niǎo)雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門(mén),久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...
摘要:另外,單頁(yè)應(yīng)用因?yàn)閿?shù)據(jù)前置到了前端,不利于搜索引擎的抓取。所以我們需要對(duì)自己的單頁(yè)應(yīng)用進(jìn)行一些優(yōu)化。 前言 最近秋招之余空出時(shí)間來(lái)按自己的興趣動(dòng)手做了一個(gè)項(xiàng)目,一個(gè)基于vue-cli3.0, vue,typescript的移動(dòng)端pwa,現(xiàn)在趁熱打鐵,將這個(gè)項(xiàng)目從開(kāi)發(fā)到部署整個(gè)過(guò)程記錄下來(lái),并將從這個(gè)項(xiàng)目中學(xué)習(xí)到的東西分享出來(lái),如果大家有什么意見(jiàn)或補(bǔ)充也可以在評(píng)論區(qū)提出。先介紹一下這個(gè)項(xiàng)...
摘要:為了便于您更清晰的理解的體系架構(gòu),在這里我將為您展示年開(kāi)發(fā)者知識(shí)圖譜,它包含了所有開(kāi)發(fā)過(guò)程中的關(guān)鍵部分。在數(shù)據(jù)展示前端導(dǎo)入導(dǎo)出圖表面板數(shù)據(jù)綁定等場(chǎng)景無(wú)需大量代碼開(kāi)發(fā)和測(cè)試,可極大節(jié)省企業(yè)研發(fā)成本并降低交付風(fēng)險(xiǎn)。 作為 Vue 的初學(xué)者,您或許已經(jīng)聽(tīng)過(guò)很多關(guān)于它的專業(yè)術(shù)語(yǔ)了,例如:?jiǎn)雾?yè)面應(yīng)用程序、異步組件、服務(wù)器端呈現(xiàn)等,您可能還聽(tīng)過(guò)和Vue經(jīng)常一起被提到的工具和庫(kù),如Vuex、Webp...
閱讀 3436·2023-04-25 22:44
閱讀 950·2021-11-15 11:37
閱讀 1644·2019-08-30 15:55
閱讀 2658·2019-08-30 15:54
閱讀 1096·2019-08-30 13:45
閱讀 1444·2019-08-29 17:14
閱讀 1866·2019-08-29 13:50
閱讀 3424·2019-08-26 11:39