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

 更新时间:2022年09月16日 09:42:18   作者:super_wanan  
这篇文章主要介绍了Vue响应式原理,响应式就是当对象本身(对象的增删值)或者对象属性(重新赋值)发生了改变的时候,就会运行一些函数,最常见的示render函数

概述

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

思路

统一封装更新函数

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

这里思考个问题:

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

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

第一步统一封装更新函数

基于上篇文章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响应式更新内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue中子组件向父组件传值以及.sync修饰符详析

    Vue中子组件向父组件传值以及.sync修饰符详析

    .sync 修饰符所提供的功能,当一个子组件改变了一个prop的值时,这个变化也会同步到父组件中所绑定,下面这篇文章主要给大家介绍了关于Vue中子组件向父组件传值以及.sync修饰符的相关资料,需要的朋友可以参考下
    2022-11-11
  • vue如何关闭prettier警告warn

    vue如何关闭prettier警告warn

    这篇文章主要介绍了vue如何关闭prettier警告warn问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • Vue Element前端应用开发之前端API接口的封装

    Vue Element前端应用开发之前端API接口的封装

    对整个系统来说,一般会有很多业务对象,而每个业务对象的API接口又有很多。我们这个VUE+Element 前端应用就是针对ABP框架的业务对象,因此前端的业务对象接口也是比较统一的,那么可以考虑在前端中对后端API接口调用进行封装,引入ES6的方式进行前端API的抽象简化。
    2021-05-05
  • vue实现Input输入框模糊查询方法

    vue实现Input输入框模糊查询方法

    这篇文章主要为大家详细介绍了vue实现Input输入框模糊查询方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • Vue.prototype详解及使用方式

    Vue.prototype详解及使用方式

    这篇文章主要介绍了Vue.prototype详解及使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • Vue3 + Vue-PDF 实现PDF 文件在线预览实战

    Vue3 + Vue-PDF 实现PDF 文件在线预览实战

    这篇文章主要介绍了Vue3 + Vue-PDF 实现PDF 文件在线预览实战,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-06-06
  • vue 弹窗时 监听手机返回键关闭弹窗功能(页面不跳转)

    vue 弹窗时 监听手机返回键关闭弹窗功能(页面不跳转)

    这篇文章主要介绍了vue 弹窗时 监听手机返回键关闭弹窗功能,本文给大家介绍的非常详细,具有一定的参考借鉴价值(页面不跳转) ,需要的朋友可以参考下
    2019-05-05
  • 详解vue跨组件通信的几种方法

    详解vue跨组件通信的几种方法

    本篇文章主要介绍了详解vue跨组件通信的几种方法 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • 一定要知道的 25 个 Vue 技巧

    一定要知道的 25 个 Vue 技巧

    这篇文章主要给大家分享将 prop 限制为类型列表、默认内容和扩展点、使用引号观察嵌套值、知道何时使用 v-if、单作用域 slot 的简写、有条件地渲染slot等25 个Vue 技巧,下文是下相关资料,需要的朋友可以参考一下
    2021-11-11
  • vue组件中的数据传递方法

    vue组件中的数据传递方法

    这篇文章主要介绍了vue组件中的数据传递方法,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-05-05

最新评论