vue-learning:18 - js - watch
watch
watch可以监听data和computed中值的变化。
watch在实例对象作用域中可以监听实例对象的数据,即var vm = new Vue(options)时作为配置对象属性传入。监听组件作用域内的数据,可以在组件的配置选项中传入。
基本语法形式:
- key: porp | obj.prop | arr[idx] | computed
- value: String | Function | Array | Object( handler | [deep | immediate])
- retuan: Function
var vm = new Vue({
    data: {
        a: 1,
        b: 2,
        c: 3,
        d: 4,
        e: {
            f: {
                g: 5
            }
        },
        h: [6,7,8]
    },
    computed: {
        i: function () {
            return a * 2
        }
    }
    watch: {
        // key: porp | obj.prop | arr[idx] | computed
        a: 'setA',
        'e.f.g': 'setA',
        e: {
            hander: function (newVal, oldVal) { /*do something */ },
            deep: true,
            }
        'h[1]': function (newVal, oldVal) { /*对数据项监听无效 */ }
        i: function (newVal, oldVal) { /*do something */ }
        // value: String | Function | Object | Array
        b: function (newVal, oldVal) {
            console.log('new: %s, old %s', newVal, oldVal)
        },
        c: {
            // handler is neccessary, deep and immediate are selectable
            hander: function (newVal, oldVal) { /*do something*/ },
            deep: true,
            immediate: true,
        },
        d: [
            function handle1(newVal, oldVal) { /*do something */ },
            {
                hander: function (newVal, oldVal) { /*do something */ },
                deep: true,
            }
        ],
    },
    methods: {
        setA: function (newVal, oldVal) {
            console.log('watch with data by method')
        }
    }
})
watch 的使用
- 监听data对象中的某个对象属性
- 监听回调函数可用的参数(newVale, oldVale)
- 立即执行一次回调immediate: true
- 深度监听deep: true
- 注销监听器unWatch()
监听对象某个属性
在data数据中我们会存入对象,有时只想监听当对象某一个属性值变化,此时可以在监听器的名称key中,使用.操作符,就像访问这个对象的属性一样。但是必须用字符串符号包裹。
new Vue({
    data: {
        e: {
            f: {
                g: 5
            }
        },
        h: [6,7,8]
    },
    watch: {
        'e.f.g': function () {
            // do something
        },
         'h[1]': function () { /*do something */ }
    }
})
深度监听 deep: true
默认情况下,监听某个对象时,只有当整个对象引用改变时才触发监听回调。但有时,我们期望这个对象的任意一个属性值发生变化都能触发监听回调。而不是监听整个对象引用变化或仅仅某一个属性值变化。此时我们可以在对象写法中传入deep:true的配置项
下例的例子,不管是e.n变化,还是e.f.g变化,又或是e.f或e变化都会触发回调。
new Vue({
    data: {
        e: {
            f: {
                g: 5
            },
            n: 6
        },
    },
    watch: {
        e: {
            handler: function { /* do something */},
            deep: true
        }
    }
})
立即执行一次回调immediate: true
watch最初绑定的时候,默认是不会马上执行的,要等到监听值变化后才响应。那如果想要初次绑定成功就马上执行一次回调,怎么办呢?我们需要传入immediate=true选项。
watch: {
        e: {
            handler: function { /* do something */},
            deep: true,
            immediate: true,
        }
    }
深度监听、立即执行都需要采用对象写法形式传入配置
监听回调函数可用的参数(newVale, oldVale)
当监听回调执行时,会被传入两个参数:
- 监听对象当前值(已改变的新值)
- 监听对象原来的值
 b: function (newVal, oldVal) {
            // 此时 this.b === newValue
        },
注销监听
为什么要注销 watcher?因为我们的组件是经常要被销毁的,比如我们跳一个路由,从一个页面跳到另外一个页面,那么原来的页面的 watcher 其实就没用了,这时候我们应该注销掉原来页面的 watch,不然的话可能会导致内置溢出。
好在我们平时 watch 都是写在组件的选项中的,他会随着组件的销毁而销毁。在beforeDestory阶段实例对象的所有属性和事件都被销毁。
const app = new Vue({
  template: '<div id="root">{{text}}</div>',
  data: {
    text: 0
  },
  watch: {
    text(newVal, oldVal){
      console.log(`${newVal} : ${oldVal}`);
    }
  }
});
但是,如果我们使用下面这样的方式写 watcher,那么就要手动注销了,这种注销其实也很简单。
const unWatch = app.$watch('text', (newVal, oldVal) => {
  console.log(`${newVal} : ${oldVal}`);
})
unWatch(); // 手动注销watch
app.$watch调用后会返回一个值,是函数,就是unWatch方法,只需要执行该方法即可完成注销当前监听器实例。
unWatch方法实际上是每个监听器实例生成时继承的方法teardown(),见上面监听器初始化最后阶段示例。
像上面这种监听单个数据项变化,往往不建议使用,因为有比监听器更好的方式来处理,即计算属性。如果需要监听数据赋值的变化,可以使用计算属性的setter是更好的方式。
但是监听器很适合处理异步操作。比如数据请求的回调、或页面路由的变化。
在实际项目中常常需要在全局应用实例作用域内监听各页面路由切换的变化。
var vm = new Vue({
    watch: {
        '$route.path': function (to, from) {
            // do something
        }
    }
})
比监听路由变化更好方式是使用路由守卫。比如上例中可以设置全局路由守卫beforeEach或afterEach
watch 的源码解读
在new Vue()创建一个实例的初始化阶段,即在生命周期函数beforeCreate和created之间会初始化传入的选项options中的一系列属性值。
watch的初始化阶段包括:
判断`value`是不是数组  --> 判断`value`是不是对象 --> 判断`key`是不是函数 --> 执行初始化
- 在initwatch函数中判断当前watch的value是不是数组,如果是数组就遍历数据取出每一个数组项传入createWathcher函数,如果不是数组,直接传入。
function initWatch(vm, watch) {
    for (const key in watch) {
        const handler = watch[key]
        if (Array.isArray(handler)) {
            for (let i = 0; i < hander.length; i++) {
                createWatcher(vm, key, handler[i])
            }
        } else {
            createWatcher(vm, key, handler)
        }
    }
}
- 在createWathcer函数中判断value是不是对象,如果是对象取出obj.handler属性值赋值给handler,其它属性值如deep/immediate作为整体赋值给options。如果是字符串则取method,如果是函数直接使用。
function createWatcher(vm, expOrFn, handler, options) {
    if (isPlainObject(handler)) {
        options = handler
        handler = handler.handler
    }
    if (typeof handler === 'string') {
        handler = vm[handler]
    }
    return vm.$watch(expOrFn, handler, options)
}
- 执行vm.$watch(expOrFn, handler, options)函数。会创建一个watcher实例,并且判断是否存在immediate,为真则立即执行一次回调。如果没有则当监听对象变化时再执行回调。
Vue.prototype.$watch = function (expOrFn, cb, options) {
    const vm = this
    options = options || {}
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
        cb.call(vm, watcher.value)
    }
    // 返回监听器实例的注销方法teardown
    return function unWatchFn() {
        watcher.teardown()
    }
}
- 在类Wathcer中除了定义wathcer的行为,还做了以下两件事:- 也会判断当前key是不是字符串还是函数,如果是函数,则取计算属性中函数直接赋值,如果是字符串,则解析该字符串获取监听的值。
- 判断options的属性中是否存在deep值,为真时则对监听对象实行深度监听,否则只监听当前对象。
 
- 也会判断当前
    export default class Watcher {
        constructor (vm, expOrFn, cb, options) {
            this.vm = vm
            this.cb = cb
            if (typeof expOrFn === 'function') {
                this.getter = expOrFn
            } else {
                this.getter = parsePath(expOrFn)
            }
            if (options) {
                this.deep = !!options.deep
            } else {
                this.deep = false
            }
        }
     }
vue-learning:18 - js - watch的更多相关文章
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十五 ║Vue基础:JS面向对象&字面量& this字
		缘起 书接上文<从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十四 ║ VUE 计划书 & 我的前后端开发简史>,昨天咱们说到了以我的经历说明的web开发经历的 ... 
- vue中config/index.js:配置的详细理解
		当我们需要和后台分离部署的时候,必须配置config/index.js: 用vue-cli 自动构建的目录里面 (环境变量及其基本变量的配置) var path = require('path') ... 
- vue进阶:vuex(数据池)
		非父子组件传值 vuex 一.非父子组件传值 基于父子组件通信与传值实现非父子组件传值的示例关键代码: <template> <div> <!-- 学员展示 --> ... 
- iosselect:一个js picker项目,在H5中实现IOS的select下拉框效果
		具体文档和demo可以访问github:https://github.com/zhoushengmufc/iosselect 移动端浏览器对于select的展示样式是不一致的,ios下是类似原生的pi ... 
- iosselect:一个js picker项目,在H5中实现IOS的下拉效果
		iosselect是在webapp下的一个picker组件,可以轻松实现各类选择器效果.比如地区选择 时间选择 日期选择等. 下面是一个地址选择器demo截图,可以访问:http://zhoushen ... 
- Atom 编辑器安装 linter-eslint 插件,并配置使其支持 vue 文件中的 js 格式校验
		安装方式有如下几种. 1.最常用的安装方式. # 进入atom插件文件夹 cd ~/.atom/packages/ # git clone 插件源文件 git clone https://github ... 
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十二║Vue实战:个人博客第一版(axios+router)
		前言 今天正式开始写代码了,之前铺垫了很多了,包括 6 篇基础文章,一篇正式环境搭建,就是为了今天做准备,想温习的小伙伴可以再看看<Vue 基础入门+详细的环境搭建>,内容很多,这里就暂时 ... 
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十三║Vue实战:Vuex 其实很简单
		前言 哈喽大家周五好,马上又是一个周末了,下周就是中秋了,下下周就是国庆啦,这里先祝福大家一个比一个假日嗨皮啦~~转眼我们的专题已经写了第 23 篇了,好几次都坚持不下去想要中断,不过每当看到群里的交 ... 
- 在被vue组件引用的 js 文件里获取组件实例this
		思路: 通过调用函数 把 组件实例this 传递 到 被应用的 js文件里 实例: 文件结构 在SendThis.vue 文件中引用 了modalConfig.js import modalConf ... 
随机推荐
- Directx11教程(50) 输出depth/stencil buffer的内容
			原文:Directx11教程(50) 输出depth/stencil buffer的内容 有时候,我们需要查看depth/stencil buffer的内容,比如上一章中,我们要查看sten ... 
- 2019-4-29-dotnet-core-通过-frp-发布自己的网站
			title author date CreateTime categories dotnet core 通过 frp 发布自己的网站 lindexi 2019-04-29 12:26:45 +0800 ... 
- md5小工具
			<?php$str = "123456";echo md5($str);?> 
- 数组map用法总结
			数组中,map方法,指的是是数组的映射. map基本语法如下:function回调支持3个参数,第1个是遍历的数组内容:第2个是对应的数组索引,第3个是数组本身. map方法的作用不难理解,“映射”嘛 ... 
- Codeforces 276D
			题目链接 这题真的体现了自己思维的不足,考虑问题只是考虑他的特殊性,却不能总结出它的一般性规律. 对于这题, 如果L == R , 那么结果为0. 否则, 我们只需要找到最高的某一位 (二进制数中的某 ... 
- 如何在“代码”视图中工作并充分利用 Dreamweaver 的编码功能。如 Emmet 缩写
			可通过多种方式在 Dreamweaver 中处理代码. 您可以使用“新建文档”对话框打开新的代码文件,然后开始键入您的代码. 在 Dreamweaver 中创建新的代码文件 键入时,会显示代码提示以帮 ... 
- POJ 2632 Crashing Robots (模拟 坐标调整)(fflush导致RE)
			题目链接:http://poj.org/problem?id=2632 先话说昨天顺利1Y之后,直到今天下午才再出题 TAT,真是刷题计划深似海,从此AC是路人- - 本来2632是道略微恶心点的模拟 ... 
- python 调用API时异常捕获的技巧
- oracle表复杂查询--创建数据库实例
			n 创建数据库有两种方法: 1)通过oracle提供的向导工具 2)我们可以用手工步骤直接创建 但我们创建完一个新的数据库实例后,在服务中就会有两个新的服务创建,这时,你根据实际需要去启动相应的数据 ... 
- php switch判断一个数所在的范围
			<?php header("content-type:text/html;charset=utf8"); $score=70; switch($score) { case $ ... 
