vue视图响应式更新详细介绍

目录
  • 概述
  • 思路
  • 第一步统一封装更新函数
  • 第二步监听并触发视图更新
    • 引入Dep管家
  • 实现下语法糖v-model

概述

前面两篇文章已经实现了对数据的变化的监听以及模板语法编译初始化,但是当数据变化时,视图还不能够跟随数据实时更新。本文就在之前的基础上介绍下视图响应式更新部分。

思路

统一封装更新函数

待数据发生改变时调用对应的更新函数

这里思考个问题:

在何时注册这个更新函数?

如何找到对应的更新函数?

第一步统一封装更新函数

基于上篇文章compile的部分,将数据初始化的部分统一封装起来。

compileText (n) {
    // 获取表达式
    // n.textContent = this.$vm[RegExp.$1]
    // n.textContent = this.$vm[RegExp.$1.trim()]
    this.update(n, RegExp.$1.trim(), 'text')
  }
  text (node, exp) {
    this.update(node, exp, 'text')
    // node.textContent = this.$vm[exp] || exp
  }
  html (node, exp) {
    this.update(node, exp, 'html')
    // node.innerHTML = this.$vm[exp]
  }

很容易写出update方法:

每个指令都有对应的[dir]Updater管理器,用于在公共的update函数里调用去在相应视图渲染数据。

  update (node, exp, dir) {
    // 第一步: 初始化值
    const fn = this[dir + 'Updater']
    fn && fn(node, this.$vm[exp])
  }
  textUpdater (node, val) {
    node.textContent = val
  }
  htmlUpdater (node, val) {
    node.innerHTML = val
  }

第二步监听并触发视图更新

分析可知,每个模板渲染初始化的过程都需要对数据进行监听,并注册监听函数,因此在上述的update函数中添加更新逻辑。

  update (node, exp, dir) {
    // 第一步: 初始化值
    const fn = this[dir + 'Updater']
    fn && fn(node, this.$vm[exp])
    // 第二步: 更新
    new Watcher(this.$vm, exp, val => {
      fn && fn(node, val)
    })
  }

创建Watcher类:

// 监听器:负责依赖更新
class Watcher {
  constructor (vm, key, updateFn) {
    this.vm = vm
    this.key = key
    this.updateFn = updateFn
  }
  update () {
    // 绑定作用域为this.vm,并且将this.vm[this.key]作为值传进去
    this.updateFn.call(this.vm, this.vm[this.key])
  }
}

此时我们已经完成了更新函数的功能,需要做的就是在数据发生改变的时候,主动调用下对应的update函数。

简单测试下:声明一个全局的watchers数组。在每次Watcher的构造函数中都往watchers中push一下,那么我们就可以再Object.defineProperty()的set方法中去遍历所有的watchers,调用update方法。

浅试一下:

const watchers = []
class Watcher {
  constructor (vm, key, updateFn) {
    this.vm = vm
    this.key = key
    this.updateFn = updateFn
    watchers.push(this)
  }
  update () {
    this.updateFn.call(this.vm, this.vm[this.key])
  }
}
function defineReactive (obj, key, val) {
  // 递归
  // val如果是个对象,就需要递归处理
  observe(val)
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    get () {
      Dep.target && dep.addDep(Dep.target)
      return val
    },
    set (newVal) {
      if (newVal !== val) {
        val = newVal
        // 新值如果是对象,仍然需要递归遍历处理
        observe(newVal)
        //暴力的写法,让一个人干事指挥所有人动起来(不管你需不需要更新,全给我更新一遍)
        watchers.forEach(watch => {
           watch.update()
        })
      }
    }
  })
}

此时页面视图已经可以根据数据的变而发生相应的更新了。

引入Dep管家

只触发需要更新的函数

上述的写法过于暴力,数据量一旦稍微大点就会严重影响性能。vue内部引入了Dep这个大管家的概念来进行依赖收集,统一管理所有的watcher。只让需要干活的watcher去update。

class Dep {
  constructor () {
    this.deps = []
  }
  addDep (dep) {
    this.deps.push(dep)
  }
  notify () {
    this.deps.forEach(dep => dep.update())
  }
}

每个data中的key对应一个dep就行,所以选择在Object.defineProperty的getter函数中进行依赖收集。在watcher中触发依赖收集

class Watcher {
  constructor (vm, key, updateFn) {
    this.vm = vm
    this.key = key
    this.updateFn = updateFn
    // 触发依赖收集,使用一个静态变量target去保存对应的Watcher
    Dep.target = this
    // 主动访问vm[key],触发一下getter
    this.vm[this.key]
    Dep.target = null
  }
  update () {
    // 绑定作用域为this.vm,并且将this.vm[this.key]作为值传进去
    this.updateFn.call(this.vm, this.vm[this.key])
  }
}

收集依赖,创建Dep实例

function defineReactive (obj, key, val) {
  observe(val)
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    get () {
      Dep.target && dep.addDep(Dep.target)
      return val
    },
    set (newVal) {
      if (newVal !== val) {
        val = newVal
        observe(newVal)
        dep.notify()
      }
    }
  })
}

至此,我们一个简版的Vue就实现了。这里还没有涉及到虚拟dom得概念,以后介绍。

实现下语法糖v-model

v-model虽然很像使用了双向数据绑定的 Angular 的 ng-model,但是 Vue 是单项数据流,v-model 只是语法糖而已。

// 最简形式,省略了value的显式绑定,省略了oninput的显式事件监听,是第二句代码的语法糖形式
<input v-model="sth" />
<input v-bind:value="sth" v-on:input="sth = $event.target.value" />
//第二句代码的简写形式
<input :value="sth" @input="sth = $event.target.value" />

分析一下其就是在内部实现了v-bind:value=“” 和@input。

 model (node, exp) {
   node.value = this.$vm[exp]
   node.addEventListener('input', (e) => {
     this.$vm[exp] = e.target.value
   })
 }

到此这篇关于vue视图响应式更新详细介绍的文章就介绍到这了,更多相关vue响应式更新内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 实现一个VUE响应式属性装饰器详析

    目录 前言 不使用任何的响应Api 使用 reactive 实现 使用 ref 实现 使用装饰器实现 实现Reactive装饰器 实现Watch装饰器 总结 前言 使用面向对象的开发思想难免会用到类,既然有了类,那就应该有实例,然而我们使用类的时候可能需要实例中的某个属性是vue的响应属性,也可能里面的某个方法也可以被vue的watch监听.我就开始琢磨如何通过 Composition API 来实现这个类属性装饰器 不使用任何的响应Api // TestReactive.ts export c

  • vue如何使用媒体查询实现响应式

    目录 使用媒体查询实现响应式的两种方式 前提 1.在每个组件中为其使用媒体查询 2.写n套页面,在使用这些页面的组件中进行一次媒体查询 vue中的媒体查询 语法 使用媒体查询实现响应式的两种方式 前提 依赖: sass,sass-loader 1.在每个组件中为其使用媒体查询 这种方法的有点是减少了重写不同终端同一组件的工作量,缺点是每个组件都要使用媒体查询,当一套页面组件不同时,需要进行组件的显示与隐藏(display:none!important),在不同终端区别不大的情况下建议使用这种方法

  • 关于vue2响应式缺陷的问题

    目录 vue2响应式缺陷 1.对象新增的属性没有响应式 2.数组的部分操作没有响应式 vue2与vue3的响应式原理 vue2响应式 vue3响应式雏形 vue3的响应式相较于vue2的优势 vue2响应式缺陷 响应式 : 数据改变 ==> 视图跟着改变 vue2响应式缺陷 1.对象新增的属性没有响应式 对象,新增属性b,修改b的值,值改变但视图并未更新 解决方案 : 使用vue提供的 api $set(对象,属性名,值) 效果如属性c 2.数组的部分操作没有响应式 数组中有7种操作有响应式 a

  • Vue 如何关掉响应式问题

    目录 Vue如何关掉响应式 例子 v-once Vue响应式的处理过程 Vue如何关掉响应式 大家都知道Vue有双向数据绑定 ,但是很少人知道怎样把它这个功能关掉 比如想要让某个值的改变不改变原有值 使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化. 例子 var obj = {   foo: 'bar' } Object.freeze(obj) new Vue({   el: '#app',   data: obj }) <div id

  • vue中响应式布局如何将字体大小改成自适应

    目录 响应式布局将字体大小改成自适应 vue文字大小自适应问题 响应式布局将字体大小改成自适应 1.在app.vue的生命周期函数中添加一段代码来设置页面的rem mounted: function() {     // 页面开始加载时修改font-size     var html = document.getElementsByTagName("html")[0];     var oWidth = document.body.clientWidth || document.doc

  • 深度解析 Vue3 的响应式机制

    目录 什么是响应式 响应式原理 定制响应式数据 Vueuse 工具包 什么是响应式 响应式一直都是 Vue 的特色功能之一:与之相比,JavaScript 里面的变量,是没有响应式这个概念的:你在学习 JavaScript 的时候首先被灌输的概念,就是代码是自上而下执行的: 我们看下面的代码,代码在执行后,打印输出的两次 double 的结果也都是 2:即使 我们修改了代码中 count 的值后,double 的值也不会有任何改变 let count = 1 let double = count

  • vue视图响应式更新详细介绍

    目录 概述 思路 第一步统一封装更新函数 第二步监听并触发视图更新 引入Dep管家 实现下语法糖v-model 概述 前面两篇文章已经实现了对数据的变化的监听以及模板语法编译初始化,但是当数据变化时,视图还不能够跟随数据实时更新.本文就在之前的基础上介绍下视图响应式更新部分. 思路 统一封装更新函数 待数据发生改变时调用对应的更新函数 这里思考个问题: 在何时注册这个更新函数? 如何找到对应的更新函数? 第一步统一封装更新函数 基于上篇文章compile的部分,将数据初始化的部分统一封装起来.

  • 原理深度解析Vue的响应式更新比React快

    前言 我们都知道 Vue 对于响应式属性的更新,只会精确更新依赖收集的当前组件,而不会递归的去更新子组件,这也是它性能强大的原因之一. 例子 举例来说 这样的一个组件: <template> <div> {{ msg }} <ChildComponent /> </div> </template> 我们在触发 this.msg = 'Hello, Changed~'的时候,会触发组件的更新,视图的重新渲染. 但是 <ChildCompone

  • vue响应式更新机制及不使用框架实现简单的数据双向绑定问题

    最近看到有些人说vue是双向数据绑定的,有些人说vue是单向数据流的,我认为这两种说法都是错误的,vue是一款具有响应式更新机制的框架,既可以实现单向数据流也可以实现数据的双向绑定. 2 单向数据流与数据双向绑定 单向数据流是指model中的数据发生改变时引起view的改变. 双向数据绑定是指model中的数据发生改变时view的改变,view的改变也会引起model的改变. //这个是单向数据流,改变这个input的value值并不能是data中的text属性发生改变. <input type

  • Vue数据变化后页面更新详细介绍

    首先会通过module.hot.accept监听文件变化,并传入该文件的渲染函数: module.hot.accept(/*! ./App.vue?vue&type=template&id=472cff63&scoped=true& */ "./App.vue?vue&type=template&id=472cff63&scoped=true&", __WEBPACK_OUTDATED_DEPENDENCIES__ =&g

  • Vue模拟响应式原理底层代码实现的示例

    目录 1.Vue.js功能: 2.Observer.js功能(数据劫持): 3.Compiler.js功能: 4.Dep.js功能: 5.Watcher.js功能: 整体分析Vue的基本结构如下图所示:(备注:完整代码github地址https://github.com/1512955040/MiniVue) 上图中,为我们模拟最小vue的整体结构,首先创建一个vue类型,它负责把data中的成员注入到vue实例中,并且转化成getter/setter,observer的作用是数据劫持,对dat

  • 浅谈Vue的响应式原理

    一.响应式的底层实现 1.Vue与MVVM Vue是一个 MVVM框架,其各层的对应关系如下 View层:在Vue中是绑定dom对象的HTML ViewModel层:在Vue中是实例的vm对象 Model层:在Vue中是data.computed.methods等中的数据 在 Model 层的数据变化时,View层会在ViewModel的作用下,实现自动更新 2.Vue的响应式原理 Vue响应式底层实现方法是 Object.defineProperty() 方法,该方法中存在一个getter和s

  • 通过图带你深入了解vue的响应式原理

    前言 如果自己去实现数据驱动的模式,如何解决一下几个问题: 通过什么手段去知道我的数据变了? 通过什么东西去同步更新视图? 数据劫持--obvserver 我们需要知道数据的获取和改变,数据劫持是最基础的手段.在Obeserver中,我们可以看到代码如下: Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { // ... }, set:

  • Vue 数据响应式相关总结

    在说数据响应式之前,我们要解决一个很重要的问题,那就是Vue到底对data做了什么?先从getter和setter说起,我们用那个他们来对虚拟的属性进行读写. getter和setter 有如下代码 let obj0 = { 姓: "高", 名: "圆圆", age: 18 }; // 需求一,得到姓名 let obj1 = { 姓: "高", 名: "圆圆", 姓名() { return this.姓 + this.名; }

  • Vue.js响应式数据的简单实现方法(一看就会)

    目录 引言 基本概念 副作用函数 响应式数据 响应式数据的基本实现 实现思路 初步实现尝试 完善响应系统 泛化副作用函数名 修复漏洞 总结 引言 在Vue.js之中,Vue会自动跟踪JavaScript状态变化并在状态发生改变时响应式地更新DOM,这就是Vue.js的两大核心功能之一——响应性,是每一个Vue.js框架使用者必须熟练掌握的的功能.而得益于Vue.js自身支持的声明式渲染,Vue.js的学习成本大大降低,就算是一个前端领域的小白,只要能看懂并简单使用基本的HTML.JavaScri

  • Vue自定义指令的使用详细介绍

    目录 1. 概述 2. 钩子函数 3. 自定义全局指令 4. 自定义局部指令 5. 使用自定义指令实现权限管理 6. 使用自定义指令实现表单验证 1. 概述 除了核心功能默认内置的指令,Vue也允许注册自定义指令.有的情况下,对普通 DOM 元素进行底层操作,这时候就会用到自定义指令绑定到元素上执行相关操作. 自定义指令分为: 全局指令和局部指令,当全局指令和局部指令同名时以局部指令为准. 局部指令:只对当前实例(或组件)生效 全局指令:对全部实例(或组件)都生效 2. 钩子函数 自定义指令常用

随机推荐