Vue.js 中的 $watch使用方法

发布时间 - 2026-01-11 01:20:29    点击率:

这两天学习了Vue.js 中的 $watch这个地方知识点挺多的,而且很重要,所以,今天添加一点小笔记。

github 源码 

Observer, Watcher, vm 可谓 Vue 中比较重要的部分,检测数据变动后视图更新的重要环节。下面我们来看看 如何实现一个简单的 $watch 功能,当然Vue 中使用了很多优化手段,在本文中暂不一一讨论。

例子:

// 创建 vm
let vm = new Vue({
 data: 'a'
})

// 键路径
vm.$watch('a.b.c', function () {
 // 做点什么
})

先阐明在这个 demo 以及Vue 中,它们的关系:

vm 调用 $watch 后,首先调用 observe 函数 创建 Observer 实例观察数据,Observer 又创建 Dep , Dep 用来维护订阅者。然后创建 Watcher 实例提供 update 函数。一旦数据变动,就层层执行回调函数。


Observer和observe

递归调用 observe 函数创建 Observer。在创建 Observer 的过程中,使用 Object.defineProperty() 函数为其添加 get set 函数, 并创建 Dep 实例。

export function observe (val) {
 if (!val || typeof val !== 'object') {
  return
 }
 return new Observer(val)
}
function defineReactive (obj, key, val) {
 var dep = new Dep()

 var property = Object.getOwnPropertyDescriptor(obj, key)
 // 是否允许修改
 if (property && property.configurable === false) {
  return
 }

 // 获取定义好的 get set 函数
 var getter = property && property.get
 var setter = property && property.set

 var childOb = observe(val)
 Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: () => {
   var value = getter ? getter.call(obj) : val
   // 说明是 Watcher 初始化时获取的, 就添加订阅者
   if (Dep.target) {
    dep.depend()
    if (childOb) {
     childOb.dep.depend()
    }
    // if isArray do some....
   }
   return value
  },
  set: (newVal) => {
   var value = getter ? getter.call(obj) : val
   if (value === newVal) {
    return
   }
   if (setter) {
    setter.call(obj, newVal)
   } else {
    val = newVal
   }
   childOb = observe(newVal)
   dep.notify()
  }
 })
}

你可能会疑问 Dep.target 是个什么鬼?😳

答案是:Watcher, 我们接下来看

Dep

export default function Dep () {
 this.subs = []
}

// 就是你!!~
Dep.target = null 

// 添加订阅者
Dep.prototype.addSub = function (sub) {
 this.subs.push(sub)
}

// 添加依赖
Dep.prototype.depend = function () {
 Dep.target.addDep(this)
}

// 通知订阅者:要更新啦~
Dep.prototype.notify = function () {
 this.subs.forEach(sub => sub.update())
}

Watcher

为了给每个数据添加订阅者,我们想到的办法是在数据的 get 函数中, 但是 get 函数会调用很多次呀~。。。 肿么办?那就给 Dep 添加个参数 target

export default function Watcher (vm, expOrFn, cb) {
 this.cb = cb
 this.vm = vm
 this.expOrFn = expOrFn
 this.value = this.get()
}

Watcher.prototype.get = function () {
 Dep.target = this
 const value = this.vm._data[this.expOrFn]
 // 此时 target 有值,此时执行到了上面的 defineReactive 函数中 get 函数。就添加订阅者
 Dep.target = null
 // 为了不重复添加 就设置为 null
 return value
}

Vue Instance

在 Vue Instance 做得最多的事情就是初始化 State, 添加函数等等。

// Vue 实例
export default function Vue(options) {
 this.$options = options
 this._initState()
}

// 初始化State
Vue.prototype._initState = function () {
 let data = this._data = this.$options.data
 Object.keys(data).forEach(key => this._proxy(key))
 observe(data, this)
}

// $watch 函数,
Vue.prototype.$watch = function (expOrFn, fn, options) {
 new Watcher(this, expOrFn, fn)
}

总结

至此,我们已经实现了一个简单的 $watch 函数, Object.defineProperty() 函数可谓是举足轻重, 因此不支持该函数的浏览器, Vue 均不支持。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# vue  # watch  # watch的用法  # 实例详解vue.js浅度监听和深度监听及watch用法  # vue.js使用watch监听路由变化的方法  # Vue.js中关于侦听器(watch)的高级用法示例  # 深入理解vue.js中$watch的oldvalue与newValue  # Vue.Js中的$watch()方法总结  # Vue.js计算属性computed与watch(5)  # vue.js中$watch的用法示例  # Vue.js 的 watch函数基本用法  # 递归  # 是个  # 是在  # 在这个  # 最多  # 很重要  # 来看看  # 做得  # 为其  # 举足轻重  # 不支持  # 设置为  # 这两天  # 就给  # 均不  # 回调  # 暂不  # 大家多多  # 很多次  # 如何实现 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何用AI帮你把自己的生活经历写成一个有趣的故事?  5种Android数据存储方式汇总  Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】  JS经典正则表达式笔试题汇总  如何在服务器上三步完成建站并提升流量?  Laravel如何处理跨站请求伪造(CSRF)保护_Laravel表单安全机制与令牌校验  制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?  网站设计制作书签怎么做,怎样将网页添加到书签/主页书签/桌面?  三星、SK海力士获美批准:可向中国出口芯片制造设备  香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南  佛山企业网站制作公司有哪些,沟通100网上服务官网?  微信推文制作网站有哪些,怎么做微信推文,急?  手机网站制作与建设方案,手机网站如何建设?  Laravel Artisan命令怎么自定义_创建自己的Laravel命令行工具完全指南  如何在IIS中新建站点并配置端口与IP地址?  为什么要用作用域操作符_php中访问类常量与静态属性的优势【解答】  微信h5制作网站有哪些,免费微信H5页面制作工具?  如何用低价快速搭建高质量网站?  网站制作报价单模板图片,小松挖机官方网站报价?  轻松掌握MySQL函数中的last_insert_id()  Laravel怎么使用artisan命令缓存配置和视图  高防服务器如何保障网站安全无虞?  打开php文件提示内存不足_怎么调整php内存限制【解决方案】  Laravel如何处理CORS跨域问题_Laravel项目CORS配置与解决方案  Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)  香港服务器建站指南:外贸独立站搭建与跨境电商配置流程  Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】  JavaScript如何实现继承_有哪些常用方法  网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?  Laravel如何实现图片防盗链功能_Laravel中间件验证Referer来源请求【方案】  Laravel API资源(Resource)怎么用_格式化Laravel API响应的最佳实践  青岛网站建设如何选择本地服务器?  Laravel Session怎么存储_Laravel Session驱动配置详解  如何快速查询域名建站关键信息?  高端建站如何打造兼具美学与转化的品牌官网?  Laravel distinct去重查询_Laravel Eloquent去重方法  弹幕视频网站制作教程下载,弹幕视频网站是什么意思?  Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录  Claude怎样写约束型提示词_Claude约束提示词写法【教程】  百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏  Laravel用户密码怎么加密_Laravel Hash门面使用教程  浏览器如何快速切换搜索引擎_在地址栏使用不同搜索引擎【搜索】  网站建设整体流程解析,建站其实很容易!  Laravel怎么实现搜索高亮功能_Laravel结合Scout与Algolia全文检索【实战】  如何在Windows环境下新建FTP站点并设置权限?  如何在宝塔面板中创建新站点?  html文件怎么打开证书错误_https协议的html打开提示不安全【指南】  Android自定义listview布局实现上拉加载下拉刷新功能  Laravel如何实现URL美化Slug功能_Laravel使用eloquent-sluggable生成别名【方法】  ,网页ppt怎么弄成自己的ppt?