Vuejs 響應式系統(tǒng)

yugasun

2018.02.06 10:38*字數(shù) 2082閱讀 364評論 0喜歡 1

you-may-not-know-vuejs.png

byyugasunfromhttps://yugasun.com/post/you-may-not-know-vuejs-2.html

本文可全文轉(zhuǎn)載,但需要保留原作者和出處。

雖然說是Vuejs實踐,但是有些重要的理論還是必不可少的,本文將簡單的帶你了解Vuejs的響應式原理。

Vue 最獨特的特性之一,是其非侵入性的響應式系統(tǒng)。數(shù)據(jù)模型僅僅是普通的 Javascript 對象。而當你修改它們時,視圖會進行更新。這使得狀態(tài)管理非常簡單直接,不過理解其工作原理同樣非常重要,這樣你可以回避一些常見的問題。

原理圖剖析

vue-reactive-data

仔細閱讀這張官方原理圖,大概可以剖析為以下幾個步驟:

編譯組件:對特殊標記的部分(比如雙大括號部分)進行替換為相應的數(shù)據(jù)值。

收集依賴:對于編譯階段依賴的數(shù)據(jù)進行監(jiān)聽(這個都是通過watcher對象實現(xiàn)的)

通知更新:當步驟2中監(jiān)聽的數(shù)據(jù)發(fā)生變化時,會通知watcher進行重新計算,觸發(fā)關(guān)聯(lián)視圖更新。

可以簡單理解為一個發(fā)布訂閱系統(tǒng),當然這里的過程介紹比較通俗,單純是為了理解而解釋的,實際流程其實還是很復雜的。如果想結(jié)合源碼深入了解的,建議去閱讀這篇文章:

Vue 源碼解析:深入響應式原理

關(guān)于Vuejs模板

上一篇中提到,Vue實例在初始化的時候會將目標節(jié)點div#app內(nèi)容進行替換,是在定義了template屬性或者render函數(shù)的前提下。否則會對把div#app內(nèi)容當做模板進行編譯輸出。一般情況下,很少使用template屬性來定義模板,因為實際開發(fā)過程中,我們的模板還是很復雜的,單純通過template屬性定義,我們的代碼會顯得非常臃腫,不便于閱讀。所以,我們通常使用的是將模板內(nèi)容寫在過載目標元素的內(nèi)部,稍后將采用這種方法做代碼演示。

當然對于復雜項目,更多的是使用單文件組件(這將在以后專題文章中介紹)。

百聞不如一見

還記得上一篇中講述的如何實現(xiàn)一個簡單的 Vue 應用,并通過不同的 API

實現(xiàn)了目標元素的渲染吧。不記得也不要緊,這里還是從最基本的代碼開始。下面我們會動態(tài)的實現(xiàn)一個列表的渲染,并通過修改數(shù)據(jù),來觸發(fā)視圖的更新,以此來感受下響應式系統(tǒng)的快感。先來看一段代碼:

Demo1:data

添加

{{ item.name }}

varapp =newVue({el:"#app",? data () {return{count:1,list: [? ? ? ? {name:'Vuejs官網(wǎng)',url:'https://cn.vuejs.org'},? ? ? ? {name:'Github',url:'https://github.com'},? ? ? ? {name:'Yuga博客1',url:'https://yugasun.com'}? ? ? ]? ? }? },methods: {? ? addItem () {this.count++this.list.push({name:'Yuga博客'+this.count,url:'https://yugasun.com'})? ? }? }})

上面 Vue 實例在初始化的時候,屬性data中定義了一個站點列表,然后模板中通過v-for指令進行列表渲染,同時也使用了v-bind指令來進行屬性綁定,并且通過v-on指令來監(jiān)聽按鈕的click事件來執(zhí)行addItem方法。當點擊添加按鈕,就會向listpush一條數(shù)據(jù),視圖會再次更新。(關(guān)于指令相關(guān)文章將在下一篇中講到,感興趣的同學可以先到官網(wǎng)學習了解。)

Demo2:computed

在實際開發(fā)過程中,我們的接口請求到的數(shù)據(jù)往往會差強人意,這時就需要我們進行一些轉(zhuǎn)化,來生成我們想要的數(shù)據(jù)結(jié)構(gòu),當然最直接的方式就是每次請求完數(shù)據(jù)就通過固定函數(shù)處理一遍,但是這個得手動執(zhí)行。此時computed計算屬性就可以用上了。

我們知道除了data中定義的數(shù)據(jù)可以再模板中使用外,computed屬性也可以。只不過computed中的屬性是需要先進行計算,然后再返回想要的數(shù)據(jù)的。當我們輸出某個屬性,必須依賴另外一個data中的屬性來動態(tài)計算獲得的,此時使用computed就非常簡單了。代碼如下:

varapp =newVue({el:"#app",? data () {return{count:1,// 實際開發(fā)中往往是通過接口請求獲取requestList: ['Vuejs官網(wǎng)-https://cn.vuejs.org','Github-https://github.com','Yuga博客1-https://yugasun.com']? ? }? },computed: {list:function(){varlist = [];this.requestList.map(function(item, index){vartempArr = item.split('-');? ? ? ? list.push({name: tempArr[0],url: tempArr[1]? ? ? ? });? ? ? })returnlist;? ? }? },methods: {? ? addItem () {this.count++this.requestList.push('Yuga博客'+this.count +'-https://yugasun.com')? ? }? }})

關(guān)于計算屬性 - computed

關(guān)于計算屬性,其實有個細節(jié)是很多同學沒有注意到的,計算屬性實際上是可以修改的!那么如何才能修改呢?

其實計算屬性不僅可以定義為一個函數(shù),也可以定義為一個含有g(shù)et/set屬性的對象。當我們定義為一個函數(shù)是,Vue 內(nèi)部會默認將這個函數(shù)賦值給get屬性,一般set是未定義的。當我們定義set屬性后,就可以對它進行修改了。來看下面一段代碼:

改變姓名

{{ username }}

varapp =newVue({el:"#app",? data () {return{firstName:'Yuga',lastName:'Sun'}? },computed: {username: {get:function(){returnthis.firstName +' '+this.lastName;? ? ? },set:function(newVal){varnames = newVal.split(' ');this.firstName = names[0];this.lastName = names[1];? ? ? }? ? }? },methods: {? ? changeName () {if(this.username ==='Yuga Sun') {this.username ='Summer Wu';? ? ? }else{this.username ='Yuga Sun';? ? ? }? ? }? }})

當進行賦值操作this.username = 'Summer Wu'時,計算屬性username的set函數(shù)就會被調(diào)用,同時也對firstName和lastName進行了相應的更新。這里看似是直接進行賦值操作,其實也是通過間接修改firstName和lastName來實現(xiàn)username的更新的。因為計算屬性是基于它依賴的值進行緩存的,如果它依賴的值沒有發(fā)生改變,它自己就沒法發(fā)生改變。

關(guān)于偵聽器 - watch

創(chuàng)建 Vue 應用時,我們還提到過watch這個屬性,它其實是個對象,鍵是需要觀察的表達式,值是對應的回調(diào)函數(shù)。值也可以是方法名,或者包含選項的對象。和上面的計算屬性類似,他可以監(jiān)聽值/表達式的變化來執(zhí)行回調(diào)函數(shù)。我們先實現(xiàn)上面的功能:

varapp =newVue({el:"#app",? data () {return{firstName:'Yuga',lastName:'Sun',username:'Yuga Sun'}? },watch: {firstName:function(val, oldVal){this.username = val +' '+this.lastName;? ? },lastName:function(val, oldVal){this.username =this.firstName +' '+ val;? ? }? },methods: {? ? changeName () {if(this.username ==='Yuga Sun') {this.firstName ='Summer';this.lastName ='Wu';? ? ? }else{this.firstName ='Yuga';this.lastName ='Sun';? ? ? }? ? }? }})

以上就是最基礎(chǔ)用法,往往有些時候我們的監(jiān)聽屬性并沒有那么簡單。往往是一個對象,這時當我們修改該對象的屬性時,如何實現(xiàn)監(jiān)聽呢?先看下面代碼:

改變姓名

{{ username }}

varapp =newVue({el:"#app",? data () {return{userinfo: {firstName:'Yuga',lastName:'Sun',? ? ? },username:'Yuga Sun'}? },watch: {userinfo:function(val, oldVal){this.username = val.firstName +' '+ val.lastName;? ? }? },methods: {? ? changeName () {if(this.username ==='Yuga Sun') {this.userinfo.firstName ='Summer'this.userinfo.lastName ='Wu'}else{this.userinfo.firstName ='Yuga'this.userinfo.lastName ='Sun'}? ? }? }})

此時無論我們?nèi)绾吸c擊按鈕,都無法改變username的值,因為watch偵聽器默認只是偵聽該對象本身的賦值操作,也就是直接對this.userinfo進行賦值操作時的變化,并未對其內(nèi)部屬性進行偵聽。實際上對于偵聽的值是可以為一個對象的,它還有個deep屬性,用來設(shè)置是否偵聽內(nèi)部屬性的變化,而回調(diào)函數(shù)是通過handler來設(shè)置的。我們再次修改代碼如下:

varapp =newVue({el:"#app",? data () {return{userinfo: {firstName:'Yuga',lastName:'Sun',? ? ? },username:'Yuga Sun'}? },watch: {userinfo: {deep:true,handler:function(val, oldVal){this.username = val.firstName +' '+ val.lastName;? ? ? }? ? }? },methods: {? ? changeName () {if(this.username ==='Yuga Sun') {this.userinfo.firstName ='Summer'this.userinfo.lastName ='Wu'}else{this.userinfo.firstName ='Yuga'this.userinfo.lastName ='Sun'}? ? }? }})

點擊按鈕,你會發(fā)現(xiàn)username可以根據(jù)按鈕點擊更新了,這個屬性在實際項目中非常實用,因為往往我們修改數(shù)據(jù)時,并不是整體賦值,大部分時候都是局部修改屬性的,所以這個時候就需要通過設(shè)置deep屬性為true,來達到我們的偵聽目的。

問題來了,當偵聽對象包含很多屬性,而我們只是需要監(jiān)聽其中的一個或某幾個屬性,這時如果我們通過這種方式偵聽所有內(nèi)部屬性的變化,自然就會造成內(nèi)存的浪費。那么能不能只偵聽單一內(nèi)部屬性的變化呢?答案是肯定的。

其實在watch定義的時候,鍵是可以為需要觀察的表達式的,表達式就是說明,我們是可以寫成對象屬性訪問表達式的。比如我們只需要偵聽姓氏的修改,我們可以寫成userinfo.lastName。再來看下面代碼:

varapp =newVue({el:"#app",? data () {return{userinfo: {firstName:'Yuga',lastName:'Sun',? ? ? },username:'Yuga Sun'}? },watch: {'userinfo.lastName':function(val, oldVal){this.username =this.userinfo.firstName +' '+ val;? ? }? },methods: {? ? changeName () {if(this.username ==='Yuga Sun') {this.userinfo.lastName ='Wu'}else{this.userinfo.lastName ='Sun'}? ? }? }})

這樣我們就可以根據(jù)項目實際情況,靈活的使用偵聽器來偵聽我們所關(guān)注的屬性了,趕緊動手試一試吧~

源碼在此

專題目錄

You-May-Not-Know-Vuejs

真誠贊賞,手留余香~

你也許不知道的Vuejs

? ? ? ? ? ? 著作權(quán)歸作者所有


舉報文章

yugasun

寫了 24514 字,被 12 人關(guān)注,獲得了 26 個喜歡

熱愛前端技術(shù),喜歡鉆研各種前端新技術(shù),歡迎各位前端朋友來撩~




? ? ? ? 更多分享

被以下專題收入,發(fā)現(xiàn)更多相似內(nèi)容

收入我的專題

你也許不知道的...

MVVM框架的搭建(二)——項目搭建

介紹完背景以及初衷之后,我們開始搭建MVVM的框架,這一部分我們進行簡單的搭建,了解MVVM架構(gòu)的基本結(jié)構(gòu)。

MVVM框架的搭建(一)——背景MVVM框架的搭建(二)——項目搭建MVVM框架的搭建(三)——網(wǎng)絡(luò)請求MVVM的數(shù)據(jù)持久化(一)——ROOM的集成MVVM的數(shù)據(jù)持...

YangZC

預計2019年發(fā)布的Vue3.0到底有什么不一樣的地方?

摘要: Vue 3.0預覽。

原文:預計今年發(fā)布的Vue3.0到底有什么不一樣的地方? 作者:小肆 微信公眾號:技術(shù)放肆聊 Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。

還有幾個月距離 vue2 的首次發(fā)布就滿 3 年了,而 vue 的作者尤雨溪也在去年年末發(fā)布了關(guān)于 v...

Fundebug

vue 中遞歸組件的用法

概念:

組件是可以在自己的模板中調(diào)用自身的,不過他們只能通過name選項來做這件事。 使用vue框架,我們會發(fā)現(xiàn)export default

導出的對象中有一個name屬性,這個name屬性是一個比較重要的屬性,而且屬性好處不止這一處。 用法: 1.首先我們要知道,既然是遞...

廊橋夢醉

10個JavaScript難點

譯者按:能夠讀懂這篇博客的JavaScript開發(fā)者,運氣不會太差...

原文: 10 JavaScript concepts every Node.js programmer must master 譯者:

Fundebug 為了保證可讀性,本文采用意譯而非直譯,并且對示...

Fundebug

vue項目總結(jié)(2)vue-router(為做登錄攔截準備)

我們開始就采用了前后端分離的開發(fā)模式;話說vue不兼容IE8;不扯太多;總結(jié)一下前端在control層面做的控制;也就是路由的跳轉(zhuǎn)部分;這樣才能再深一步的開發(fā)

了解一下前端路由 在說到登錄攔截的時候必須要結(jié)合路由使用的 前端路由 1. hash 模式 隨著 ajax 的流行...

吳高亮

《烏鴉》(中部)31

“大家談一下那天那個兇殺案件吧,以及對前天晚上那個襲擊南輝的案情做一個分析?!敝匦戮妥氖泄簿志珠L鄭智對著大家首先說話了。緊接著又說了一句,“刑警隊,之前的這個案件主要是由你們牽頭負責處理,這段時間你們對案情掌握的情況都描述一下?!?/p>

刑警隊趙隊長說話了:“已經(jīng)一個月了,沒...

此城此警

大學生創(chuàng)業(yè)的注意事項

1.技術(shù)

用智力換資本,這是大學生創(chuàng)業(yè)的特色之路。一些風險投資家往往就因為看中大學生所掌握的先進技術(shù),而愿意對其創(chuàng)業(yè)計劃進行資助。因此,打算在高科技領(lǐng)域創(chuàng)業(yè)的大學生,一定要注意技術(shù)創(chuàng)新,開發(fā)具有自己獨立知識產(chǎn)權(quán)的產(chǎn)品,吸引投資商。

2.能力 大學生由于長期接受應試教育,不熟...

無限糾結(jié)Cc

別讓我笑我害怕

嚇大的田大俠

一直特別喜歡笑,直到那一年去歐洲盧浮宮玩,在人群之中看到的蒙娜麗莎的那一雙明眸,往下看去,她的微笑,真的,我再也沒敢笑過,怎么形容,在公共汽車上有人對你這么笑,就是有人在扒你腰包,公共場合這樣微笑看起來以為我的褲子拉鏈沒拉好。火車站遇到陌生的女孩對我笑,很甜,...

田大俠

《巴菲特》掃讀后感

今天在路上迅速掃讀了前幾天從得到APP收到的《巴菲特歷年股東信精選》音頻稿——嗯,每天需要讀的東西其實很多,所以要分類精讀泛讀掃讀之類的。

所以大概總結(jié)一下就是: 1長期很重要 2唯一確定的就是不確定性 3仍然看好未來 1長期很重要 APP的另外一個專欄《通往財富自由之路》...

艾萱_劉

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容