Vue來一點料

一、Vue-cli構建的項目目錄

二凹耙、Vue雙向數(shù)據(jù)綁定

原理

Vue遍歷data對象中的所有數(shù)據(jù)姿现,使用Object.defineProperty,將這些屬性在getter/setter中處理肖抱。當屬性被訪問時备典,將所有依賴該數(shù)據(jù)的函數(shù)部分收集起來,當屬性被修改時意述,通知watcher更新依賴該屬性的部分提佣。

不涉及視圖層,僅在js中簡單實現(xiàn)數(shù)據(jù)動態(tài)響應的功能

  //思想:在getter中收集依賴荤崇,在setter中運行依賴拌屏,更新數(shù)據(jù)
  let data={price:6,nums:3,total:0},target=null//目標函數(shù)

  //創(chuàng)建一個依賴,功能:保存對數(shù)據(jù)依賴的函數(shù)术荤,運行依賴data中數(shù)性的函數(shù)
  class Dep{
    constructor(){
      this.subscribers=[]//訂閱者倚喂,訂閱對data屬性有關聯(lián)的函數(shù)等
    }
    depend(){//收集關聯(lián)函數(shù),到訂閱者
      if(target&&!this.subscribers.includes(target)){
        this.subscribers.push(target)//同一個目標喜每,只能訂閱一次
      }
    }
    notify(){//數(shù)據(jù)變化,通知關聯(lián)的依賴更新雳攘,也就是重新調(diào)用函數(shù)带兜,運行
      this.subscribers.forEach(sub=>sub())
    }
  }
  //遍歷屬性,監(jiān)控
  Object.keys(data).forEach((key)=>{
    let internalValue=data[key],dep=new Dep()
    Object.defineProperty(data,key,{
      //訪問屬性吨灭,會自動調(diào)用該函數(shù)
      get(){
        dep.depend()//訂閱關聯(lián)函數(shù)
        return internalValue
      },
      //修改屬性
      set(val){
        internalValue=val
        dep.notify()//更新依賴
      }
    })
  })
  function watcher(fn){
    target=fn
    target()//target中如果訪問或修改data中的屬性刚照,那么將會自動調(diào)用getter和setter
    target=null
  }
  //調(diào)用函數(shù),開啟觀察
  watcher(()=>{
    data.total=data.price*data.nums
  })

控制臺內(nèi)輸入喧兄,查看結(jié)果:


參考資料:【譯】Vue.js是如何做到數(shù)據(jù)響應的无畔?

三啊楚、VirtualDOM與diff

什么是虛擬DOM

在真實的dom中修改其一部分,可能會引起整個dom的波動浑彰,牽一發(fā)而動全身恭理,開銷較大,那么虛擬dom就是為了改變這種大開銷而提出來的郭变,他是js對象颜价,它的目的是最小限度的修改真是的dom,來實現(xiàn)這個過程的就是diff算法诉濒。


diff算法只會在同層比較周伦,不會跨層級比較,并且每次比較的結(jié)果未荒,會立馬在真實的dom中體現(xiàn)专挪,不是都比較完了,在一次性改變真實的dom

diff算法步驟

中心思想
遍歷newVdom中的節(jié)點片排,找到它在oldVdom中的位置寨腔,如果找到了就移動對應(dom:表示真實)dom元素,如果沒有找到划纽,就說明是新增加的節(jié)點脆侮,那么就新建節(jié)點插入dom。如果newVdom遍歷結(jié)束了勇劣,oldVdom還有沒有處理過的節(jié)點靖避,就說明這些節(jié)點在newVdom中被刪除了,那么刪除即可比默。
vue中的diff
遍歷通過在newVdom和oldVdom中設置頭指針和尾指針來實現(xiàn)幻捏,通過移動指針來比較。處理的節(jié)點會標記為已處理(標記的方法有2種命咐,當節(jié)點正好在vdom的指針處篡九,移動指針將它排除到未處理列表之外即可,否則就要采用其他方法醋奠,Vue的做法是將節(jié)點設置為undefined榛臼。

節(jié)點的比較有5種情況
1、if (oldVnode === vnode)窜司,他們的引用一致沛善,可以認為沒有變化。
2塞祈、if(oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text)金刁,文本節(jié)點的比較,需要修改,則會調(diào)用Node.textContent = vnode.text尤蛮。
3媳友、if( oldCh && ch && oldCh !== ch ), 兩個節(jié)點都有子節(jié)點,而且它們不一樣产捞,這樣我們會調(diào)用updateChildren函數(shù)比較子節(jié)點醇锚,這是diff的核心.
4、 else if (ch)轧葛,只有新的節(jié)點有子節(jié)點搂抒,調(diào)用createEle(vnode),vnode.el已經(jīng)引用了老的dom節(jié)點尿扯,createEle函數(shù)會在老dom節(jié)點上添加子節(jié)點求晶。
5、else if (oldCh)衷笋,新節(jié)點沒有子節(jié)點芳杏,老節(jié)點有子節(jié)點,直接刪除老節(jié)點辟宗。

diff核心

1爵赵、首先新舊頭、尾兩兩比較(new_s:old_s泊脐,new_s:old_e空幻,new_e:old_s,new_e:old_e)共四種容客。
處理了這些場景之后秕铛,一方面一些不需要做移動的DOM得到快速處理,另一方面待處理節(jié)點變少缩挑,縮小了后續(xù)操作的處理范圍但两,性能也得到提升
2、如果過程1中供置,不滿足節(jié)點相等谨湘,又分兩種情況:

  • 結(jié)點設置了key
    設key后,會從用key生成的對象oldKeyToIdx中查找匹配的節(jié)點芥丧,所以為節(jié)點設置key可以更高效的利用dom紧阔。這也就是v-for中要設置key的原因
  • 沒有設置key
    直接創(chuàng)建新節(jié)點,刪除舊節(jié)點续担。不能復用

下面舉個例子擅耽,畫出diff完整的過程,每一步dom的變化都用不同顏色的線標出赤拒。


參考資料:解析vue2.0的diff算法
深入Vue2.x的虛擬DOM diff原理

四秫筏、Vuex

1、什么是vuex

vuex是用來管理各個組件的共享數(shù)據(jù)的挎挖,它可以看做是一個倉庫这敬,倉庫里包含了State、Actions和Mutations蕉朵,當其中一個組件更改了State中的共享數(shù)據(jù)時崔涂,也會反映在其他組件中。

使用場景:開發(fā)大型單頁面應用始衅,各個組件之間有很多共享數(shù)據(jù)冷蚂。否則可以使用總線。


在vue-cli構建的項目中使用方式
1汛闸、新建store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)//使用插件的寫法
export default new Vuex.Store({//創(chuàng)建一個倉庫蝙茶,倉庫里有State、actions和mutations
    state: {
    city: '上海'//共享數(shù)據(jù)
    } ,
    actions: {
        changeCity(ctx, city){
         ctx.commit('Xiugaicity', city)//通過commit觸發(fā)mutations修改數(shù)據(jù)诸老,‘xiugaicity’是自定義的觸發(fā)事件
         }
        },
    mutations: {
        xiugaicity(state, city){
            state.city = city
        }
    }
})

2隆夯、在main.js中引入,在vue實例中添加store

import store from './store/index.js'
var vm=new Vue({
  el: '#app',
  // router必須有别伏,否則app中的使用router-view會報錯
  router,
  store,
  // 這里的App是從import App from './App'這里倒入的變量蹄衷,可以更改
  components: { App },
  template: '<App/>'
})

3、在組件中使用厘肮,在需要修改城市的地方調(diào)用dipatch,觸發(fā)actions中的行為

this.$store.dispatch('changeCity',city)//'changeCity'自定義事件愧口,與actions中的事件一致

如果只有同步操作,修改state中的數(shù)據(jù)的時候类茂,可以不dispatch驚動actions耍属,只需要this.$store.commit('xiugaicity',city),越過actions直接commit觸發(fā)mutations更改數(shù)據(jù)。

4大咱、優(yōu)化代碼

//對于state中的屬性每次通過this.$store.state.city來獲取太麻煩恬涧,
//借助mapState,將state中的屬性映射為組件的this.city屬性,對于mutations等碴巾,
//同樣有mapMutations 溯捆,將this.$store.commit('xiugaicity',city)映射為this.xiugaicity方法
// 在單獨構建的版本中輔助函數(shù)為 Vuex.mapState
import { mapState } from 'vuex'

computed: {
  localComputed () { /* ... */ },
  // 使用對象展開運算符可以和組件中的其他計算屬性并存
  ...mapState({
   city:city
  })
}

五、Vue組件通信

1厦瓢、通過Prop向子組件傳遞數(shù)據(jù)

父組件在子組件實例中提揍,通過定義一個屬性:a=“message”,希望子組件接受這個信息煮仇,子組件通過props來接收這個屬性劳跃。

<body>
  <div id="app">//cnt是要傳遞的信息的包裝屬性,count代表的值才是真正的信息
      <button-counter :cnt="count"></button-counter>
  </div>

</body>
<script>
  Vue.component('button-counter',{
    data:function(){
      return{
        count:this.cnt
      }
    },
    props:['cnt'],//接收cnt屬性浙垫,不要直接改變父組件傳遞過來的cnt刨仑,需要復制一下郑诺,否則會報錯
    template:`<button @click="count++">Click{{count}}</button>`
  })
  new Vue({
    el:'#app',
    data:{
      count:0
    }
  })
</script>

props書寫方式:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
props: { title: String, likes: Number, isPublished: Boolean, commentIds: Array, author: Object }
props特性,對應的屬性值不會在dom中顯示杉武,非props特性會在dom結(jié)構中顯示辙诞,子組件使用非props傳過來的值,會報錯轻抱,不使用則不會報錯飞涂。

2、通過事件向父級組件發(fā)送消息

子組件通過$emit('event',value)向父級組件拋出事件且傳值祈搜,父級組件通過@event="函數(shù)名或者js表達式"來執(zhí)行较店。
1、父組件通過js表達式接受

<body>
  <div id="app">
      <button-counter @addcount="count+=$event"></button-counter>
      <div>count:{{count}}</div>
  </div>

</body>
<script>
  Vue.component('button-counter',{
    data:function(){
      return{
      }
    },
    template:`<button @click="$emit('addcount',1)">Click add count</button>`
  })
  new Vue({
    el:'#app',
    data:{
      count:0
    }
  })
</script>

2容燕、在methods中處理

<body>
  <div id="app">
      <button-counter @addcount="addcount"></button-counter>
      <div>count:{{count}}</div>
  </div>

</body>
<script>
  Vue.component('button-counter',{
    data:function(){
      return{
      }
    },
    template:`<button @click="add">Click add count</button>`,
    methods:{
      add(){
        this.$emit('addcount',1)
      }
    }
  })
  new Vue({
    el:'#app',
    data:{
      count:0
    },
    methods:{
      addcount(val){
        this.count+=val
      }
    }
  })
</script>

3梁呈、使用bus總線,組件之間傳值

<body>
  <div id="app">
      <button-counter :name='bob'></button-counter>
      <button-counter  :name='ali'></button-counter>
  </div>
</body>
<script>
  Vue.prototype.bus=new Vue()//使用bus總線蘸秘,bus本身就是vue實例
  Vue.component('button-counter',{
    data:function(){
      return{
        n:this.name
      }
    },
    props:['name'],
    template:`<button @click="change">name:{{this.n}}</button>`,
    methods:{
      change(){
        this.bus.$emit('addcount',this.n)
      }
    },
    mounted(){//在各個組件mounted中監(jiān)聽組件傳值
      var this_=this
      this.bus.$on('addcount',function(n){
        this_.n=n
      })
    }
  })
  new Vue({
    el:'#app',
    data:{
      bob:'bob',
      ali:'dfd'
    }
  })
</script>

4捧杉、使用vuex來管理組件的共享數(shù)據(jù)。

MVC|MVP|MVVM

1秘血、MVC

視圖(View):用戶界面味抖。
控制器(Controller):業(yè)務邏輯
模型(Model):數(shù)據(jù)保存
通信方式


1、View 傳送指令到 Controller
2灰粮、Controller 完成業(yè)務邏輯后仔涩,要求 Model 改變狀態(tài)
3、 Model 將新的數(shù)據(jù)發(fā)送到 View粘舟,用戶得到反饋

2熔脂、MVP

MVP 模式將 Controller 改名為 Presenter,同時改變了通信方向柑肴。


1.各部分之間的通信霞揉,都是雙向的。

  1. View 與 Model 不發(fā)生聯(lián)系晰骑,都通過 Presenter 傳遞适秩。
  2. View 非常薄,不部署任何業(yè)務邏輯硕舆,稱為"被動視圖"(Passive View)秽荞,即沒有任何主動性,而 Presenter非常厚抚官,所有邏輯都部署在那里扬跋。
3、MVVM

MVVM 模式將 Presenter 改名為 ViewModel凌节,基本上與 MVP 模式完全一致钦听。



唯一的區(qū)別是洒试,它采用雙向綁定(data-binding):View的變動,自動反映在 ViewModel朴上,反之亦然儡司。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市余指,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跷坝,老刑警劉巖酵镜,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異柴钻,居然都是意外死亡淮韭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門贴届,熙熙樓的掌柜王于貴愁眉苦臉地迎上來靠粪,“玉大人,你說我怎么就攤上這事毫蚓≌技” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵元潘,是天一觀的道長畔乙。 經(jīng)常有香客問我,道長翩概,這世上最難降的妖魔是什么牲距? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮钥庇,結(jié)果婚禮上牍鞠,老公的妹妹穿的比我還像新娘。我一直安慰自己评姨,他們只是感情好难述,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吐句,像睡著了一般龄广。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蕴侧,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天择同,我揣著相機與錄音,去河邊找鬼净宵。 笑死敲才,一個胖子當著我的面吹牛裹纳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播紧武,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼剃氧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了阻星?” 一聲冷哼從身側(cè)響起朋鞍,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妥箕,沒想到半個月后滥酥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡畦幢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年坎吻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宇葱。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘦真,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出黍瞧,到底是詐尸還是另有隱情诸尽,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布印颤,位于F島的核電站弦讽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏膀哲。R本人自食惡果不足惜往产,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望某宪。 院中可真熱鬧仿村,春花似錦、人聲如沸兴喂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衣迷。三九已至畏鼓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間壶谒,已是汗流浹背云矫。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留汗菜,地道東北人让禀。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓挑社,卻偏偏與公主長得像,于是被迫代替她去往敵國和親巡揍。 傳聞我的和親對象是個殘疾皇子痛阻,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容