最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

100行代碼理解和分析vue2.0響應式架構

 更新時間:2017年03月09日 10:53:37   作者:楊川寶  
通過100行代碼幫助大家理解和分析vue2.0響應式架構的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下

分享前啰嗦

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

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

不過不看也沒關系,但是最好了解下Object.defineProperty

本文分享什么

理解vue2.0的響應式架構,就是下面這張圖


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

本分實現(xiàn)什么

const demo = new Vue({
 data: {
 text: "before",
 },
 //對應的template 為 <div><span>{{text}}</span></div>
 render(h){
 return h('div', {}, [
 h('span', {}, [this.__toString__(this.text)])
 ])
 }
})
 setTimeout(function(){
 demo.text = "after"
 }, 3000)

對應的虛擬dom會從

<div><span>before</span></div> 變?yōu)?span style="color: #800000"> <div><span>after</span></div>

好,開始吧!??!

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

來來來先看代碼吧

 class Vue {
 constructor(options) {
 this.$options = options
 this._data = options.data
 observer(options.data, this._update)
 this._update()
 }
 _update(){
 this.$options.render()
 }
 }


 function observer(value, cb){
 Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
 }

 function defineReactive(obj, key, val, cb) {
 Object.defineProperty(obj, key, {
 enumerable: true,
 configurable: true,
 get: ()=>{},
 set:newVal=> {
 cb()
 }
 })
 }

 var demo = new Vue({
 el: '#demo',
 data: {
 text: 123,
 },
 render(){
 console.log("我要render了")
 }
 })

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

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

 var demo = new Vue({
 el: '#demo',
 data: {
 text: 123,
 },
 render(){
 console.log("我要render了")
 }
 })

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

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

demo._data.text沒有demo.text用著爽,沒關系,我們加一個代理

 _proxy(key) {
 const self = this
 Object.defineProperty(self, key, {
 configurable: true,
 enumerable: true,
 get: function proxyGetter () {
 return self._data[key]
 },
 set: function proxySetter (val) {
 self._data[key] = val
 }
 })
 }

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

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

第一步先說到這里,我們會發(fā)現(xiàn)一個問題,data中任何一個屬性的值改變,都會引起
_update的觸發(fā)進而重新渲染,屬性這顯然不夠精準啊

第二步,詳細闡述第一步為什么不夠精準

比如考慮下面代碼

 new Vue({
 template: `
 <div>
 <section>
 <span>name:</span> {{name}}
 </section>
 <section>
 <span>age:</span> {{age}}
 </section>
 <div>`,
 data: {
 name: 'js',
 age: 24,
 height: 180
 }
 })

 setTimeout(function(){
 demo.height = 181
 }, 3000)

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

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

 function VNode(tag, data, children, text) {
 return {
 tag: tag,
 data: data,
 children: children,
 text: text
 }
 }

 class Vue {
 constructor(options) {
 this.$options = options
 const vdom = this._update()
 console.log(vdom)
 }
 _update() {
 return this._render.call(this)
 }
 _render() {
 const vnode = this.$options.render.call(this)
 return vnode
 }
 __h__(tag, attr, children) {
 return VNode(tag, attr, children.map((child)=>{
 if(typeof child === 'string'){
 return VNode(undefined, undefined, undefined, child)
 }else{
 return child
 }
 }))
 }
 __toString__(val) {
 return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val);
 }
 }


 var demo = new Vue({
 el: '#demo',
 data: {
 text: "before",
 },
 render(){
 return this.__h__('div', {}, [
 this.__h__('span', {}, [this.__toString__(this.text)])
 ])
 }
 })

我們運行一下,他會輸出

 {
 tag: 'div',
 data: {},
 children:[
 {
 tag: 'span',
 data: {},
 children: [
 {
 children: undefined,
 data: undefined,
 tag: undefined,
 text: '' // 正常情況為 字符串 before,因為我們?yōu)榱搜菔揪筒粚懘淼拇a,所以這里為空
 }
 ]
 }
 ]
 }

這就是 虛擬最簡單虛擬DOM,taghtml 標簽名,data 是包含諸如classstyle這些標簽上的屬性,childen就是子節(jié)點,關于虛擬DOM就不展開說了。

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

 var demo = new Vue({
 el: '#demo',
 data: {
 text: "before",
 name: "123",
 age: 23
 },
 render(){
 return this.__h__('div', {}, [
 this.__h__('span', {}, [this.__toString__(this.text)])
 ])
 }
 })

就像這段代碼,render 函數(shù)里其實只依賴text,并沒有依賴name和age,所以,我們只要text改變的時候,我們自動觸發(fā)render 函數(shù) 讓它生成一個虛擬DOM就ok了(剩下的就是這個虛擬DOM和上個虛擬DOM做比對,然后操作真實DOM,只能以后再說了),那么我們正式考慮一下怎么做

第三步,'touch' 拿到依賴

回到最上面那張圖,我們知道data上的屬性設置defineReactive后,修改data 上的值會觸發(fā)set。
那么我們取data上值是會觸發(fā)get了。
對,我們可以在上面做做手腳,我們先執(zhí)行一下render,我們看看data上哪些屬性觸發(fā)了get,我們豈不是就可以知道 render 會依賴data 上哪些變量了。
然后我么把這些變量做些手腳,每次這些變量變的時候,我們就觸發(fā)render。
上面這些步驟簡單用四個子概括就是 計算依賴。
(其實不僅是render,任何一個變量的改別,是因為別的變量改變引起,都可以用上述方法,也就是computed 和 watch 的原理,也是mobx的核心)

第一步:
我們寫一個依賴收集的類,每一個data 上的對象都有可能被render函數(shù)依賴,所以每個屬性在defineReactive時候就初始化它,簡單來說就是這個樣子的。

 class Dep {
 constructor() {
 this.subs = []
 }
 add(cb) {
 this.subs.push(cb)
 }
 notify() {
 console.log(this.subs);
 this.subs.forEach((cb) => cb())
 }
 }
 function defineReactive(obj, key, val, cb) {
 const dep = new Dep()
 Object.defineProperty(obj, key, {
 // 省略
 })
 }

然后,當執(zhí)行render 函數(shù)去'touch'依賴的時候,依賴到的變量get就會被執(zhí)行,然后我們就可以把這個 render 函數(shù)加到 subs 里面去了。
當我們,set 的時候 我們就執(zhí)行 notify 將所有的subs數(shù)組里的函數(shù)執(zhí)行,其中就包含render 的執(zhí)行。
至此就完成了整個圖,好我們將所有的代碼展示出來

 function VNode(tag, data, children, text) {
 return {
 tag: tag,
 data: data,
 children: children,
 text: text
 }
 }

 class Vue {
 constructor(options) {
 this.$options = options
 this._data = options.data
 Object.keys(options.data).forEach(key => this._proxy(key))
 observer(options.data)
 const vdom = watch(this, this._render.bind(this), this._update.bind(this))
 console.log(vdom)
 }
 _proxy(key) {
 const self = this
 Object.defineProperty(self, key, {
 configurable: true,
 enumerable: true,
 get: function proxyGetter () {
 return self._data[key]
 },
 set: function proxySetter (val) {
 self._data.text = val
 }
 })
 }
 _update() {
 console.log("我需要更新");
 const vdom = this._render.call(this)
 console.log(vdom);
 }
 _render() {
 return this.$options.render.call(this)
 }
 __h__(tag, attr, children) {
 return VNode(tag, attr, children.map((child)=>{
 if(typeof child === 'string'){
 return VNode(undefined, undefined, undefined, child)
 }else{
 return child
 }
 }))
 }
 __toString__(val) {
 return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val);
 }
 }

 function observer(value, cb){
 Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
 }

 function defineReactive(obj, key, val, cb) {
 const dep = new Dep()
 Object.defineProperty(obj, key, {
 enumerable: true,
 configurable: true,
 get: ()=>{
 if(Dep.target){
 dep.add(Dep.target)
 }
 return val
 },
 set: newVal => {
 if(newVal === val)
 return
 val = newVal
 dep.notify()
 }
 })
 }
 function watch(vm, exp, cb){
 Dep.target = cb
 return exp()
 }

 class Dep {
 constructor() {
 this.subs = []
 }
 add(cb) {
 this.subs.push(cb)
 }
 notify() {
 this.subs.forEach((cb) => cb())
 }
 }
 Dep.target = null


 var demo = new Vue({
 el: '#demo',
 data: {
 text: "before",
 },
 render(){
 return this.__h__('div', {}, [
 this.__h__('span', {}, [this.__toString__(this.text)])
 ])
 }
 })


 setTimeout(function(){
 demo.text = "after"
 }, 3000)

我們看一下運行結果


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

 function watch(vm, exp, cb){
 Dep.target = cb
 return exp()
 }

Dep.target 賦值,相當于 flag 一下,然后 get 的時候

 get: () => {
 if (Dep.target) {
 dep.add(Dep.target)
 }
 return val
 },

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

總結

我非常喜歡,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 天然幫你做到了最優(yōu),而且對于像萬年不變的 如標簽上靜態(tài)的class屬性,vue2.0 在重新渲染后做diff 的時候是不比較的,vue2.0比達到性能最大化的react 還要快的一個原因。
然后源碼在此,喜歡的記得給個star 哦
后續(xù),我會簡單聊聊,vue2.0的diff。

本文已被整理到了《Vue.js前端組件學習教程》,歡迎大家學習閱讀。

關于vue.js組件的教程,請大家點擊專題vue.js組件學習教程進行學習。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • 淺談vue生命周期共有幾個階段?分別是什么?

    淺談vue生命周期共有幾個階段?分別是什么?

    這篇文章主要介紹了淺談vue生命周期共有幾個階段?分別是什么?具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • vue如何在for循環(huán)中設置ref并獲取$refs

    vue如何在for循環(huán)中設置ref并獲取$refs

    眾所周知在寫循環(huán)的時候給循環(huán)中的數(shù)據(jù)定義ref以便再下面直接通過this.$ref.來訪問,下面這篇文章主要給大家介紹了關于vue如何在for循環(huán)中設置ref并獲取$refs的相關資料,需要的朋友可以參考下
    2022-12-12
  • vue項目移動端實現(xiàn)ip輸入框問題

    vue項目移動端實現(xiàn)ip輸入框問題

    這篇文章主要介紹了vue項目移動端實現(xiàn)ip輸入框問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03
  • Vuex之理解Store的用法

    Vuex之理解Store的用法

    本篇文章主要介紹了Vuex之理解Store的用法,Store類就是存儲數(shù)據(jù)和管理數(shù)據(jù)方法的倉庫,實現(xiàn)方式是將數(shù)據(jù)和方法已對象形式傳入其實例中
    2017-04-04
  • vue?退出登錄?清除localStorage的問題

    vue?退出登錄?清除localStorage的問題

    這篇文章主要介紹了vue?退出登錄?清除localStorage的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Vue中配置使用process.env詳解

    Vue中配置使用process.env詳解

    process.env?是?Node.js?中的一個環(huán)境對象,其中保存著系統(tǒng)的環(huán)境的變量信息,可使用?Node.js?命令行工具直接進行查看,這篇文章主要介紹了Vue中配置process.env詳解,需要的朋友可以參考下
    2023-03-03
  • Vue中添加手機驗證碼組件功能操作方法

    Vue中添加手機驗證碼組件功能操作方法

    組件是Vue.js最強大的功能之一。組件可以擴展HTML元素,封裝可重用的代碼。這篇文章主要介紹了VUE 中添加手機驗證碼組件,需要的朋友可以參考下
    2017-12-12
  • vue時間戳和時間的相互轉換方式

    vue時間戳和時間的相互轉換方式

    本文通過示例代碼介紹了vue時間戳和時間的相互轉換方式,通過場景分析介紹了vue3使用組合式api將時間戳格式轉換成時間格式(2023年09月28日 10:00),感興趣的朋友一起看看吧
    2023-12-12
  • vue3?emits事件使用示例詳解

    vue3?emits事件使用示例詳解

    這篇文章主要為大家介紹了vue3?emits事件使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • 淺談vue項目可以從哪些方面進行優(yōu)化

    淺談vue項目可以從哪些方面進行優(yōu)化

    本篇文章主要介紹了淺談vue項目可以從哪些方面進行優(yōu)化,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05

最新評論

武川县| 凯里市| 赫章县| 清水河县| 磐石市| 鸡泽县| 荃湾区| 开阳县| 壤塘县| 绿春县| 蚌埠市| 绍兴市| 合阳县| 叙永县| 通城县| 鸡泽县| 西平县| 门源| 娄底市| 米泉市| 来安县| 台南市| 宁化县| 丰顺县| 保德县| 霞浦县| 湘乡市| 旅游| 吴川市| 钟山县| 永善县| 赤城县| 扎兰屯市| 枣阳市| 浪卡子县| 肥西县| 靖安县| 梅河口市| 凤阳县| 望奎县| 大庆市|