欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > Vue2进阶之Vue3高级用法

Vue2进阶之Vue3高级用法

2025/3/13 11:21:21 来源:https://blog.csdn.net/qq_34306228/article/details/143369845  浏览:    关键词:Vue2进阶之Vue3高级用法

Vue3高级用法

  • 响应式
    • Vue2:Object.defineProperty
      • Object.defineProperty
      • this.$set设置响应式
    • Vue3:Proxy
  • composition API
    • Vue2 option API和Vue3 compositionAPI
    • reactive和shallowReactive
    • readonly效果
    • toRefs效果
  • 生命周期
    • main.js
    • index.html
    • LifeCycle.vue
  • 异步组件元素节点
    • 正常写
    • index.html
    • main.js
    • Async.vue
    • AsyncComp.vue
    • 使用异步
      • main.js
  • teleport 传送门—createPortal React
    • index.html
    • main.js
    • Dialog.vue
  • 自定义hooks
    • index.html
    • main.js
    • useMousePosition.js
    • MouseMove.vue

作业:Vue3官网所有内容过一遍 Vue3

响应式

  • Vue2:Object.defineProperty
  • Vue3:Proxy

Vue2:Object.defineProperty

Object.defineProperty

//Vue2:Object.definePropertyconst initData={value:1
}const data={}Object.keys(initData).forEach(key=>{Object.defineProperty(data,key,{// getter setterget(){console.log("访问",key)return initData[key]},set(val){console.log("设置值",key)initData[key]=val}})
})

请添加图片描述

this.$set设置响应式

set给目的对象添加响应式属性后,并触发事件更新

this.$set(data,a,1)

请添加图片描述
请添加图片描述

Vue3:Proxy

// Vue3:Proxy
const person={name:"张三"
}let proxy=new Proxy(person,{get:function(target,key){if(key in target){return target[key]}throw new Error(`${key} is not defined`)},set(target,key,val){console.log("设置值",key)target[key]=val}}
)
let obj=Object.create(proxy)

请添加图片描述
proxy的正规写法:

// Proxy正规写法
const initData={value:1
}
let proxy=new Proxy(initData,{get:function(target,key,receiver){console.log("访问",key)return Reflect.get(target,key,receiver)},set:function(target,key,val,receiver){console.log("修改",key)return Reflect.set(target,key,val,receiver)}}
)

请添加图片描述

拓展
怎么将esNext转换为es5写法?
通过babel,国外主流的swc转换

composition API

Vue2 option API和Vue3 compositionAPI

Vue3的compositionAPI和Vue2的optionsAPI的最大的区别是:更加倾向于函数式编程以及Vue3支持多个根节点
Vue2:

<template><!--XXXX-->
</template>
<script>export default {data(){return{ XXXX }},methods:{},computed:{}}
</script>
<style></style>

Vue2最容易产生的问题是:写一个文件一开始还好,写着写着就发现这个文件内容非常非常多,非常非常繁琐。
OptionAPI非常非常容易导致一个文件内容非常非常多,越往后越难改,非常非常容易出bug
Rect相对于Vue不容易写出那么夸张的效果
Vue2的mixin将组件单一内容拆解到一个文件,太灵活了,对多人协作不友好

=>Vue3主要解决的就是这个问题,将明确的逻辑抽象到一起

React=>自定义hook,将一部分的逻辑功能放到单一组件里去维护

App.vue

<template><div class="mine"></div>
</template><script>
import {defineComponent,ref,isRef} from 'vue'
export default defineComponent({// 相当于Vue2生命周期中的beforeCreate,createdsetup(){const count=ref(10)const user="张三"console.log("count,user",count,count.value,user)console.log("count is ref?",isRef(count))console.log("user is ref?",isRef(user))}
})
</script>

请添加图片描述

reactive和shallowReactive

<template><div class="mine"></div>
</template><script>
import {defineComponent,reactive,shallowReactive} from 'vue'
export default defineComponent({// 相当于Vue2生命周期中的beforeCreate,createdsetup(){const person={name:"张三",age:18,contacts:{phone:12345}}const personReactive=reactive(person)console.log("person reactive",personReactive)console.log("person reactive name",personReactive.name)console.log("person reactive contacts",personReactive.contacts)console.log("--------------分割线------------------------")const shallowPersonReactive=shallowReactive(person)console.log("shallow person reactive",shallowPersonReactive)console.log("shallow person reactive name",shallowPersonReactive.name)console.log("shallow person reactive contacts",shallowPersonReactive.contacts)}
})
</script>

请添加图片描述

readonly效果

<template><div class="mine"></div>
</template><script>
import {defineComponent,ref,reactive,readonly} from 'vue'
export default defineComponent({// 相当于Vue2生命周期中的beforeCreate,createdsetup(){const count=ref(10)const obj=reactive({abc:18,count,userInfo:{age:66}})console.log("reactive obj:",obj)// 在Proxy的set中,是不允许做修改的const objOnly=readonly(obj)  console.log("readonly obj:",objOnly)objOnly.abc=100console.log("readonly obj:",objOnly)}
})
</script>

请添加图片描述

toRefs效果

<template><div class="mine"></div>
</template><script>
import {defineComponent,ref,isRef,reactive,shallowReactive,readonly, toRefs} from 'vue'
export default defineComponent({// 相当于Vue2生命周期中的beforeCreate,createdsetup(){const count=ref(10)const obj=reactive({abc:18,count,userInfo:{age:66}})console.log("reactive obj:",obj)console.log("toRefs obj",toRefs(obj))}
})
</script>

请添加图片描述
如果是通过ref创建出来的,一般是RefImpl,如果是通过toRefs创建出来的,一般把toRefs视为一个对象,针对对象里的所有属性,全部转换为toRefs的效果

生命周期

经常应用的场景:
1.初始化 mount
2.数据变化 update
3.卸载 unmount

加入LiftCycle组件

main.js

import { createApp } from 'vue'
import App from './App.vue'import LifeCycle from './LifeCycle.vue'createApp(App).mount('#app')createApp(LifeCycle).mount('#lifeCycle')

index.html

<div id="lifeCycle"></div>

全部:

<!DOCTYPE html>
<html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><link rel="icon" href="<%= BASE_URL %>favicon.ico"><title><%= htmlWebpackPlugin.options.title %></title></head><body><noscript><strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><!-- built files will be auto injected --><div id="lifeCycle"></div></body>
</html>

LifeCycle.vue

<template><div>{{ count }}{{ name }}</div><button @click="addNumber">+1</button><button @click="updateName">update name</button>
</template>
<script>
export default {data() {return {count:0,name:"张三"}},methods:{addNumber(){this.count++},updateName(){this.name = "李四"}},//  1.初始化,data还不能用beforeCreate(){console.log("beforeCreate")},//   data可以用,dom不可用created(){console.log("created")},//   挂载之前,DOM还没有生成beforeMount(){console.log("beforeMount")},//   在VNode(初次渲染/更新)渲染时调用renderTracked({key,target,type}){console.log("renderTracked",key,target,type)},//  挂载之后,DOM已经生成mounted(){console.log("mounted")console.log("-------------------------------------------------------------")},//  2.updaterenderTriggered({key,target,type}){console.log("renderTriggered",key,target,type)},beforeUpdate(){console.log("beforeUpdate")},renderTracked({key,target,type}){console.log("renderTriggered",key,target,type)},updated(){console.log("updated")},// 3.卸载beforeUnmount(){console.log("beforeUnmount")},unmounted(){console.log("unmounted")}
}
</script>
<style scoped></style>

请添加图片描述

异步组件元素节点

正常写

  • src
    • Async.vue
    • components
      • AsyncComp.vue

index.html

<!-- 3.异步组件元素节点 -->
<div id="async"></div>
<!DOCTYPE html>
<html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><link rel="icon" href="<%= BASE_URL %>favicon.ico"><title><%= htmlWebpackPlugin.options.title %></title></head><body><noscript><strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><!-- 1.composition 元素节点 --><div id="app"></div><!-- built files will be auto injected --><!-- 2.生命周期元素节点 --><div id="lifeCycle"></div><!-- 3.异步组件元素节点 --><div id="async"></div></body>
</html>

main.js

import { createApp } from 'vue'
import App from './App.vue'import LifeCycle from './LifeCycle.vue'import Async from './Async.vue'
import AsyncComp from './components/AsyncComp.vue'createApp(App).mount('#app')createApp(LifeCycle).mount('#lifeCycle')const async=createApp(Async)
async.component("async-comp",AsyncComp)
async.mount('#async')

Async.vue

<template>ASYNC<async-comp></async-comp>
</template><script setup lang="ts"></script><style scoped></style>

AsyncComp.vue

<template><div>async defineComponent</div>
</template><script setup lang="ts"></script><style scoped></style>

在这里插入图片描述
但是这样执行

pnpm run build

打包后,只会生成一个js文件
在这里插入图片描述

使用异步

main.js

const AsyncComp=defineAsyncComponent(()=>import('./components/AsyncComp.vue'))async.component("async-comp",AsyncComp)

全部代码:

import { createApp,defineAsyncComponent } from 'vue'
import App from './App.vue'import LifeCycle from './LifeCycle.vue'import Async from './Async.vue'
// import AsyncComp from './components/AsyncComp.vue'createApp(App).mount('#app')createApp(LifeCycle).mount('#lifeCycle')const async=createApp(Async)const AsyncComp=defineAsyncComponent(()=>import('./components/AsyncComp.vue'))async.component("async-comp",AsyncComp)
async.mount('#async')

再执行

pnpm run build

会生成两个js文件

在这里插入图片描述
这两个文件是将我们异步的组件给单独拿出来,将异步组件单独拿出来的效果是,因为要做的是异步组件的动态引入,一般是额外使用或之后去用,就没有必要跟原先代码单独一起打包。

对应的是React.lazy和React中的suspense

const myComponent=React.lazy(()=>import('./Component'))function MyComponent(){return (<div><Suspense fallback={<Loading />}><Component /></Suspense></div>)
}

teleport 传送门—createPortal React

将子节点渲染到父节点以外的DOM的方式

  • src
  • Dialog.vue

index.html

<!-- 4.teleport 元素节点 -->
<div id="dialog"></div>

全部代码:

<!DOCTYPE html>
<html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><link rel="icon" href="<%= BASE_URL %>favicon.ico"><title><%= htmlWebpackPlugin.options.title %></title></head><body><noscript><strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><!-- 1.composition 元素节点 --><div id="app"></div><!-- built files will be auto injected --><!-- 2.生命周期元素节点 --><div id="lifeCycle"></div><!-- 3.异步组件元素节点 --><div id="async"></div><!-- 4.teleport 元素节点 --><div id="dialog"></div></body>
</html>

main.js

const dialog=createApp(Dialog)
dialog.mount('#dialog')

全部代码:

import { createApp,defineAsyncComponent } from 'vue'
import App from './App.vue'import LifeCycle from './LifeCycle.vue'import Async from './Async.vue'
import Dialog from './Dialog.vue'
// import AsyncComp from './components/AsyncComp.vue'createApp(App).mount('#app')createApp(LifeCycle).mount('#lifeCycle')const async=createApp(Async)const AsyncComp=defineAsyncComponent(()=>import('./components/AsyncComp.vue'))async.component("async-comp",AsyncComp)
async.mount('#async')const dialog=createApp(Dialog)
dialog.mount('#dialog')

Dialog.vue

<template><div class="portals"><button @click="showNotification">切换弹窗</button><teleport to="#dialog"><div v-if="isOpen" class="notification">这是一个弹窗</div></teleport></div>
</template>
<script>
import { ref } from 'vue';export default {setup(){const isOpen=ref(false)let closePopupconst showNotification=()=>{isOpen.value=trueclearTimeout(closePopup)closePopup=setTimeout(()=>{isOpen.value=false},20000)}return {isOpen,showNotification}}
}
</script>
<style scoped>
.notification{position: fixed;bottom: 20px;background-color: #fff;border: 1px solid #ccc;width: 300px;padding:30px;
}
</style>

弹窗是挂载在dialog下的,而不是protals下
在这里插入图片描述

自定义hooks

hooks最重要的特点:对于我们来说,不需要关心内部的逻辑,而且与Vue2相比,提供了一个非常合理的方式,使用Vue2的option API很容易写出三五千行的代码,但是对于函数式编程来说,按照逻辑功能拆分下来,一个文件至少包含一个功能,其他功能引用即可。

  • public
    • index.html
  • src
    • hooks
      • useMousePosition.js
    • MouseMove.vue
    • main.js

index.html

<!-- 5.自定义hook -->
<div id="mousemove"></div>

全部代码:

<!DOCTYPE html>
<html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><link rel="icon" href="<%= BASE_URL %>favicon.ico"><title><%= htmlWebpackPlugin.options.title %></title></head><body><noscript><strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><!-- 1.composition 元素节点 --><div id="app"></div><!-- built files will be auto injected --><!-- 2.生命周期元素节点 --><div id="lifeCycle"></div><!-- 3.异步组件元素节点 --><div id="async"></div><!-- 4.teleport 元素节点 --><div id="dialog"></div><!-- 5.自定义hook --><div id="mousemove"></div></body>
</html>

main.js

import MouseMove from './MouseMove.vue'const mousemove=createApp(MouseMove)
mousemove.mount('#mousemove')

全部代码:

import { createApp,defineAsyncComponent } from 'vue'
import App from './App.vue'import LifeCycle from './LifeCycle.vue'import Async from './Async.vue'
import Dialog from './Dialog.vue'
// import AsyncComp from './components/AsyncComp.vue'
import MouseMove from './MouseMove.vue'// createApp(App).mount('#app')// createApp(LifeCycle).mount('#lifeCycle')// const async=createApp(Async)// const AsyncComp=defineAsyncComponent(()=>import('./components/AsyncComp.vue'))// async.component("async-comp",AsyncComp)
// async.mount('#async')// const dialog=createApp(Dialog)
// dialog.mount('#dialog')const mousemove=createApp(MouseMove)
mousemove.mount('#mousemove')

useMousePosition.js

import { onMounted, onUnmounted, ref } from "vue";function useMousePosition() {const x=ref(0)const y=ref(0)const updateMouse=e=>{x.value=e.pageXy.value=e.pageY}onMounted(()=>{document.addEventListener('click',updateMouse)}) onUnmounted(()=>{document.removeEventListener('click',updateMouse)})return{x,y}
}export default useMousePosition

MouseMove.vue

<!-- 提供鼠标位置自定义hooks -->
<template><div><p>X:{{x}}</p><p>Y:{{y}}</p></div>
</template><script>
import { defineComponent } from 'vue';
import useMousePosition from './hooks/useMousePosition';export default defineComponent({setup(){const {x,y}=useMousePosition()return {x,y}}
})
</script><style scoped></style>

请添加图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词