二.Vue3.x(基础)
前言
补充一些官方文档中没有的,但是实际开发中最好需要了解的内容
1.Teleport
把节点挂载到指定 id 下
vue
<template>
<teleport to="#app">
<div class="modal">
hello world
</div>
</teleport>
</template>
2.Suspense
展示加载状态
vue
<Suspense>
<template >
<!-- 异步组件 -->
<Suspended-component />
</template>
<template #fallback>
<!-- 异步组件加载完毕前展示的内容 -->
Loading...
</template>
</Suspense>
3.reactive
- 对象或数组(原理是Proxy)
4.ref
基本数据类型(原理是Object.defienProperty)
对象或数组(原理是Proxy)
ref相关
ref
- 接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个
.value
property,指向该内部值。 - 如果将对象分配为 ref 值,则它将被
reactive
函数处理为深层的响应式对象。 - template 内使用 ref 对象,会自动解包
- 接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个
unref
- 如果参数是一个 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 的语法糖函数。
- 基于 ref 一个 {} , {} 会被 reactive 二次处理,unref(ref({})) 返回的是响应式的 {}
toRef
- 可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。
- 用人话讲就是为 源响应式对象(toRef的第一个参数) 上的某个 property 新创建一个 ref
toRefs
- 将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。
- 主要功能:当从组合式函数 (.js) 返回响应式对象时,用toRefs就可以在不丢失响应性的情况下对返回的对象进行解构/展开。
- toRef 是转单个, toRefs全转。
- 原理:toRefs 会将 reactive 生成的对象的根级属性全都用 ref 转成 ref 对象,然后解构出来的都是 ref 对象,从而不丢失响应式
isRef
- 检查值是否为一个 ref 对象
customRef
- 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。
- 自定义 ref 的get set,用于某些数据变更操作的额外功能封装,可以理解为computed 指令类似的功能之类的
shallowRef
- 如字面意思,浅响应式,如果传入的是基本类型跟 ref 没区别。
- 如果传入的是引用类型,.value值将不会是响应式的数据,ref的 value 属性则会是响应式的
triggerRef
- 手动执行与 shallowRef 关联的任何作用 (effect)。
- 简单讲就是配合 shallowRef 用的,并且 shallowRef 传入的是个引用类型
5.slot
- 公开子组件插槽
vue
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}"></slot>
</template>
6.keycode
键盘绑定 keycode 无效,绑定别名有效
vue
<!-- 无效 -->
<input v-on:keyup.13="submit" />
<!-- 有效 -->
<input v-on:keyup.enter="submit" />
移除 on off 和 $once 方法,使用 mitt 代替
filters 被移除,使用 methods 或者 computed 代替
7. 全局挂载
js
app.config.globalProperties.http = function () {}
app.config.globalProperties.$log = window.console.log
vue
<template>
<div class="card-wrap">
<el-button type="primary" @click="onClick">点击</el-button>
<span class="card">{{ title }}</span>
</div>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue'
const title = ref('abc')
const app = getCurrentInstance()
const onClick = () => {
app.appContext.config.globalProperties.$message.success('消息')
}
</script>
8.render
js
import { h} from 'vue'
render() {
return h('div')
}
9.定义异步组件
js
import { defineAsyncComponent } from "vue"
// 方式一
const asyncPageWithOptions = defineAsyncComponent({
loader: () => import("./NextPage.vue"),
delay: 200,
timeout: 3000,
error: ErrorComponent,
loading: LoadingComponent,
})
// 方式二
const asyncComponent = defineAsyncComponent(
() =>
new Promise((resolve, reject) => {
/* ... */
})
)
// 方式三
const com = ref("")
const getComponent = () => {
import("./NextPage.vue").then(component){
com = component.default
}
}
10.指令生命周期修改
js
Vue.directive("highlight", {
beforeMount(el, binding, vnode) {
// 对应bind
el.style.background = binding.value
},
mounted() {}, // 对应inserted
beforeUpdate() {}, // 新增
updated() {}, // 对应update
beforeUnmount() {}, // 新增
unmounted() {}, // 对应unbind
})
14. watch 方法
- props数据
js
watch(
() => data,
(newVal,oldVal) => {
...
},
{
deep: true,
immediate:true
}
)
- ref数据
js
watch(
data,
(newVal,oldVal) => {
...
},
{
deep: true,
immediate:true
}
)
js
watch(
[data1,data2],
([newVal1,newVal2],[oldVal1,oldVal2]) => {
...
},
{
deep: true,
immediate:true
}
)
- reactive 数据
js
watch(
()=>data,
(newVal,oldVal) => {
...
},
{
deep: true,
immediate:true
}
)
computed
- 选项式写法 支持一个对象传入get函数和set函数自定义操作
js
const name = computed<string>({
get() {
return firstName.value + '-' + lastName.value
},
set(newVal) {
console.log(newVal);
[firstName.value, lastName.value] = newVal.split('-') // 解构赋值
}
})
- 函数式写法 只能支持一个getter函数,不允许修改值
js
const name = computed(() => {
return firstName.value + '-' + lastName.value
})
15.onRenderTriggered
16.指令 v-memo
可以缓存 html 模板,比如 v-for 列表不会变化的就缓存,简单说就是用内存换时间
17.v-model 语法糖
在自定义组件上使用v-model
vue
<template>
<div>
<el-button @click="increase(-1)">减 1</el-button>
<span style="color: red; padding: 6px">{{ propsMessage }}</span>
<el-button @click="increase(1)">加 1</el-button>
</div>
</template>
<script>
import { defineComponent, computed } from "vue"
export default defineComponent({
name: "InputNumber",
props: {
modelValue: {
type: String,
default: "0",
},
},
emits: ["update:modelValue"],
setup(props, { emit }) {
const propsMessage = computed({
get: () => {
return props.modelValue
},
set: (val) => {
emit("update:modelValue", val)
},
})
const increase = (val) => {
propsMessage.value += val
}
return {
propsMessage,
increase,
}
},
})
</script>
props 一般不能再组件内修改,它是通过父级修改的,因此实现 v-model 一般会有一个currentValue
的内部 data,初始时从 value 获取一次值,当 value 修改时,也通过 watch 监听到及时更新;组件不会修改的 value 的值,而是修改 currentValue,同时将修改的值通过自定义事件input
派发给父组件,父组件接收到后,由父组件修改 value。所以,上面的数字选择器组件可以有下面两种方式实现。
vue
<template>
<InputNumber v-model:custom="value" @update:custom="handleChange" />
</template>
<script>
import InputNumber from "../components/input-number/input-number.vue"
export default {
components: { InputNumber },
data() {
return {
value: 1,
}
},
methods: {
handleChange(val) {
this.value = val
},
},
}
</script>
或者
vue
<template>
<InputNumber v-model="value" />
</template>
<script>
import InputNumber from "../components/input-number/input-number.vue"
export default {
components: { InputNumber },
data() {
return {
value: 1,
}
},
}
</script>
如果你不想用value
和input
这两个名字,也可以用model
选项指定它们的名字
vue
<template>
<div>
<button @click="increase(-1)">减 1</button>
<span style="color: red;padding: 6px">{{ currentValue }}</span>
<button @click="increase(1)">加 1</button>
</div>
</template>
<script>
export default {
name: "InputNumber",
props: {
number: {
type: Number,
},
},
model: {
prop: "number",
event: "change",
},
data() {
return {
currentValue: this.number,
}
},
watch: {
value(val) {
this.currentValue = val
},
},
methods: {
increase(val) {
this.currentValue += val
this.$emit("number", this.currentValue)
},
},
}
</script>
18.slot透传
js
<template #root="scoped">
<slot name="root" v-bind="scoped"></slot>
</template>
19.指令
js
<!-- 自定义指令 v-focus -- 自动聚焦 -->
<script setup>
const vFocus = {
mounted: (el) => el.focus()
}
</script>
<template>
<input v-focus />
</template>
js
app.directive('color', (el, binding) => {
// 仅在 `mounted` 和 `updated` 时都调用
el.style.color = binding.value
})