自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

vue2.0源碼分析之理解響應(yīng)式架構(gòu)

開發(fā) 前端
我之前介紹過vue1.0如何實現(xiàn)observer和watcher。本想繼續(xù)寫下去,可是vue2.0橫空出世,所以直接看vue2.0吧。這篇文章在公司分享過,終于寫出來了。我們采用用最精簡的代碼,還原vue2.0響應(yīng)式架構(gòu)實現(xiàn)

分享前啰嗦

我之前介紹過vue1.0如何實現(xiàn)observer和watcher。本想繼續(xù)寫下去,可是vue2.0橫空出世..所以

直接看vue2.0吧。這篇文章在公司分享過,終于寫出來了。我們采用用最精簡的代碼,還原vue2.0響應(yīng)式架構(gòu)實現(xiàn)

以前寫的那篇 vue 源碼分析之如何實現(xiàn) observer 和 watcher可以作為本次分享的參考。

不過不看也沒關(guān)系,但是***了解下Object.defineProperty

本文分享什么

理解vue2.0的響應(yīng)式架構(gòu),就是下面這張圖

順帶介紹他比react快的其中一個原因

本分實現(xiàn)什么

  1. const demo = new Vue({ 
  2.   data: { 
  3.     text: "before"
  4.   }, 
  5.   //對應(yīng)的template 為 <div><span>{{text}}</span></div> 
  6.   render(h){ 
  7.     return h('div', {}, [ 
  8.       h('span', {}, [this.__toString__(this.text)]) 
  9.     ]) 
  10.   } 
  11. }) 
  12.  setTimeout(function(){ 
  13.    demo.text = "after" 
  14.  }, 3000)  

對應(yīng)的虛擬dom會從

<div><span>before</span></div> 變?yōu)? <div><span>after</span></div>

好,開始吧!!!

***步, 講data 下面所有屬性變?yōu)閛bservable

來來來先看代碼吧

  1. class Vue { 
  2.       constructor(options) { 
  3.         this.$options = options 
  4.         this._data = options.data 
  5.         observer(options.data, this._update) 
  6.         this._update() 
  7.       } 
  8.       _update(){ 
  9.         this.$options.render() 
  10.       } 
  11.     } 
  12.  
  13.  
  14.     function observer(value, cb){ 
  15.       Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb)) 
  16.     } 
  17.  
  18.     function defineReactive(obj, key, val, cb) { 
  19.       Object.defineProperty(obj, key, { 
  20.         enumerable: true
  21.         configurable: true
  22.         get: ()=>{}, 
  23.         set:newVal=> { 
  24.           cb() 
  25.         } 
  26.       }) 
  27.     } 
  28.  
  29.     var demo = new Vue({ 
  30.       el: '#demo'
  31.       data: { 
  32.         text: 123, 
  33.       }, 
  34.       render(){ 
  35.         console.log("我要render了"
  36.       } 
  37.     }) 
  38.  
  39.      setTimeout(function(){ 
  40.        demo._data.text = 444 
  41.      }, 3000)  

為了好演示我們只考慮最簡單的情況,如果看了vue 源碼分析之如何實現(xiàn) observer 和 watcher可能就會很好理解,不過沒關(guān)系,我們?nèi)詢烧Z再說說,這段代碼要實現(xiàn)的功能就是將

  1. var demo = new Vue({ 
  2.      el: '#demo'
  3.      data: { 
  4.        text: 123, 
  5.      }, 
  6.      render(){ 
  7.        console.log("我要render了"
  8.      } 
  9.    })  

中data 里面所有的屬性置于 observer,然后data里面的屬性,比如 text 以改變,就引起_update()函數(shù)調(diào)用進而重新渲染,是怎樣做到的呢,我們知道其實就是賦值的時候就要改變對吧,當(dāng)我給data下面的text 賦值的時候 set 函數(shù)就會觸發(fā),這個時候 調(diào)用_update 就ok了,但是

  1. setTimeout(function(){ 
  2.       demo._data.text = 444 
  3.     }, 3000)  

demo._data.text沒有demo.text用著爽,沒關(guān)系,我們加一個代理

  1. _proxy(key) { 
  2.       const self = this 
  3.       Object.defineProperty(self, key, { 
  4.         configurable: true
  5.         enumerable: true
  6.         get: function proxyGetter () { 
  7.           return self._data[key
  8.         }, 
  9.         setfunction proxySetter (val) { 
  10.           self._data[key] = val 
  11.         } 
  12.       }) 
  13.     }  

然后在Vue的constructor加上下面這句

  1. Object.keys(options.data).forEach(key => this._proxy(key)) 

***步先說到這里,我們會發(fā)現(xiàn)一個問題,data中任何一個屬性的值改變,都會引起

_update的觸發(fā)進而重新渲染,屬性這顯然不夠精準(zhǔn)啊

第二步,詳細闡述***步為什么不夠精準(zhǔn)

比如考慮下面代碼

  1. new Vue({ 
  2.      template: ` 
  3.        <div> 
  4.          <section
  5.            <span>name:</span> {{name}} 
  6.          </section
  7.          <section
  8.            <span>age:</span> {{age}} 
  9.          </section
  10.        <div>`, 
  11.      data: { 
  12.        name'js'
  13.        age: 24, 
  14.        height: 180 
  15.      } 
  16.    }) 
  17.  
  18.    setTimeout(function(){ 
  19.      demo.height = 181 
  20.    }, 3000)  

template里面只用到了data上的兩個屬性name和age,但是當(dāng)我改變height的時候,用***步的代碼,會不會觸發(fā)重新渲染?會!,但其實不需要觸發(fā)重新渲染,這就是問題所在!!

第三步,上述問題怎么解決

簡單說說虛擬 DOM

首先,template***都是編譯成render函數(shù)的(具體怎么做,就不展開說了,以后我會說的),然后render 函數(shù)執(zhí)行完就會得到一個虛擬DOM,為了好理解我們寫寫最簡單的虛擬DOM

  1. function VNode(tag, data, children, text) { 
  2.       return { 
  3.         tag: tag, 
  4.         data: data, 
  5.         children: children, 
  6.         text: text 
  7.       } 
  8.     } 
  9.  
  10.     class Vue { 
  11.       constructor(options) { 
  12.         this.$options = options 
  13.         const vdom = this._update() 
  14.         console.log(vdom) 
  15.       } 
  16.       _update() { 
  17.         return this._render.call(this) 
  18.       } 
  19.       _render() { 
  20.         const vnode = this.$options.render.call(this) 
  21.         return vnode 
  22.       } 
  23.       __h__(tag, attr, children) { 
  24.         return VNode(tag, attr, children.map((child)=>{ 
  25.           if(typeof child === 'string'){ 
  26.             return VNode(undefined, undefined, undefined, child) 
  27.           }else
  28.             return child 
  29.           } 
  30.         })) 
  31.       } 
  32.       __toString__(val) { 
  33.         return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val); 
  34.       } 
  35.     } 
  36.  
  37.  
  38.     var demo = new Vue({ 
  39.       el: '#demo'
  40.       data: { 
  41.         text: "before"
  42.       }, 
  43.       render(){ 
  44.         return this.__h__('div', {}, [ 
  45.           this.__h__('span', {}, [this.__toString__(this.text)]) 
  46.         ]) 
  47.       } 
  48.     })  

我們運行一下,他會輸出

  1.       tag: 'div'
  2.       data: {}, 
  3.       children:[ 
  4.         { 
  5.           tag: 'span'
  6.           data: {}, 
  7.           children: [ 
  8.             { 
  9.               children: undefined, 
  10.               data: undefined, 
  11.               tag: undefined, 
  12.               text: '' // 正常情況為 字符串 before,因為我們?yōu)榱搜菔揪筒粚懘淼拇a,所以這里為空 
  13.             } 
  14.           ] 
  15.         } 
  16.       ] 
  17.     }  

這就是 虛擬最簡單虛擬DOM,tag是html 標(biāo)簽名,data 是包含諸如 class 和 style 這些標(biāo)簽上的屬性,childen就是子節(jié)點,關(guān)于虛擬DOM就不展開說了。

回到開始的問題,也就是說,我得知道,render 函數(shù)里面依賴了vue實例里面哪些變量(只考慮render 就可以,因為template 也會是幫你編譯成render)。敘述有點拗口,還是看代碼吧

  1. var demo = new Vue({ 
  2.       el: '#demo'
  3.       data: { 
  4.         text: "before"
  5.         name"123"
  6.         age: 23 
  7.       }, 
  8.       render(){ 
  9.         return this.__h__('div', {}, [ 
  10.           this.__h__('span', {}, [this.__toString__(this.text)]) 
  11.         ]) 
  12.       } 
  13.     })  

就像這段代碼,render 函數(shù)里其實只依賴text,并沒有依賴 name和 age,所以,我們只要text改變的時候

我們自動觸發(fā) render 函數(shù) 讓它生成一個虛擬DOM就ok了(剩下的就是這個虛擬DOM和上個虛擬DOM做比對,然后操作真實DOM,只能以后再說了),那么我們正式考慮一下怎么做

第三步,'touch' 拿到依賴

回到最上面那張圖,我們知道data上的屬性設(shè)置defineReactive后,修改data 上的值會觸發(fā) set。

那么我們?nèi)ata上值是會觸發(fā) get了。

對,我們可以在上面做做手腳,我們先執(zhí)行一下render,我們看看data上哪些屬性觸發(fā)了get,我們豈不是就可以知道 render 會依賴data 上哪些變量了。

然后我么把這些變量做些手腳,每次這些變量變的時候,我們就觸發(fā)render。

上面這些步驟簡單用四個子概括就是 計算依賴。

(其實不僅是render,任何一個變量的改別,是因為別的變量改變引起,都可以用上述方法,也就是computed 和 watch 的原理,也是mobx的核心)

***步,

我們寫一個依賴收集的類,每一個data 上的對象都有可能被render函數(shù)依賴,所以每個屬性在defineReactive

時候就初始化它,簡單來說就是這個樣子的

  1. class Dep { 
  2.       constructor() { 
  3.         this.subs = [] 
  4.       } 
  5.       add(cb) { 
  6.         this.subs.push(cb) 
  7.       } 
  8.       notify() { 
  9.         console.log(this.subs); 
  10.         this.subs.forEach((cb) => cb()) 
  11.       } 
  12.     } 
  13.     function defineReactive(obj, key, val, cb) { 
  14.       const dep = new Dep() 
  15.       Object.defineProperty(obj, key, { 
  16.         // 省略 
  17.       }) 
  18.     }  

然后,當(dāng)執(zhí)行render 函數(shù)去'touch'依賴的時候,依賴到的變量get就會被執(zhí)行,然后我們就可以把這個 render 函數(shù)加到 subs 里面去了。

當(dāng)我們,set 的時候 我們就執(zhí)行 notify 將所有的subs數(shù)組里的函數(shù)執(zhí)行,其中就包含render 的執(zhí)行。

至此就完成了整個圖,好我們將所有的代碼展示出來

  1. function VNode(tag, data, children, text) { 
  2.      return { 
  3.        tag: tag, 
  4.        data: data, 
  5.        children: children, 
  6.        text: text 
  7.      } 
  8.    } 
  9.  
  10.    class Vue { 
  11.      constructor(options) { 
  12.        this.$options = options 
  13.        this._data = options.data 
  14.        Object.keys(options.data).forEach(key => this._proxy(key)) 
  15.        observer(options.data) 
  16.        const vdom = watch(this, this._render.bind(this), this._update.bind(this)) 
  17.        console.log(vdom) 
  18.      } 
  19.      _proxy(key) { 
  20.        const self = this 
  21.        Object.defineProperty(self, key, { 
  22.          configurable: true
  23.          enumerable: true
  24.          get: function proxyGetter () { 
  25.            return self._data[key
  26.          }, 
  27.          setfunction proxySetter (val) { 
  28.            self._data.text = val 
  29.          } 
  30.        }) 
  31.      } 
  32.      _update() { 
  33.        console.log("我需要更新"); 
  34.        const vdom = this._render.call(this) 
  35.        console.log(vdom); 
  36.      } 
  37.      _render() { 
  38.        return this.$options.render.call(this) 
  39.      } 
  40.      __h__(tag, attr, children) { 
  41.        return VNode(tag, attr, children.map((child)=>{ 
  42.          if(typeof child === 'string'){ 
  43.            return VNode(undefined, undefined, undefined, child) 
  44.          }else
  45.            return child 
  46.          } 
  47.        })) 
  48.      } 
  49.      __toString__(val) { 
  50.        return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val); 
  51.      } 
  52.    } 
  53.  
  54.    function observer(value, cb){ 
  55.      Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb)) 
  56.    } 
  57.  
  58.    function defineReactive(obj, key, val, cb) { 
  59.      const dep = new Dep() 
  60.      Object.defineProperty(obj, key, { 
  61.        enumerable: true
  62.        configurable: true
  63.        get: ()=>{ 
  64.          if(Dep.target){ 
  65.            dep.add(Dep.target) 
  66.          } 
  67.          return val 
  68.        }, 
  69.        set: newVal => { 
  70.          if(newVal === val) 
  71.            return 
  72.          val = newVal 
  73.          dep.notify() 
  74.        } 
  75.      }) 
  76.    } 
  77.    function watch(vm, exp, cb){ 
  78.      Dep.target = cb 
  79.      return exp() 
  80.    } 
  81.  
  82.    class Dep { 
  83.      constructor() { 
  84.        this.subs = [] 
  85.      } 
  86.      add(cb) { 
  87.        this.subs.push(cb) 
  88.      } 
  89.      notify() { 
  90.        this.subs.forEach((cb) => cb()) 
  91.      } 
  92.    } 
  93.    Dep.target = null 
  94.  
  95.  
  96.    var demo = new Vue({ 
  97.      el: '#demo'
  98.      data: { 
  99.        text: "before"
  100.      }, 
  101.      render(){ 
  102.        return this.__h__('div', {}, [ 
  103.          this.__h__('span', {}, [this.__toString__(this.text)]) 
  104.        ]) 
  105.      } 
  106.    }) 
  107.  
  108.  
  109.     setTimeout(function(){ 
  110.       demo.text = "after" 
  111.     }, 3000)  

我們看一下運行結(jié)果

好我們解釋一下 Dep.target 因為我們得區(qū)分是,普通的get,還是在查找依賴的時候的get,

所有我們在查找依賴時候,我們將

  1. function watch(vm, exp, cb){ 
  2.       Dep.target = cb 
  3.       return exp() 
  4.     }  

Dep.target 賦值,相當(dāng)于 flag 一下,然后 get 的時候

  1. get: () => { 
  2.           if (Dep.target) { 
  3.             dep.add(Dep.target) 
  4.           } 
  5.           return val 
  6.         },  

判斷一下,就好了。到現(xiàn)在為止,我們再看那張圖是不是就清楚很多了?

總結(jié)

我非常喜歡,vue2.0 以上代碼為了好展示,都采用最簡單的方式呈現(xiàn)。

不過整個代碼執(zhí)行過程,甚至是命名方式都和vue2.0一樣

對比react,vue2.0 自動幫你監(jiān)測依賴,自動幫你重新渲染,而

react 要實現(xiàn)性能***化,要做大量工作,比如我以前分享的

react如何性能達到***化(前傳),暨react為啥非得使用immutable.js

react 實現(xiàn)pure render的時候,bind(this)隱患。

而 vue2.0 天然幫你做到了***,而且對于像萬年不變的 如標(biāo)簽上靜態(tài)的class屬性,

vue2.0 在重新渲染后做diff 的時候是不比較的,vue2.0比 達到性能***化的react 還要快的一個原因

然后源碼在此,喜歡的記得給個 star 哦😍

后續(xù),我會簡單聊聊,vue2.0的diff。

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2021-09-27 06:29:47

Vue3 響應(yīng)式原理Vue應(yīng)用

2019-07-01 13:34:22

vue系統(tǒng)數(shù)據(jù)

2017-07-25 14:07:14

前端Vue模板渲染

2021-08-27 12:59:59

React前端命令

2011-03-18 11:00:48

LAMPLAMP 架構(gòu)

2024-09-02 16:10:19

vue2前端

2011-06-16 08:48:33

Java

2023-12-06 07:43:56

Vue如何定義事件

2020-06-09 11:35:30

Vue 3響應(yīng)式前端

2010-07-20 08:50:00

autoreleaseObjective C

2021-01-22 11:47:27

Vue.js響應(yīng)式代碼

2021-05-19 14:25:19

前端開發(fā)技術(shù)

2016-10-26 20:49:24

ReactJavascript前端

2017-08-30 17:10:43

前端JavascriptVue.js

2021-07-28 20:13:04

響應(yīng)式編程

2022-03-09 23:02:30

Java編程處理模型

2022-02-18 09:39:51

Vue3.0Vue2.0Script Set

2011-07-25 15:17:50

SQL SERVER數(shù)

2011-07-25 14:56:00

SQL SERVER數(shù)

2022-08-22 09:01:24

Vue響應(yīng)式原則雙向數(shù)據(jù)綁定
點贊
收藏

51CTO技術(shù)棧公眾號