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 方法
- 计算属性结果不需要 data 中定义,会以 return 返回值的形式返回
- 计算属性变量在 computed 中定义,被监听的属性(属性监听)在 data 中定义。
- 计算属性默认只有 get 来读取,手动修改计算属性时,会触发手写的 set 函数(不推荐)。
- 计算属性的值会被缓存,只有实例中相关依赖值改变时,才重新计算,性能好但不适合做异步请求。
- 计算属性是声明式的描述一个值依赖了其他值,依赖的值改变后重新计算结果更新 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>
注意
- 避免过度使用 Watch
- Watch 功能非常强大,但过度使用可能会导致代码变得难以理解和维护。在编写代码时,应仅在必要时使用 watch,并尽量使用计算属性或方法来处理简单的数据变化。
- 避免在 Watch 中进行异步操作
- 虽然 watch 允许执行异步操作,但要谨慎使用。因为 watch 是在数据变化时被调用的,频繁的异步操作可能导致性能问题或引发意外的行为。如果需要进行异步操作,最好使用 Vue 提供的异步方法(如 this.emit)来触发操作。
- 避免无限循环
- 当在 watch 中修改被监听的数据时,可能会导致无限循环的问题。
- 使用深度监听时的性能影响
- 使用 deep 选项来深度监听对象或数组的变化时,需要注意性能问题。深度监听会递归遍历所有属性或元素,因此在大型数据结构上使用时可能会影响性能。如果可能的话,可以针对具体的属性进行监听,而不是整个对象或数组。
- Watch 与计算属性的选择
- 在某些情况下,watch 和计算属性可以达到相同的效果。如果需要在数据变化时执行异步操作或有副作用时,应使用 watch。而如果仅需要根据数据进行简单的变换和计算,则更适合使用计算属性