🎉 此文章是本人在学习的过程中总结的,如有错的地方欢迎评论区留言指正
响应式数据定义
在vue2中在data函数的return中定义响应数据:
data () {
return {
name: "zhangsan",
person: {
name: 'zhangsan',
age: 25
}
}
}
vue3中我们可以通过ref和reactive来定义响应数据:
const name = ref("zhangsan");
const person = reactive({
name: "zhangsan",
age: 25,
});
const greeting = "Hello " + name.value;
person.age++;
在模板中使用reactive定义的对象都需要通过 对象名[对象属性]的方式使用,写起来比较麻烦,此时可以通过toRefs将响应式对象转为普通对象,转换后的每个属性都指向原始对象对应属性的ref
<template>
<div class="homePage">
<p>第 {{ year }} 年</p>
<p>姓名: {{ person.name }}</p>
<p>年龄: {{ person.age }}</p>
<p>动物名称: {{ animalName }}</p>
<p>科种: {{ type }}</p>
</div>
</template>
<script>
import { defineComponent, reactive, ref, toRefs } from "vue";
export default defineComponent({
setup() {
const year = ref(2022);
const person = reactive({
name: "zhangsan",
age: 25,
});
const animal = reactive({
animalName: "青蛙",
type: "两栖动物",
});
return {
year,
person,
...toRefs(animal),
};
},
});
</script>
ref:
- 建议定义基本数据的时候使用
- 在 js 中通过
.value方法来获取数据的值
reactive:
- 建议定义复杂数据的时候使用
- 可使用
toRefs将其解包,在模板中使用他的属性
生命周期钩子
| 钩子 | 选项式 API | Hook inside setup |
|---|---|---|
| 创建前 | beforeCreate | Not needed* |
| 创建后 | created | Not needed* |
| 挂载前 | beforeMount | onBeforeMount |
| 挂载后 | mounted | onMounted |
| 更新前 | beforeUpdate | onBeforeUpdate |
| 更新后 | updated | onUpdated |
| 销毁前 | beforeDestory | beforeUnmounte |
| 销毁后 | destoryed | unmounted |
| keepAlive | activated | onActivated |
| keepAlive | deactivated | onDeactivated |
官网是这么解释的 Hook 中没有 beforeCreate 和 created的原因:
🏷️TIP
因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们.换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。
单文件组件样式特性
- 深度选择器
处于 scoped 样式中的选择器如果想要做更“深度”的选择,也即:影响到子组件,使用:deep()这个伪类
.list :deep(.el-button) {
...;
}
🏷️TIP
通过
v-html创建的DOM内容不会被作用域样式影响,但你仍然可以使用深度选择器来设置其样式。
- vue3 插槽选择器
一般情况下作用域的样式不会作用到插槽<slot/>里面的内容,可以使用:slotted来选择到插槽
:slotted(div) {
...;
}
- 全局样式
:global(.tab) {
color: blue;
}
- css module
可通过<style module>方式将 css 类作为$style对象暴露出来给组件使用,同时也支持自定义名称:<style module="molly">
<template>
<p :class="molly1.red">red</p>
</template>
<style module="molly1">
.red {
color: red;
}
</style>
<style module="molly2">
.red {
color: red;
}
</style>
- style v-bind
在vue3.2中增加了一个v-bind的特性,简单来说就是把我们script中的数据可以在style标签中使用,下面我们来写一个最简单的例子:
<template>
<p :class="molly.red">red</p>
</template>
<script>
import { ref } from "vue";
const color = ref("red");
</script>
<style module="molly">
.red {
color: v-bind(color);
}
</style>
watch 和 watchEffect
watch:
watch(source, callback, [options]);
参数说明:
- source: 可以支持
String、Object、Function、Array; 用于指定要侦听的响应式变量 - callback: 执行的回调函数
- options:支持
deep、immediate和flush选项。
1. 侦听单一源
const year = ref(2022);
const person = reactive({
name: "zhangsan",
age: 25,
});
watch(year, (newValue, oldValue) => {
// ...
});
watch(
() => person.age,
(newValue, oldValue) => {
// ...
},
{
deep: true,
immediate: true,
}
);
2. 侦听多个源
const year = ref(2022);
const person = reactive({
name: "zhangsan",
age: 25,
});
watch([year, () => person.age], ([newYear, newAge], [oldYear, oldAge]) => {
console.log(newYear, oldYear, newAge, oldAge);
});
watchEffect:
watchEffect(Function, [options]);
watchEffect会自动查找依赖,当副作用函数中的依赖改变时,执行该函数
const person = reactive({
name: "zhangsan",
age: 25,
});
watchEffect((onInvalidate) => {
console.log(person.age);
});
刷新页面,此时控制台输出25
const person = reactive({
name: "zhangsan",
age: 25,
});
watchEffect((onInvalidate) => {
console.log(person.age);
});
setTimeout(() => {
age.value++;
}, 1000);
此时控制台先输出25,一秒钟后打印26,由此我们可以知道,watchEffect会在页面初始化的时候执行一次,然后当副作用函数中的依赖发生变化时会重新执行该副作用函数。
停止监听:
一般情况下 watchEffect 会在组件卸载的时候自动停止,但是也可以显式调用返回值停止监听:
const person = reactive({
name: "zhangsan",
age: 25,
});
const stop = watchEffect((onInvalidate) => {
console.log(person.age);
});
stop(); // 停止监听
清除副作用:
watchEffect传入的函数可以接收一个 onInvalidate 函数作入参,用来清理上次回调的影响。当满足下述情况时,onInvalidate会被触发
- 副作用函数重新执行时(可以理解为当依赖值改变后先会执行
onInvalidate, 其次再执行副作用函数) - 监听被停止或者组件卸载时
🏷️TIP
依赖值改变,先执行副作用,再更新组件
const person = reactive({
name: "zhangsan",
age: 25,
});
const stop = watchEffect((onInvalidate) => {
console.log(person.age);
onInvalidate(() => {
console.log("执行了onInvalidate");
});
});
stop();
此时控制台依次输出25,执行了onInvalidate。
const person = reactive({
name: "zhangsan",
age: 25,
});
watchEffect((onInvalidate) => {
console.log(person.age);
onInvalidate(() => {
console.log("执行了onInvalidate");
});
});
此时控制台先输出25,一秒钟后执行了onInvalidate,26。
副作用刷新时机:
前面提到依赖值改变,先执行副作用,再更新组件,如果需要在组件更新后执行副作用函数,我们可以用到watchEffect的第二个参数
// 在组件更新后触发,这样你就可以访问更新的 DOM。
// 注意:这也将推迟副作用的初始运行,直到组件的首次渲染完成。
watchEffect(
() => {
/* ... */
},
{
flush: "post",
}
);
在Vue 3.2+的版本上更新的新的 api 可以替代上述操作,那就是watchPostEffect
watchEffect 的常用场景
ToDo...
<script setup>
<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 <script> 语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯
TypeScript声明props和抛出事件。 - 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
- 更好的
IDE类型推断性能 (减少语言服务器从代码中抽离类型的工作)。
- 在使用
<script setup>的时候,任何声明的变量、函数以及 import 的内容都能在模板直接使用
<template>
<div @click="handleClick">{{ color }}</div>
</template>
<script setup>
import { ref } from "vue";
const color = ref("red");
const handleClick = () => {
console.log("我点击了");
};
</script>
<style module="molly">
.red {
color: v-bind(color);
}
</style>
- 使用组件
<template>
<!-- <my-component></my-component> -->
<!-- 建议这么使用,保持一致性 -->
<MyComponent />
</template>
<script setup>
import MyComponent from "./MyComponent.vue";
</script>
- 递归组件
一个单文件组件可以通过它的文件名被其自己所引用。例如:名为 FooBar.vue 的组件可以在其模板中用 <FooBar/> 引用它自己。
请注意这种方式相比于 import 导入的组件优先级更低。如果有命名的 import 导入和组件的推断名冲突了,可以使用 import 别名导入:
<script setup>
import { FooBar as FooBarChild } from "./components";
</script>
defineProps和defineEmits
通过defineProps 和 defineEmits来声明 props 和 emits
<script setup>
const props = defineProps({
foo: {
type: String,
default: '001'
}
})
console.log(props.foo);
const emit = defineEmits(['change', 'delete']) // emit需要先声明
const handleClick = () => {
emit('change')
}
</script>
defineExpose
当使用 <script setup> 的时候外部组件是无法获取到组件的实例,该组件是默认关闭的,如果想从外部组件获取该组件实例的方法或者变量时,就会用到defineExpose
<script setup>
import { ref } from 'vue'
const a = 1;
const b = ref(2);
const test = () => {
console.log("test")
}
defineExpose({
a,
b,
test
})
</script>
- 自定义指令
🏷️TIP
必须以
vNameOfDirective的形式来命名本地自定义指令,以使得它们可以直接在模板中使用。
<template>
<div v-my-directive></div>
</template>
<script setup>
const vMyDirective = {
beforeMount: (el) => {
// 在元素上做些操作
},
};
</script>
<script setup>
// 导入的指令同样能够工作,并且能够通过重命名来使其符合命名规范
import { myDirective as vMyDirective } from "./MyDirective.js";
</script>
🪄 teleport 组件
teleport组件可以直接通过to属性将其中的 html 插入到指定的节点上,相比vue2就非常的银杏 😆
<teleport to="body"> </teleport>
vite 相关问题
关于在 vite 项目中使用类似 webpack 中 require.context 方法
import.meta.glob("./zh/*.js") 会转为下述代码:
const modules = {
./zh/login.js: () => import("/src/lang/zh/login.js")
}
import.meta.glob 该方法返回一个 promise 来接收
import.meta.globEager 直接.default 来接收