VUE分享03-高频API(上)

42 阅读5分钟

ref

ref 被用来给 DOM 元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的 DOM 元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例。 注意:只要想在 Vue 中直接操作 DOM 元素,就必须用 ref 属性进行注册 (因为不推荐原生的方式操作 DOM)

在 DOM 上使用 ref

ref 加在普通的元素上,用 this.ref.xxx 获取到的是 dom 元素

<template>
    <div>
        <div ref="divRef">第一个一个普通的div</div>
        <div ref="divRef2">第二个个普通的div</div>
        <button @click="getRef">按钮</button>
    </div>
</template>
<script>
export default {
    methods: {
        getRef() {
            /**
             * 使用this.$refs
             * 拿到当前组件里所有的ref对象
             */
            console.log(this.$refs);
        },
    },
};
</script>
在组件上使用 ref

ref 加在子组件上,用 this.ref.xxx 获取到的是组件实例,可以使用组件的所有方法

<template>
    <div>
        <QfNum ref="numRef"></QfNum>
        <button @click="add">子组件+1</button>
    </div>
</template>
<script>
import QfNum from "./qf-num.vue";
export default {
    components: {
        QfNum,
    },

    methods: {
        add() {
            // 可以直接获取到对应的组件状态和方法
            this.$refs.numRef.add();
            console.log(this.$refs.numRef.num);
        },
    },
};
</script>

生命周期

  • 世间万物都有自己生命周期,vue 也有这一特点,vue 的生命周期可以简单分为四个阶段:创建阶段,挂载阶段,更新阶段,销毁阶段。
  • 每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。

创建阶段

beforeCreate

在组件实例创建之前调用,此时组件的数据观测、事件监听和模板编译尚未开始,因此无法访问到组件的响应式数据、计算属性、方法等

<template>
    <div>生命周期</div>
</template>
<script>
export default {
    beforeCreate() {
        console.log("创建前");
    },
};
</script>
created

在组件实例创建之后调用,此时组件的数据观测、事件监听和模板编译已经完成,可以访问到组件的响应式数据、计算属性、方法等,但是还没有挂载到 DOM 上,因此无法访问到组件的元素或子组件。常用于发送网络请求

<template>
    <div>生命周期</div>
</template>
<script>
export default {
    created() {
        console.log("创建后");
    },
};
</script>

挂载阶段

beforeMount

在组件挂载到 DOM 之前调用,此时组件的虚拟 DOM 已经创建,但是还没有插入到父容器中,可以对虚拟 DOM 进行一些操作或修改

<template>
    <div>生命周期</div>
</template>
<script>
export default {
    beforeMount() {
        console.log("挂载前");
    },
};
</script>
mounted

在组件挂载到 DOM 之后调用,此时组件的虚拟 DOM 已经插入到父容器中,并且生成了真实的 DOM 节点,可以访问到组件的元素或子组件,并且可以执行一些需要 DOM 的操作。

<template>
    <div>生命周期</div>
</template>
<script>
export default {
    mounted() {
        console.log("挂载后");
    },
};
</script>

更新阶段

beforeUpdate

在组件更新之前调用,此时组件的数据已经发生变化,但是还没有更新到 DOM 上,可以在这个钩子中获取更新前的状态,并进行一些比较或逻辑处理。

<template>
    <div>生命周期</div>
</template>
<script>
export default {
    beforeUpdate() {
        console.log("更新前");
    },
};
</script>
update

在组件更新之后调用,此时组件的数据已经更新到 DOM 上,并且完成了重新渲染,可以在这个钩子中获取更新后的状态,并进行一些后续操作或效果处理。不能在这个生命周期里做响应式操作,否则会死循环

<template>
    <div>生命周期</div>
</template>
<script>
export default {
    updated() {
        console.log("更新后");
    },
};
</script>

卸载阶段

beforeUnmount

在组件卸载之前调用,此时组件还处于可用状态,可以在这个钩子中执行一些清理操作,如移除事件监听器、取消网络请求、停止定时器等。

<template>
    <div>生命周期</div>
</template>
<script>
export default {
    beforeUnmount() {
        console.log("卸载前");
    },
};
</script>
unmounted

在组件卸载之后调用,此时组件已经从 DOM 中移除,并且停止了所有的响应式效果和事件监听,无法再访问到组件的任何属性或方法。

<template>
    <div>生命周期</div>
</template>
<script>
export default {
    unmounted() {
        console.log("卸载后");
    },
};
</script>

forceUpdate

迫使 Vue 实例重新(rander)渲染虚拟 DOM,注意并不是重新加载组件。结合 vue 的生命周期,调用 $forceUpdate 后只会触发 beforeUpdate 和 updated 这两个钩子函数,不会触发其他的钩子函数。它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件

  • 基本使用
    • 把当在 data 里没有显示的声明一个对象的属性,而是之后给该对象添加属性,这种情况 vue 是检测不到数据变化的,可以使用$forceUpdate()
  • 注意: 官方说如果你现在的场景需要用 forceUpdate 方法, 那么 99% 是你的操作有问题
<template>
    <div>{{ name }} <button @click="update">改变名字为李四</button></div>
</template>

<script>
export default {
    methods: {
        update() {
            this.name = "李四";
            // 如果不加forceUpdate 页面不会渲染
            this.$forceUpdate();
        },
    },
};
</script>

计算属性 computed

  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用 computed
  • computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于 data 中声明过或者父组件传递的 props 中的数据通过计算得到的值。
  • 如果 computed 属性属性值是函数,那么默认会走 get 方法;函数的返回值就是属性的属性值;在 computed 中的,属性都有一个 get 和一个 set 方法,当数据变化时,调用 set 方法
  1. 计算属性结果不需要 data 中定义,会以 return 返回值的形式返回
  2. 计算属性变量在 computed 中定义,被监听的属性(属性监听)在 data 中定义。
  3. 计算属性默认只有 get 来读取,手动修改计算属性时,会触发手写的 set 函数(不推荐)。
  4. 计算属性的值会被缓存,只有实例中相关依赖值改变时,才重新计算,性能好但不适合做异步请求。
  5. 计算属性是声明式的描述一个值依赖了其他值,依赖的值改变后重新计算结果更新 DOM。属性监听的是定义的变量,当定义的值发生变化时,执行相对应的函数
基本使用
<template>
    <div>
        计算属性
        <!-- 可以直接使用 -->
        {{ arrLength }}
    </div>
</template>
<script>
export default {
    data() {
        return {
            arr: [1, 2, 3, 4],
        };
    },

    /**
     * 计算属性放在computed对象里面
     * 里面存放的是方法 方法的名字就是这个计算属性的名字
     * 方法的返回值就是计算属性的返回值
     * 只有内部数据变化才会重新计算
     */
    computed: {
        /**
         * 计算arr数组的length
         */
        arrLength() {
            // return的值就是arrLength的值
            return this.arr.length;
        },
    },
};
</script>
修改计算属性 (一般来说不需要修改)
<template>
    <div>
        计算属性
        <!-- 可以直接使用 -->
        {{ name }}
        <button @click="updateName">修改名字</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            firstName: "张",
            lastName: "三",
        };
    },

    /**
     * 可写计算属性
     * 计算属性默认是只读的。
     * 当你尝试修改一个计算属性时,你会收到一个运行时警告。
     * 只在某些特殊场景中你可能才需要用到“可写”的属性,
     * 你可以通过同时提供 getter 和 setter 来创建
     * 不推荐直接使用
     */
    computed: {
        /**
         * 计算arr数组的length
         */
        name: {
            // getter
            get() {
                return this.firstName + " " + this.lastName;
            },
            // setter
            set(newValue) {
                // 注意:我们这里使用的是解构赋值语法
                [this.firstName, this.lastName] = newValue.split(" ");
            },
        },
    },

    methods: {
        updateName() {
            this.name = "李 四";
        },
    },
};
</script>

侦听器(观察者) watch

在 Vue 中,watch 是一个用于监听数据变化的功能。它可以监听一个或多个数据,并在数据发生变化时执行相应的操作。当我们需要在数据变化时执行异步操作、计算属性或执行一些副作用时,watch 就派上了用场啦。

基本使用

观察 num 变化,每次变化都会触发 hello

<template>
    <div>
        {{ num }}
        <button @click="num += 1">+1</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            num: 1,
        };
    },

    watch: {
        /**
         * 在watch中,要观察那个属性就直接把对应属性定义乘方法
         *
         * 接收两个参数
         * newVal 更新后的值
         * oldVal 更新前的值
         */
        num(newVal, oldVal) {
            console.log(newVal, oldVal, "哈喽");
        },
    },
};
</script>

immediate 立即触发

立即触发监听 默认情况下,watch 在初始化时不会立即执行。如果我们希望在初始数据加载后立即触发监听,可以使用 immediate 选项

<template>
    <div>
        {{ num }}
        <button @click="num += 1">+1</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            num: 1,
        };
    },

    watch: {
        /**
         * 把要观察的属性写成一个对象
         * 每次改变 都会触发 handler 方法 注意:只能叫handler
         * immediate 立即执行
         */
        num: {
            handler(newVal, oldVal) {
                console.log(newVal, oldVal, "哈喽");
            },
            immediate: true,
        },
    },
};
</script>

deep 深度监听

watch 默认只能进行浅层次监听,如果数据非常复杂,则需要进行深度监听

<template>
    <div>
        <!-- {{ num }} -->
        <!-- <button @click="num += 1">+1</button> -->
        <button @click="update">改变</button>
        {{ arr }}
    </div>
</template>

<script>
export default {
    data() {
        return {
            arr: [{ name: "张三", child: [{ name: "张三儿子" }] }],
        };
    },

    watch: {
        arr: {
            handler() {
                console.log("改变了吗");
            },
            deep: true,
        },
    },

    methods: {
        update() {
            this.arr[0].child[0].name = "李四";
        },
    },
};
</script>

注意

  1. 避免过度使用 Watch
    • Watch 功能非常强大,但过度使用可能会导致代码变得难以理解和维护。在编写代码时,应仅在必要时使用 watch,并尽量使用计算属性或方法来处理简单的数据变化。
  2. 避免在 Watch 中进行异步操作
    • 虽然 watch 允许执行异步操作,但要谨慎使用。因为 watch 是在数据变化时被调用的,频繁的异步操作可能导致性能问题或引发意外的行为。如果需要进行异步操作,最好使用 Vue 提供的异步方法(如 this.nextTickthis.nextTick或this.emit)来触发操作。
  3. 避免无限循环
    • 当在 watch 中修改被监听的数据时,可能会导致无限循环的问题。
  4. 使用深度监听时的性能影响
    • 使用 deep 选项来深度监听对象或数组的变化时,需要注意性能问题。深度监听会递归遍历所有属性或元素,因此在大型数据结构上使用时可能会影响性能。如果可能的话,可以针对具体的属性进行监听,而不是整个对象或数组。
  5. Watch 与计算属性的选择
    • 在某些情况下,watch 和计算属性可以达到相同的效果。如果需要在数据变化时执行异步操作或有副作用时,应使用 watch。而如果仅需要根据数据进行简单的变换和计算,则更适合使用计算属性