IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 【Vue】计算属性 computed & 侦听器 watch -> 正文阅读

[JavaScript知识库]【Vue】计算属性 computed & 侦听器 watch

计算属性 computed

我们都知道,{{}} 里面可以填写 JS 表达式

<div id="app"> {{ msg.split('').reverse().join('') }} </div>

当需要显示的数据比较复杂时,我们可以在计算属性 computed 中对数据进行加工:
( 计算属性中不能进行异步操作

getter

getter - 获取计算属性时调用

<div id="app"> {{ reMsg }} </div>
let vm = new Vue({
    el: '#app',
    data() {
        return {
            msg: "superman"
        }
    },
    // 设置 computed
    computed: {
        // 设置计算属性 reMsg
        reMsg: {
            // 设置 getter
            get() {
                console.log("getter");
                // this 指向 Vue 实例
                return [...this.msg].reverse().join('');
            }
        }
    }
});
  • 计算属性 reMsg 会成为 Vue 实例的属性;所以能直接在插值表达式 {{}} 中使用 reMsg

  • getter 的 this 指向当前 Vue 实例

    同时,this 会作为 getter 的第 1 参数传递进来

computed: {
    reMsg: {
        get(vm) {
            console.log("getter");
            // this === vm
            return [...vm.msg].reverse().join('');
        }
    }
}

计算属性的简写

  • 如果只写 getter,可以简写计算属性:
computed: {
    reMsg(vm) {
        console.log("getter");
        return [...vm.msg].reverse().join('');
    }
}

setter

setter - 修改计算属性时调用

  • setter 的 this 也是指向当前 Vue 实例
  • setter 的第 1 参数 val 为计算属性 reMsg 将要被修改成的值
computed: {
    reMsg: {
        get(vm) {
            console.log("getter");
            return [...vm.msg].reverse().join('');
        },
        // 设置 setter
        set(val) {
            console.log("setter");
            this.msg = [...val].reverse().join('');
        }
    }
}
  • Vue 知道 vm.reMsg 依赖于 vm.msg,因此当 vm.msg 发生改变时,vm.reMsg 也会更新 ( 但不会触发 setter )
    vm.reMsg 更新后,会调用 getter(因为数据被改变后,需要重新渲染页面,渲染页面需要获取更新后的数据)
  • 也就是说,setter 方法被调用后,getter 方法也会被调用
  • 所以,getter 被调用的情况有:① 初次渲染页面时、② 依赖的数据被改变时、③ 计算属性被改变时
  • 底层是通过 Object.defineProperty 方法设置的 getter & setter

computed & methods

  • 计算属性可以做到的,其实函数也可以做到:
<div id="app">
    <div> {{ reMsgFun() }} </div>
    <div> {{ reMsg }} </div>
</div>
let vm = new Vue({
    el: "#app",
    data: {
        msg: "superman",
    },
    methods: {
        reMsgFun() {
            return [...this.msg].reverse().join('');
        }
    },
    computed: {
        reMsg() {
            return [...this.msg].reverse().join('');
        }
    }
});
  • 不同的是,计算属性是基于它们的响应式依赖进行缓存的

就是说,computed 有缓存机制,获取的值会先存储在缓存区。如果相关响应式依赖没有发生改变,下一次获取时,就直接在缓存区获取,无需再调用 getter 方法。只在相关响应式依赖发生改变时,它们才会重新求值

如果通过调用方法来获取数据值,则每次获取都需要调用一次方法

<!-- 调用方法 -->
<div> {{ reMsgFun() }} </div>
<div> {{ reMsgFun() }} </div>
<div> {{ reMsgFun() }} </div>
<!-- 调用计算属性 -->
<div> {{ reMsg }} </div>
<div> {{ reMsg }} </div>
<div> {{ reMsg }} </div>
methods: {
    reMsgFun() {
        console.log("methods"); // 输出多次
        return [...this.msg].reverse().join('');
    }
},
computed: {
    reMsg() {
        console.log("getter"); // 只输出 1 次
        return [...this.msg].reverse().join('');
    }
}

侦听器 watch

  • 侦听器 watch 可以监听 Vue 实例的属性(data 里面的数据、computed 里面的计算属性…)

  • 当监听的数据被改变,侦听器 watch 就会执行一些操作

    用于在数据变化时,执行异步操作 / 比较复杂的操作(使用 computed 能完成简单地操作)

<!-- .lazy:脱标才会更新数据;不添加 .lazy 则实时更新数据 -->
用户名称:<input type="text" v-model.lazy='name'>
<span v-text="tip"></span>

handler

  • watch 有 handler 方法,当被监听的数据被改变时,就会调用 handler 方法

    handler 方法接收 2 个参数:修改后的值 newVal、修改前的值 oldVal

let vm = new Vue({
    el: "#app",
    data: {
        name: '',
        tip: ''
    },
    // 设置监听器 watch
    watch: {
        // 监听数据 name
        name: {
            // name 被改变时,调用 handler
            handler(newVal, oldVal) {
                console.log("newVal", newVal); // 获取修改后的值
                console.log("oldVal", oldVal); // 获取修改前的值
                // 检查昵称是否重复
                this.checkName(newVal);
                // 验证等待
                this.tip = "请等待正在发送请求…";
            }
        }
    },
    methods: {
        checkName(name) {
            // 异步操作
            setTimeout(() => {
                // 判断昵称是否重复
                if (name == 'admin') {
                    this.tip = '这个名字已经存在,请你换一个';
                } else {
                    this.tip = '用户名称可以使用';
                }
            }, 2000);
        }
    }
});

如果只设置 handler 方法,我们可以简写 watch 侦听器

watch: {
    name(newVal, oldVal) {
        console.log("newVal", newVal);
        console.log("oldVal", oldVal);
        this.checkName(newVal);
        this.tip = "请等待正在发送请求…";
    },
},

immediate

  • 配置项 immediate 默认为 false,表示被监听的数据被修改 则执行 handler 方法

    改为 true 则表示:立即执行一次 handler 方法先

watch: {
    name: {
        handler(newVal, oldVal) {
            console.log("newVal", newVal); // newVal 
            console.log("oldVal", oldVal); // oldVal undefined  -  一开始不存在旧的值,所以是 undefined
            this.checkName(newVal);
            this.tip = "请等待正在发送请求…";
        },
        immediate: true // 立即监听:不管数据有没有变化,watch 一上来就监听
    }
},

$watch()

我们也可以通过 Vue 实例调用侦听器 w a t c h : ‘ v m . watch:`vm. watchvm.watch( expOrFn, callback, [options] )`

vm.$watch("name", {
    immediate: true,
    handler(newVal, oldVal) {
        console.log("newVal", newVal);
        console.log("oldVal", oldVal);
        this.checkName(newVal);
        this.tip = "请等待正在发送请求…";
    }
});

vm.$watch 返回一个取消观察函数,用来停止监听,并触发回调:

let unwatch = vm.$watch('name', callback);
// 之后取消观察
unwatch();

$watch 的简写形式:

vm.$watch("name", function (newVal, oldVal) {
    console.log("newVal", newVal);
    console.log("oldVal", oldVal);
    this.checkName(newVal);
    this.tip = "请等待正在发送请求…";
});

注意:如果 callback 使用箭头函数,this 会指向 window(默认 this 指向 Vue 实例)

deep

默认情况下,如果我们监听的数据是一个对象,那么只有整个对象被修改时,才会触发回调函数
就是说,如果只是修改该对象的某个属性,是不会触发回调函数的

<!-- 修改的是对象数据的属性值 -->
用户名称:<input type="text" v-model.lazy='obj.name'>
let vm = new Vue({
    el: "#app",
    data: {
        obj: { // 对象数据
            name: "superman",
        }
    },
    watch: {
        obj: { // 监听对象
            handler(newVal, oldVal) {
                console.log(newVal, oldVal);
            }
        }
    }
});

当然,我们也可以监听对象的指定属性,那么修改该属性值时,也能触发回调函数

watch: {
    "obj.name": { // 监听对象属性
        handler(newVal, oldVal) {
            console.log(newVal, oldVal);
        }
    }
}

但是,如果对象的属性较多,我们需要监听多个属性时,这样写就会有大量的冗余代码

此时,我们可以设置 deep: true
如此,侦听器 watch 便可深度监听对象数据,就是说,即使修改的只是对象数据的属性值,也会触发回调函数

watch: {
    obj: { // 监听对象
        handler(newVal, oldVal) {
            console.log(newVal.name, oldVal.name);
        },
        deep: true // 深度监听
    }
}

同样的,如果我们监听的数据是一个数组,那么只有整个数组被修改时,才会触发回调函数
就是说,如果只是修改该数组的某一项,是不会触发回调函数的

此时,我们可以设置属性 deep: true 。如此,侦听器 watch 便可深度监听数组数据
就是说,即使修改的只是数组数据的某一项,也会触发回调函数

watch & computed

computed 能完成的功能,watch 都能完成;watch 能完成的功能,computed 不一定能完成
watch 可以执行异步操作;computed 不能执行异步操作

因为 computed 的数据,是通过 return 语句返回的
假设将 return 语句包裹在定时器中,返回值将 return 给定时器里面的箭头函数,我们将获取不到返回值

注意 2 个原则:

  • Vue 管理的函数,this 指向 Vue 实例,最好写成普通函数,否则 this 将丢失,而指向 window
  • 异步操作的回调函数,不被 Vue 管理,最好写成箭头函数,这样 this 才能指向 Vue 实例
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:40:43  更:2022-03-21 20:42:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:23:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码