日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
vue2.0源碼分析之理解響應(yīng)式架構(gòu)

分享前啰嗦

創(chuàng)新互聯(lián)公司是一家專業(yè)的成都網(wǎng)站建設(shè)公司,我們專注網(wǎng)站建設(shè)、成都做網(wǎng)站、網(wǎng)絡(luò)營銷、企業(yè)網(wǎng)站建設(shè),友情鏈接,廣告投放為企業(yè)客戶提供一站式建站解決方案,能帶給客戶新的互聯(lián)網(wǎng)理念。從網(wǎng)站結(jié)構(gòu)的規(guī)劃UI設(shè)計到用戶體驗提高,創(chuàng)新互聯(liá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 為 
    {{text}}
  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會從

before
變?yōu)?
after

好,開始吧!!!

***步, 講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.     function observer(value, cb){
  13.       Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
  14.     }
  15.     function defineReactive(obj, key, val, cb) {
  16.       Object.defineProperty(obj, key, {
  17.         enumerable: true,
  18.         configurable: true,
  19.         get: ()=>{},
  20.         set:newVal=> {
  21.           cb()
  22.         }
  23.       })
  24.     }
  25.     var demo = new Vue({
  26.       el: '#demo',
  27.       data: {
  28.         text: 123,
  29.       },
  30.       render(){
  31.         console.log("我要render了")
  32.       }
  33.     })
  34.      setTimeout(function(){
  35.        demo._data.text = 444
  36.      }, 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)用進(jìn)而重新渲染,是怎樣做到的呢,我們知道其實就是賦值的時候就要改變對吧,當(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.         set: function 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ā)進(jìn)而重新渲染,屬性這顯然不夠精準(zhǔn)啊

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

比如考慮下面代碼

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

我們運行一下,他會輸出

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

這就是 虛擬最簡單虛擬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.    class Vue {
  10.      constructor(options) {
  11.        this.$options = options
  12.        this._data = options.data
  13.        Object.keys(options.data).forEach(key => this._proxy(key))
  14.        observer(options.data)
  15.        const vdom = watch(this, this._render.bind(this), this._update.bind(this))
  16.        console.log(vdom)
  17.      }
  18.      _proxy(key) {
  19.        const self = this
  20.        Object.defineProperty(self, key, {
  21.          configurable: true,
  22.          enumerable: true,
  23.          get: function proxyGetter () {
  24.            return self._data[key]
  25.          },
  26.          set: function proxySetter (val) {
  27.            self._data.text = val
  28.          }
  29.        })
  30.      }
  31.      _update() {
  32.        console.log("我需要更新");
  33.        const vdom = this._render.call(this)
  34.        console.log(vdom);
  35.      }
  36.      _render() {
  37.        return this.$options.render.call(this)
  38.      }
  39.      __h__(tag, attr, children) {
  40.        return VNode(tag, attr, children.map((child)=>{
  41.          if(typeof child === 'string'){
  42.            return VNode(undefined, undefined, undefined, child)
  43.          }else{
  44.            return child
  45.          }
  46.        }))
  47.      }
  48.      __toString__(val) {
  49.        return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val);
  50.      }
  51.    }
  52.    function observer(value, cb){
  53.      Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
  54.    }
  55.    function defineReactive(obj, key, val, cb) {
  56.      const dep = new Dep()
  57.      Object.defineProperty(obj, key, {
  58.        enumerable: true,
  59.        configurable: true,
  60.        get: ()=>{
  61.          if(Dep.target){
  62.            dep.add(Dep.target)
  63.          }
  64.          return val
  65.        },
  66.        set: newVal => {
  67.          if(newVal === val)
  68.            return
  69.          val = newVal
  70.          dep.notify()
  71.        }
  72.      })
  73.    }
  74.    function watch(vm, exp, cb){
  75.      Dep.target = cb
  76.      return exp()
  77.    }
  78.    class Dep {
  79.      constructor() {
  80.        this.subs = []
  81.      }
  82.      add(cb) {
  83.        this.subs.push(cb)
  84.      }
  85.      notify() {
  86.        this.subs.forEach((cb) => cb())
  87.      }
  88.    }
  89.    Dep.target = null
  90.    var demo = new Vue({
  91.      el: '#demo',
  92.      data: {
  93.        text: "before",
  94.      },
  95.      render(){
  96.        return this.__h__('div', {}, [
  97.          this.__h__('span', {}, [this.__toString__(this.text)])
  98.        ])
  99.      }
  100.    })
  101.     setTimeout(function(){
  102.       demo.text = "after"
  103.     }, 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如何性能達(dá)到***化(前傳),暨react為啥非得使用immutable.js

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

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

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

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

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


網(wǎng)頁標(biāo)題:vue2.0源碼分析之理解響應(yīng)式架構(gòu)
本文網(wǎng)址:http://m.5511xx.com/article/ccsejdj.html