Vue3常见api使用指南(TS版)

server/2024/12/16 12:50:42/

defineProps() 和 defineEmits()

  • 内置函数,无需import导入,直接使用。
  • 传入到 defineProps 和 defineEmits 的选项会从 setup 中提升到模块的范围。因此,传入的选项不能引用在 setup 范围中声明的局部变量(比如设置默认值时),但是,它可以引用导入(import)的变量,因为它们也在模块范围内。就是说props设置默认值的时候不能直接用setup里的变量,可以用import引入的数据
<script setup>
//props是响应式的不能解构
//方法1:不能设置默认值(使用withDefaults解决)
const props = defineProps({foo?: String,id: [Number, String],onEvent: Function, //Function类型metadata: null
})//方法2
const props = defineProps({ foo: { type: String, required: true, default: '默认值' }, bar: Number })//方法3-推荐:弊端:不能设置默认值(使用withDefaults解决)
interface Props {data?: number[]
}
//const props = defineProps<Props>();
//或const props = defineProps<{ data?: number[] }>();
const props = withDefaults(defineProps<Props>(), {  data: () => [1, 2] })const emit = defineEmits(['change', 'delete'])  //声明从父组件来的事件
//将事件传递出去
emit('change', 参数);
emit('delete', 参数);
</script>

defineExpose()

内置函数,无需import导入,直接使用。Vue3中的setup默认是封闭的,如果想要使用ref或者 $parent 获取到的组件的的变量或函数,被访问的组件须使用defineExpose将属性和方法暴露出去。使用方式参考获取DOM

获取DOM

官网Api
Vue3中,移除了 $children 属性

  • ref
  • $parent
  • $root
<!--父组件parent.vue -->
<template><child ref="childRef"></child><div ref="divEl"></div>
</template>
<script setup lang="ts">
import child from './components/child.vue';
import type { ComponentInternalInstance } from 'vue';
//import { getCurrentInstance, ComponentInternalInstance } from 'vue'; 我用了自动导入,不需要引getCurrentInstance//方法一(常用推荐):
//typeof P 是获取到类,InstanceType<类>是拿到类的实例,一个是类一个是实例不一样
//为了获取组件的类型,我们首先需要通过 `typeof` 得到其类型,再使用 TypeScript 内置的 `InstanceType` 工具类型来获取其实例类型://获取组件。这个变量名和 DOM 上的 ref 属性必须同名,会自动形成绑定。变量名不能和组件名同名,即chilRef不能命名为child
let childRef = ref<InstanceType<typeof child> | null>(null);  //获取dom
let divEl = ref<HTMLDivElement | null>(null)//方法二:(不推荐)
//这样写会标红:类型“ComponentInternalInstance | null”上不存在属性“proxy”。使用类型断言或非空断言
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
//或const { proxy } = getCurrentInstance()!;let parentNum = ref(1)onMounted(() => {console.log(divEl.value);  //直接拿到dom本身console.log(childRef.value?.msg);   //.value的方式调用子组件的数据和方法(defineExpose暴露)childRef.value?.open();console.log(proxy?.$refs);   //proxy对象{childRef: Proxy(Object), divEl: div}
});
defineExpose({parentNum
})</script>

注意: 如果ref在v-for里,将会是一个数组,这里和vue2一样。使用childRef.value[0].msg

//子组件child.vue
<script setup lang="ts">
import type { ComponentInternalInstance } from 'vue'
let msg: string = '111';
const open = function() {console.log(222);}
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
onMounted(() => {//标红:类型“ComponentPublicInstance”上不存在属性“parentNum”console.log('parent', proxy?.$parent?.parentNum);
})
defineExpose({msg,open
})
</script>

useAttrs()

包含了父组件传递过来的所有属性(子组件内没有被defineProps和defineEmits声明的),包括 class 和 style 以及事件(相当于vue2中相当于listeners)。在vue2中,listeners)。在vue2中,listeners)。在vue2中,attrs 是接到不到 class 和 style 的

//parent.vue
<template><childfoo="222"foo2="333"class="child":style="{}"@test="handleTest"@test2="handleTest"></child>
</template>
<script setup lang="ts">
function handleTest() {}
</script>

//child.vue
<template><div></div>
</template>
<script setup lang="ts">const props = defineProps(['foo2'])const emits = defineEmits(['test2'])console.log(props); //{foo2: '333'}const attrs = useAttrs()console.log(attrs);  // {foo: '222', class: 'child', style: {…}, onTest: f handleTest()}
</script>

全局注册

//vue2中
Vue.prototype.$axios = xxx//使用
this.$axios

# 扩展全局属性

//main.ts
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)app.config.globalProperties.name = '猪八戒'
app.mount('#app')<script setup lang="ts">
import type { ComponentInternalInstance } from 'vue';
// proxy 就是当前组件实例,可以理解为组件级别的 this,没有全局的、路由、状态管理之类的
const { proxy, appContext  } = getCurrentInstance() as ComponentInternalInstance;//global 就是全局实例
const global = appContext.config.globalPropertiesconsole.log(global.name) 
console.log(proxy?.name) //会标红
</script>

异步组件

## 异步组件,路由懒加载

设置组件名称

  1. 在 3.2.34 或以上的版本中,使用 <script setup> 的单文件组件会自动根据文件名生成对应的 name 选项,即使是在配合 <KeepAlive> 使用时也无需再手动声明。不写index.vue,写一个具象的组件名称
  2. 再加个平级的script标签(注意:两个script标签使用的语言要同步,lang="ts")
<script lang="ts" setup>
</script>
<script lang="ts">export default {name: 'draft',inheritAttrs: false,customOptions: {},};
</script>

3.defineOptions
这个宏可以用来直接在 <script setup> 中声明组件选项,而不必使用单独的 <script> 块
官网解释

<script lang="ts" setup>defineOptions({name:'draft'})
</script>

 4.利用插件

  • vite-plugin-vue-setup-extend-plus
  • vite-plugin-vue-setup-extend(断点调试存在问题,未修复sourcemap is broken
  • unplugin-vue-define-options error in production:defineOptions is not defined
//会报错[vueSetupExtend不是一个函数],删掉package.json 中的 type: module即可
//vite.config.ts
import { defineConfig, Plugin } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueSetupExtend from 'vite-plugin-vue-setup-extend-plus'export default defineConfig({plugins: [vue(), vueSetupExtend()],
})
//SFC
<template><div>hello world {{ a }}</div>
</template><script lang="ts" setup name="App" inheritAttrs="false">const a = 1
</script>

watch的使用

官网
当我们需要在数据变化时执行一些“副作用”:如更改 DOM、执行异步操作(发起网络请求等),我们可以使用 watch 函数:

  • 监听基本数据类型
const name = ref('猪八戒')
// 监听 ref 属性 
watch(name, (newValue, oldValue) => { })

 监听对象

const obj = reactive({a: 1,b: 2,c: {d: 1,e: 2},f: []
})
//法一:深层次,当直接侦听一个响应式对象时,侦听器会自动启用深层模式:
//可以监听到obj.f = [1]和obj.c.d ++
watch(obj, (newValue, oldValue) => {
})//法二:深层次,必须写deep: true,不然浅层的也监听不到
watch(() => obj, (newValue, oldValue) => {
}, { deep: true })//法三:浅层, 监听不到obj.f = [1]和obj.c.d ++
watch(() => { ...obj }, (newValue, oldValue) => {
})

 监听对象的某个属性

watch(() => obj.a, (newValue, oldValue) => {
})//如果是对象的属性是引用数据类型,必须加deep: true才能监听到
watch(() => obj.f, (newValue, oldValue) => {
}, { deep: true })

监听多个数据

//法一:
watch([() => obj.a, name], ([newA, newName], [oldA, oldName]) => {});
//法二:
watch(() => [obj.a, obj.b], (newValue, oldValue) => {
})

watchEffect

官网

  • watch 默认是懒执行的:仅当数据源变化时,才会执行回调。可以传入immediate: true 选项来强制侦听器的回调立即执行
  • watchEffect的回调会立即执行(依赖收集),不需要指定 immediate: true。在执行期间,它会在同步执行过程中,自动追踪所有能访问到的响应式属性。watch:手动指定依赖watchEffect:自动收集依赖(注意: 依赖太多各种坑)
  • watchEffect 无法访问侦听数据的新值和旧值
//侦听器的回调使用与源完全相同的响应式状态是很常见的。例如下面的代码,在每当 `todoId` 的引用发生变化时使用侦听器来加载一个远程资源
const todoId = ref(1)
const data = ref(null)watch(todoId, async () => {const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)data.value = await response.json()
}, { immediate: true })//使用watchEffect简化代码,我们不再需要明确传递 `todoId` 作为源值.
watchEffect(async () => { const response = await fetch( `https://jsonplaceholder.typicode.com/todos/${todoId.value}`);data.value = await response.json() 
})
  • 使用哪种随你喜欢,如果不需要使用先前值(oldValue)并且希望立即执行就用watchEffect,可以少写一点代码。watch的自由度更高,watchEffect相当于封装了一层
  • 推荐在大部分时候用 watch 显式的指定依赖以避免不必要的重复触发,也避免在后续代码修改或重构时不小心引入新的依赖。watchEffect 适用于一些逻辑相对简单,依赖源和逻辑强相关的场景

停止监听器

  • 一个关键点是,侦听器必须用同步语句创建,这时它会在宿主组件卸载时自动停止。如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。
// 它会自动停止 
watchEffect(() => {}) // ...这个则不会! 
setTimeout(() => { watchEffect(() => {}) }, 100)//停止监听器
const unwatch = watchEffect(() => {}) // ...当该侦听器不再需要时 unwatch()//第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。
//该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。
watch(id, async (newId, oldId, onCleanup) => {const { response, cancel } = doAsyncWork(newId)// 当 `id` 变化时,`cancel` 将被调用,// 取消之前的未完成的请求onCleanup(cancel)data.value = await response
})

computed

官网

//创建一个只读的计算属性 ref:
const count = ref(1) 
const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 
plusOne.value++ // 错误//创建一个可写的计算属性
onst count = ref(1) 
const plusOne = computed({ get: () => count.value + 1, set: (val) => { count.value = val - 1 } 
}) plusOne.value = 1 console.log(count.value) // 0

 计算属性的 getter 应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。举例来说,不要在 getter 中做异步请求或者更改 DOM

CSS

  • 样式穿透:Vue3 中不支持 /deep/ 或者 >>>写法, 支持:deep(.class)
<style scoped> .a :deep(.b) { /* ... */ } 
</style>

 css绑定js变量(v-bind):单文件组件的 <style> 标签支持使用 v-bind CSS 函数将 CSS 的值链接到动态的组件状态。

<script setup>
const theme = {color: 'red'
}
</script><template><p>hello</p>
</template><style scoped>
p {color: v-bind('theme.color');
}
</style>

provie和inject

//父级
const name = ref('猪八戒'); 
const changeName = (newName: string) => {name.value = newName;
};
provide('name', name);
provide('changeName', changeName);  //更改name的方法//子级孙级
const name = inject('name') as string;  //使用类型断言,不然会有红色波浪线
const changeName = inject('changeName') as Fn;


http://www.ppmy.cn/server/150631.html

相关文章

IP地址不可用是怎么回事?ip地址不能用怎么解决

在数字化时代&#xff0c;IP地址作为网络设备的唯一标识&#xff0c;对于确保网络通信的顺畅至关重要。然而&#xff0c;有时我们可能会遇到IP地址不可用的情况&#xff0c;这不仅影响了网络连接的稳定性&#xff0c;还可能给我们的日常生活和工作带来诸多不便。本文将深入探讨…

警惕!手动调整服务器时间可能引发的系统灾难

警惕&#xff01;手动调整服务器时间可能引发的系统灾难 1. 鉴权机制1.1 基于时间戳的签名验证1.2 基于会话的认证机制&#xff08;JWT、TOTP&#xff09; 2. 雪花算法生成 ID 的影响2.1 时间戳回拨导致 ID 冲突2.2 ID 顺序被打乱 3. 日志记录与审计3.1 日志顺序错误3.2 审计日…

docker设置容器自动启动

说起开机自动启动应该很多人都遇到过&#xff0c;我们公司做的系统很多的中间件都没有设置开机自动启动然后中间修改问题又设置了一些临时生效的文件&#xff0c;开始的时候大家都不以为意&#xff0c;知道公司陆续有人离职入职管理交接一塌糊涂&#xff0c;项目成了历史遗留问…

One API 替代品 Chat Nio 安装与使用教程

有这样一位初中生&#xff0c;他在初一下学期发起了一个项目&#xff0c;专门用来给他的朋友们免费体验 GPT 模型。 到了八年级的暑假&#xff0c;他决定把这个项目开源出来&#xff0c;并且正式命名为 Chat Nio&#xff0c;同时项目的定位为一站式 LLM 模型管理平台。 到了九…

网络层IP协议(TCP)

IP协议&#xff1a; 在了解IP协议之前&#xff0c;我们市面上看到的"路由器"其实就是工作在网络层。如下图&#xff1a; 那么网络层中的IP协议究竟是如何发送数据包的呢&#xff1f; IP报头&#xff1a; IP协议的报头是比较复杂的&#xff0c;作为程序猿只需要我们重…

Debezium OracleSnapshotChangeEventSource 分析

Debezium OracleSnapshotChangeEventSource 分析 目录 1. 概述2. 核心功能3. 实现分析4. 使用场景5. 示例分析6. 最佳实践7. 总结1. 概述 OracleSnapshotChangeEventSource 是 Debezium Oracle 连接器中负责执行数据库快照的核心组件,主要负责: 初始数据快照增量数据快照Sc…

IIC 通信协议

IIC 通信协议 参考&#xff1a;CSDN-Projectsauron、B站-江协科技 IIC Overview IIC协议&#xff08;Inter-Integrated Circuit&#xff0c;可简写为 I2C&#xff09;&#xff0c;是一种用于各种电子设备之间进行通信和数据交换的串行通信协议。它是由飞利浦&#xff08;Phil…

2023蓝桥杯题解 仅C、D

2023蓝桥杯题解 洛谷2023蓝桥杯C题 洛谷2023蓝桥杯D题 C题 [蓝桥杯 2023 省 A] 平方差 题目描述 给定 L , R L,R L,R&#xff0c;问 L ≤ x ≤ R L \leq x \leq R L≤x≤R 中有多少个数 x x x 满足存在整数 y , z y,z y,z 使得 x y 2 − z 2 xy^2-z^2 xy2−z2。 输入…