Vue3开发文档总结
Vue3中获取this的方式
Vue2中每个组件使用this指向当前组件实例, this上包含全局挂在的内容:路由、状态管理等
Vue3中每个this,想要获取组件实例有两种
- 获取当前组件实例
- 获取全局实例
<script setup> import { getCurrentInstance } from 'vue' // proxy:当前组件实例,=》 组件级别的this,没有全局的、路由、状态管理等 const { proxy, appContext } = getCurrentInstance(); // global全局实例 const global = appContext.config.globalProperties; </script>
全局注册(属性/方法)
Vue2中全局挂在东西是这样滴:
Vue.prototype.xxx = xxx
之后通过this.xxx获得
在Vue3中需要在全局实例上挂载(在main.js中全局注册)
// main.js import { createApp } from 'vue' import App from './App.vue' const app = createApp(App); // 添加全局属性 app.config.globalProperties.name = 'Wangzz' // XXX.vue 其他组件中引用 import { getCurrentInstance } = from 'vue' const { appContext } = getCurrentInstance() const global = appContext.config.globalProperties; console.log(global.name) // Wangzz
template
Vue2中只能存在一个根节点,Vue3中支持多个根节点
本质上Vue3组件还是一个根节点,因为DOM树是树状结构,
Vue3在编译阶段新增判断,如果当前组件不只是一个根元素,则添加一个fragment组件把这个多根组件
给包裹起来,相当于这个组件还是只有一个根节点
<template> <div>01</div> <div>02</div> </template> // -------------》 编译后 <template> <fragment> <div>01</div> <div>02</div> </fragment> </template>
🤡Vue3中如何获取DOM???
<template> <el-form ref='formRef'></el-form> <child-component/> </template> <script setup lang='ts'> import ChildComponent from './child.vue' import { getCurrentInstance } from './child.vue' import { ElForm } from 'element-plus' // 方法一,变量名和DOM上ref属性同名之后会形成绑定 const formRef = ref(null); console.log(formRef.value) // 获取到DOM了 // 方法二 const { proxy } = getCurrentInstance(); proxy.$refs.formRef.validate((valid) => { ... }) // 方法三, eg: 在ts中,可以直接获取组件类型 // 直接获取子组件 const formRef = ref<InstanceType<typeof ChildComponent>>(); </script>
初始化
V2中进入页面就请求接口,或其他初始化操作,一般放置在[created]或[mounted]
而Vue3中beforeCreated或created这两个钩子不需要,[setup]会在beforeCreated和created两个钩子之前执行
因此beforeCreated/created/beforeMount/mounted这几个钩子的内容可以直接放置在setup中
或者放置在🤩onMounted或onBeforeMount中🤩
<script setup> import { onMounted } from 'vue' // 请求接口函数 const getData = () => { let res = xxxApi(); } onMounted(() => { getData(); }); </script>
如何解除绑定???
V2中一般清除定时器\监听之类的操作存在两种方法
- 用$once搭配hook: beforeDestroy(V3不支持)
- 用beforeDestroy/ deactivated这两个钩子,
import { onBreforeUnmount, onDeactivated } from 'vue' // 组件卸载之前,对应V2的beforeDestroy onBeforeUnmount(() => { clearTimeout(timer); window.removeAddEventListener('.........'); }); // 退出缓存组件,对应V2的deactivated onDeactivated(() => { clearTimeout(timer); window.removeAddEventListener('......'); });
🤡 ref VS reactive
ref和reactive都是用来创建响应式对象
- ref 用于创建基础类型
- reactive 用于创建响应式
实际上ref也常用于定义数组,也有人在一个组件中定义一个reactive,之后组件的所有数据都放在这个reactive对象中
Warning 🪔
- ref如果包裹引用数据类型,本质上还是通过reactive实现的
- ref返回的数据在template中是直接使用的, 在script中需要通过.value访问
<template> <div> {{count}}</div> </template> <script setup> import {ref, reactive} from 'vue' const count = ref(1) // (❁´◡`❁) somebody usage const arr = ref([]); console.log(arr.value) // [] // (●'◡'●) somebody usage const data = reactive({ name: 'wangzz', hobbies: ['Reading', 'Running'], info: ' a simple guy' }) console.log(data.name); </script>
// ref要返回一个包装对象, V2中data都是返回一个对象
// 对象引用类型,需要使用代理或劫持
// 如果只是返回基础类型的话,存储在栈中,执行栈执行完就会回收
// 无法进行代理或劫持,即无法追踪后续的变化,所以不得不返回一个对象,这样才能有响应式
🥰 toRef 和 toRefs
toRef和toRefs的共同点是用来创建响应式的引用的,取出响应式对象里的属性或解构响应对象
解构出来的属性依然是响应式属性,如果不适用这两个直接结构的话会丢失响应式效果.
主要是方便我们使用直接变量 xxx 而不需要 data.xxx 而且修改xxx的也是直接修改对象属性的
案例使用
👀监听watch的使用
watch用来监听一个已经存在属性,发生变化的时候做出某些操作
V2中的使用
watch: { userId: 'getData', userName(newVal, oldVal) { this.getData() }, userInfo: { handler(newVal, newVal) {this.getData()}, immediate: true, deep: true } }
💸V3中的监听写法
V3中watch是一个函数,可以接受三个参数,分别是1.监听的属性 2.接收的新值和老值的回调函数, 3. 配置项
<script setup> import { watch, ref, reactive } from 'vue' const name = ref("wangzz"); const data = reactive({ name: "wangzz", age: 24, children: [] }); // 监听ref属性 watch(name, (newVal, oldVal) => { ... }) // 监听其他属性,路由或状态管理 watch( () => data.age, (newVal, oldVal) => {...} ) // 监听多个属性,数组放多个值,返回的新值和老值都是数组形式 watch([data.age, data.money], ([newAge, newMoney], [oldAge, oldMoney]) => { ... }); // 第三个参数是一个对象,该对象是一个配置对象可配置5个属性 watch(data.children, (newList, oldList) => {...}, { immediate: true, deep: true, // 回调函数的执行时机,默认在组件更新之前调用,更新后调用改成post flush: 'pre', // 默认值是pre, 可以改成post或sync // 下面两个用来调式 onTrack(e) { debugger }, onTrigger(e) { debugger }, }); </script>
副作用 watch和watchEffect是一样的
在watch回调函数中接受第三个参数onInvalidate, 为清楚副作用的函数
首次触发监听的回调函数(handler)不会执行onInvalidate,之后每次触发默认会限制性onInvalidate
默认的执行机制在更新前调用
// 回调函数会接受一个参数,为清除副作用的函数 // key触发更新的时候会先打印222再打印wangzz~ // 如果需要在更新后调用 => flsuh: post watch(key, (newKey, oldKey, onInvalidate) => { console.log("wangzz ~~"); // 获取DOM默认获取到的是更新前的DOM,如果是flush:post,可以获取更新后的DOM console.log("DOM节点: ", dom.innerHTML); onInvalidate(() => { console.log(2222); }); });
onInvalidate使用场景: eg: 监听的回调函数(handler)中存在一些异常操作,
当再次触发watch的时候可以用它来对前面未完成的异步任务执行取消上一次触发的watch时未完成的请求
watchEffect
V3中除了watch还增加了一个watchEffect.区别是:
-
watch是对传入一个或多个值进行监听,触发的时候会返回新值和旧值,且默认第一次不会执行;
-
watchEffect是传入一个立即执行函数,默认第一次就会执行,且不需要传入监听内容,会自动手机函数内的数据源作为依赖
watch中会作为回调的第三个参数传入, watchEffect中回调函数的第一个参数
// 监听方法赋值 cosnt unwatch = watch('kehy', callback) const unwatchEffect = watchEffect(() => {}) // 需要停止监听的时候,手动调用停止监听 unwatch() unwatchEffect();
🤩🤩 watchEffect的使用
<script setup> import { watchEffect } from 'vue' // 正常使用 watchEffect(() => { // 会自动收集这个回调函数使用到的属性作为依赖进行监听 // 监听的是userInfo.name属性就不会监听userInfo console.log(userInfo.name); }); // 存在两个参数,参数一是触发监听回调函数, 参数二是可选配置项 watchEffect(() => { ... } , { // 可配置项,和watch一样 flush: 'pre', onTrack(e) {debugger} onTrigger(e) {debugger} }); // 回调函数接收一个参数,为清除副作用的函数,和watch同理 watchEffect(onInvalidate => { console.log("Wangzz ~~~"); onInvalidate(() => { console.log(23123124); }); }); </script>
// 组件数据更新之前执行,还是组件数据更新之后执行
// 组件数据更新之后执行 watchPostEffect(() => { }); // 组件数据更新之前执行 watchPreEffect(() => { });
computed🦕🦕🦕 - 计算属性
V2中computed最常见的使用场景: mapGetters/ mapState获取状态管理的属性, 获取url上的属性
条件判断\类型转换之类的支持函数和对象两种写法
V3中computed不再是一个对象而是一个函数, 函数的第一个参数是侦听器源,用于返回计算的新值,也支持对象写法第二个参数用于调试
<script setup> import { computed } from 'vue' const props = defineProps(['visible', 'type']; const emit = defineEmits(['myClick']); // 函数写法,计算类型 const isFirst = computed(() => props.type === 1) // 对象写法 const status = computed({ get() { return props.visible }, // 相当于V2中this.visible set(val) { emit('myClick', val}, // 相当于V2中this.$emit('input', val) }); // computed 第二个参数也是一个对象,调试使用 const hehe = computed(参数一上面两种都可, { onTrack (e) { debugger } onTrigger (e) { debugger } }) </script>
🦉🦉🦉 nextTick
nextTick的使用方法,除了不能用this其他的和V2使用方式一样
<script setup> import { nextTick } from 'vue' // 方式一 const handleClick = async() => { await nextTick(); console.log('wangzz ~~'); } // 方式二 nextTick(() => { console.log("wangzz ~~"); }); // 方式三 nextTick().then(() => { console.log("测试数据 ~~~~"); }); </script>
mixins 和 hooks
Vue2中逻辑的抽离服用一般采用mixins 缺点:
- 没有独立命令空间, mixins会和组件内部产生命名冲突
- 不去翻代码不知道引入的mixins中有什么
- 引入多个mixins时不知道自己使用来自哪一个mixins的
Vue3逻辑抽离服用hooks语法,本质是一个函数可以传递参数和获取返回值
// xxx.js export const getData = () => {} export default function unInstance() { return { ... } } // xxx.vue import unInstance, { getData } from 'xx.js' const { ... } = unInstance(); onMounted(() => { getData() });
★🦉🦉🦉🦉🦉 hooks如何写出更优雅的代码还需要多写多实践
组件通信
Vue3中组件通信的方式有如下几种
- props + defineProps
- defineEmits
- defineExpose + ref
- useAttrs
- v-modal(支持多个)
- provide/ inject
- mitt
- Vuex/ Pinia
多个v-model
V2中每个组件仅能写一个v-model, 子组件没有写model的话,默认在props接受value即可
修改就是使用this.$emit('input')事件
V3中每个组件支持写多个v-model, 没有.sync和model重命名的操作
// 父组件 <template> <child v-model:name='name' v-model:age='age'/> </template> <script setup> import {ref} from 'vue' const name = ref("wangzz"); const age = ref(24); </script> // 子组件 <script setup> const emit = defineEmits(['update:name', 'update:age']); const handleClick = () => { console.log("点击了"); emit('update:name', '新名字') } </script>
状态管理 (Vue3推荐Pinia)
Vuex4用法案例
// main.js中 import { createApp } from 'vue' import App from './App.vue' import Store from './store' const app = createApp(App) app.use(Store) // 模块: store/user/index.js export default { state: {}, getters: {}, actions: {}, mutations: {}, } // 模块: store/index.js import { createStore } from 'vuex' import user from './user' const store = createStore({ state: {}, getters: {}, actions: {}, mutations: {}, modules: { user } }); export default store; // 组件中使用store, 需要使用到状态管理的.vue文件 <script setup> import { useStore } from 'vuex' const store = useStore(); </script>
路由的使用
Vue-Router4的使用,主要是route和router的使用 // main.js中 import { createApp } from 'vue' import App from './App.vue' import Router from './App.vue' const app = createApp(App) app.use(Router) // router/index.js import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' // routes数组中和V2一样 const routes = [ { path: '/', redirect: { name: 'login'}} ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }); export default router // 需要用到路由的.vue文件中 <script setup> import { useRoute, useRouter } from 'vue-router' // route对应V2中的this.$route const route = useRoute(); // router对应V2中this.$router const router = useRouter(); </script>
🤡 CSS样式穿透
V2在scoped中修改子组件或组件库中的组件样式
可以用样式穿透,不管是Less还是Sass都是用/deep/ .className {}
V3中不支持/deep/写法 ===========> 而是 :deep(.className) {}
<style lang='scss' scoped> // V2写法 /deep/ .el-form { ... } // V3写法 :deep(.el-form) { // ... } </style>
CSS绑定JS变量
<template> <div class='name'>Wangzz ~~ </div> </template> <script setup> import { ref } from 'vue' const str = ref("#f00"); </script> <style scoped lang='scss'> .name { background-color: v-bind(str); } </style>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具