偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

分享前啰嗦

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

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

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

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

本文分享什么

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

順帶介紹他比react快的其中一個(gè)原因

本分實(shí)現(xiàn)什么

  1. const demo = new Vue({ 
  2.   data: { 
  3.     text: "before"
  4.   }, 
  5.   //對(duì)應(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)  

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

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

好,開(kāi)始吧!!!

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

來(lái)來(lái)來(lái)先看代碼吧

  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)  

為了好演示我們只考慮最簡(jiǎn)單的情況,如果看了vue 源碼分析之如何實(shí)現(xiàn) observer 和 watcher可能就會(huì)很好理解,不過(guò)沒(méi)關(guān)系,我們?nèi)詢烧Z(yǔ)再說(shuō)說(shuō),這段代碼要實(shí)現(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)用進(jìn)而重新渲染,是怎樣做到的呢,我們知道其實(shí)就是賦值的時(shí)候就要改變對(duì)吧,當(dāng)我給data下面的text 賦值的時(shí)候 set 函數(shù)就會(huì)觸發(fā),這個(gè)時(shí)候 調(diào)用_update 就ok了,但是

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

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

  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)) 

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

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

第二步,詳細(xì)闡述***步為什么不夠精準(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上的兩個(gè)屬性name和age,但是當(dāng)我改變height的時(shí)候,用***步的代碼,會(huì)不會(huì)觸發(fā)重新渲染?會(huì)!,但其實(shí)不需要觸發(fā)重新渲染,這就是問(wèn)題所在!!

第三步,上述問(wèn)題怎么解決

簡(jiǎn)單說(shuō)說(shuō)虛擬 DOM

首先,template***都是編譯成render函數(shù)的(具體怎么做,就不展開(kāi)說(shuō)了,以后我會(huì)說(shuō)的),然后render 函數(shù)執(zhí)行完就會(huì)得到一個(gè)虛擬DOM,為了好理解我們寫(xiě)寫(xiě)最簡(jiǎn)單的虛擬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.     })  

我們運(yùn)行一下,他會(huì)輸出

  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àn)槲覀優(yōu)榱搜菔揪筒粚?xiě)代理的代碼,所以這里為空 
  13.             } 
  14.           ] 
  15.         } 
  16.       ] 
  17.     }  

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

回到開(kāi)始的問(wèn)題,也就是說(shuō),我得知道,render 函數(shù)里面依賴了vue實(shí)例里面哪些變量(只考慮render 就可以,因?yàn)閠emplate 也會(huì)是幫你編譯成render)。敘述有點(diǎn)拗口,還是看代碼吧

  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ù)里其實(shí)只依賴text,并沒(méi)有依賴 name和 age,所以,我們只要text改變的時(shí)候

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

第三步,'touch' 拿到依賴

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

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

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

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

上面這些步驟簡(jiǎn)單用四個(gè)子概括就是 計(jì)算依賴。

(其實(shí)不僅是render,任何一個(gè)變量的改別,是因?yàn)閯e的變量改變引起,都可以用上述方法,也就是computed 和 watch 的原理,也是mobx的核心)

***步,

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

時(shí)候就初始化它,簡(jiǎn)單來(lái)說(shuō)就是這個(gè)樣子的

  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'依賴的時(shí)候,依賴到的變量get就會(huì)被執(zhí)行,然后我們就可以把這個(gè) render 函數(shù)加到 subs 里面去了。

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

至此就完成了整個(gè)圖,好我們將所有的代碼展示出來(lái)

  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)  

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

好我們解釋一下 Dep.target 因?yàn)槲覀兊脜^(qū)分是,普通的get,還是在查找依賴的時(shí)候的get,

所有我們?cè)诓檎乙蕾嚂r(shí)候,我們將

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

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

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

判斷一下,就好了。到現(xiàn)在為止,我們?cè)倏茨菑垐D是不是就清楚很多了?

總結(jié)

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

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

對(duì)比react,vue2.0 自動(dòng)幫你監(jiān)測(cè)依賴,自動(dòng)幫你重新渲染,而

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

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

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

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

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

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

后續(xù),我會(huì)簡(jiǎn)單聊聊,vue2.0的diff。

責(zé)任編輯:龐桂玉 來(lái)源: 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如何定義事件

2010-07-20 08:50:00

autoreleaseObjective C

2020-06-09 11:35:30

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

2021-01-22 11:47:27

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

2021-05-19 14:25:19

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

2017-08-30 17:10:43

前端JavascriptVue.js

2016-10-26 20:49:24

ReactJavascript前端

2022-03-09 23:02:30

Java編程處理模型

2021-07-28 20:13:04

響應(yīng)式編程

2022-02-18 09:39:51

Vue3.0Vue2.0Script Set

2011-07-25 14:56:00

SQL SERVER數(shù)

2011-07-25 15:17:50

SQL SERVER數(shù)

2022-06-26 00:00:02

Vue3響應(yīng)式系統(tǒng)
點(diǎn)贊
收藏

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