千家信息网

怎么理解vue2.0响应式架构

发表于:2024-11-20 作者:千家信息网编辑
千家信息网最后更新 2024年11月20日,本篇内容主要讲解"怎么理解vue2.0响应式架构",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"怎么理解vue2.0响应式架构"吧!讲data 下面所有属性
千家信息网最后更新 2024年11月20日怎么理解vue2.0响应式架构

本篇内容主要讲解"怎么理解vue2.0响应式架构",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"怎么理解vue2.0响应式架构"吧!

讲data 下面所有属性变为observable

来来来先看代码吧

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 源码分析之如何实现 observer 和 watcher可能就会很好理解,不过没关系,我们三言两语再说说,这段代码要实现的功能就是将

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

中data 里面所有的属性置于 observer,然后data里面的属性,比如 text 以改变,就引起_update()函数调用进而重新渲染,是怎样做到的呢,我们知道其实就是赋值的时候就要改变对吧,当我给data下面的text 赋值的时候 set 函数就会触发,这个时候 调用_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))

***步先说到这里,我们会发现一个问题,data中任何一个属性的值改变,都会引起

_update的触发进而重新渲染,属性这显然不够精准啊

第二步,详细阐述***步为什么不够精准

比如考虑下面代码

new Vue({      template: `        
name: {{name}}
age: {{age}}
`, data: { name: 'js', age: 24, height: 180 } }) setTimeout(function(){ demo.height = 181 }, 3000)

template里面只用到了data上的两个属性name和age,但是当我改变height的时候,用***步的代码,会不会触发重新渲染?会!,但其实不需要触发重新渲染,这就是问题所在!!

第三步,上述问题怎么解决

简单说说虚拟 DOM

首先,template***都是编译成render函数的(具体怎么做,就不展开说了,以后我会说的),然后render 函数执行完就会得到一个虚拟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,因为我们为了演示就不写代理的代码,所以这里为空             }           ]         }       ]     }

这就是 虚拟最简单虚拟DOM,tag是html 标签名,data 是包含诸如 class 和 style 这些标签上的属性,childen就是子节点,关于虚拟DOM就不展开说了。

回到开始的问题,也就是说,我得知道,render 函数里面依赖了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 函数里其实只依赖text,并没有依赖 name和 age,所以,我们只要text改变的时候

我们自动触发 render 函数 让它生成一个虚拟DOM就ok了(剩下的就是这个虚拟DOM和上个虚拟DOM做比对,然后操作真实DOM,只能以后再说了),那么我们正式考虑一下怎么做

第三步,'touch' 拿到依赖

回到最上面那张图,我们知道data上的属性设置defineReactive后,修改data 上的值会触发 set。

那么我们取data上值是会触发 get了。

对,我们可以在上面做做手脚,我们先执行一下render,我们看看data上哪些属性触发了get,我们岂不是就可以知道 render 会依赖data 上哪些变量了。

然后我么把这些变量做些手脚,每次这些变量变的时候,我们就触发render。

上面这些步骤简单用四个子概括就是 计算依赖。

(其实不仅是render,任何一个变量的改别,是因为别的变量改变引起,都可以用上述方法,也就是computed 和 watch 的原理,也是mobx的核心)

***步,

我们写一个依赖收集的类,每一个data 上的对象都有可能被render函数依赖,所以每个属性在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, {         // 省略       })     }

然后,当执行render 函数去'touch'依赖的时候,依赖到的变量get就会被执行,然后我们就可以把这个 render 函数加到 subs 里面去了。

当我们,set 的时候 我们就执行 notify 将所有的subs数组里的函数执行,其中就包含render 的执行。

至此就完成了整个图,好我们将所有的代码展示出来

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 因为我们得区分是,普通的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         },

到此,相信大家对"怎么理解vue2.0响应式架构"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

时候 函数 属性 代码 就是 变量 问题 架构 精准 没关系 不够 也就是 内容 情况 我要 手脚 方法 还是 代理 学习 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 明源软件开发公司 一城好物登录显示服务器维护中 怎么攻击传奇sf服务器 深圳邦企网络技术有限公司 数据库原理中主数据文件是什么 韩商言有没有打过网络安全大赛 在航天公司做软件开发 android搭建服务器 服务器机箱都有啥牌子的 rust选什么服务器 access数据库怎么替换字段 如何切换数据库和使用数据库 软件开发会被人工智能替代吗 超密集异构网络技术图文 笔记本配置成服务器的用处 北邮网络技术考研专业课 南铁院计算机网络技术 猫的服务器 注塑机上的服务器作用 黑暗之魂重置版登录服务器 ACCP软件开发课程 怎样查一个软件的服务器时间 数据库服务器支撑集群 国际软件开发技术基础实验 网络安全方面证书 数据库查询名字相同的人 网络安全法对于外企的影响 思维导图软件开发技术壁垒 搭建网站主机和服务器是什么区别 服务器产品型号对比
0