• Vue3基础看这一篇就够了(万字长篇,附实例代码及效果演示)


    目录

    前言

    概述

    Vue3组合式api  VS  Vue2选项式api

    基础部分

    setup

    选项式api的风格

    组合式api的风格

    区别

     响应式数据

    ref

    reactive

    shallowReactive 与 shallowRef 

     计算属性和监听

     computed 函数

     watch 函数

     watchEffect

    生命周期

     响应式数据只读

    toRaw 返回代理的源

    markRaw 标记对象拒绝代理

    provide 与 inject 跨组件传值

    判断是否为响应式数据

    toRef 和 toRefs 解构响应式数据

     新组件

    Fragment

    Teleport 

    Suspense

    组合式函数 

    全局的api及指令的变动

    结语


    前言

    vue3已经出了好长一段时间了,最近闲来无事简单学习了一下,新增的东西还是挺多的,写一篇文章来记录一下。

    概述

    Vue3组合式api  VS  Vue2选项式api

    谈到 vue3,首先想到的就是组合式api,很大程度的解决了vue2选项式api的缺点,那有啥缺点?当文件中的业务代码非常多的时候,阅读修改代码的时候是非常痛苦的,data,method,watch还有计算属性之间来回跳转, 我已经准备拔刀了。

    下面这些图被疯转,很形象的展现了vue2和vue3的区别,可以看到组合式api就是将单个功能的状态,方法,计算属性等等需要用到的东西都组合在一起抽离成一个hook,也就是对应图4的function,最终再统一引入组合到一起。这样做的好处就是单个功能的代码都在一起,方便调式修改。

     

     

    基础部分

    setup

    setup是vue3的一个新的配置项,只在初始化的时候执行一次,所有的组合式函数都在此使用。setup可以在选项式api的风格中使用也可以通过组合式api的风格 。通过代码简单对比一下。vue3推荐使用组合式。

    选项式api的风格

    1. <template>
    2. <div>
    3. <h1>v3h1>
    4. <h3>{{ sum }}h3>
    5. <button @click="sum++">+1button>
    6. div>
    7. template>
    8. <style scoped>style>

    组合式api的风格

    1. <template>
    2. <div>
    3. <h1>v3h1>
    4. <h3>{{ sum }}h3>
    5. <button @click="sum++">+1button>
    6. div>
    7. template>
    8. <style scoped>style>

    区别

  • <template>
  • <div>
  • <h1>v3h1>
  • <h3>{{ sum }}h3>
  • <button @click="add">+1button>
  • div>
  • template>
  • <style scoped>style>
  • reactive

    为对象做深层!!!!响应式代理, 也就是如果对象有多层依旧是响应式的,返回一个Proxy实例, 如果传入一个字符串或者数字,它将不是响应式的。Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)Proxy - JavaScript | MDN。Vue使用 Proxy 进行数据劫持, Reflect 进行反射修改 Reflect - JavaScript | MDN

    1. <template>
    2. <div>
    3. <h1>v3h1>
    4. <h1>{{ sum }}h1>
    5. <h3>姓名:{{ person.name }}h3>
    6. <h3>年龄:{{ person.age }}h3>
    7. <h3>工作:{{ person.job.j1.jname }}h3>
    8. <h3 v-if="person.hobby">爱好: {{ person.hobby }}h3>
    9. <button @click="person.name += '-'">修改姓名button>
    10. <button @click="person.age++">修改年龄button>
    11. <button @click="person.job.j1.jname += '!'">修改工作button>
    12. <button @click="add">增加爱好button>
    13. <button @click="deleteHB">删除爱好button>
    14. div>
    15. template>
    16. <style scoped>style>

    shallowReactive 与 shallowRef 

    shallowRef 直译过来意思是浅层的 ref,shallowRef 传入对象不会求助 reactive,仅仅对ref对象的 value 属性具有响应式。

    shallowReactive 只处理对象第一层的响应式,  如果修改了深层的数据页面是不会响应的,但是会在下次页面更新中渲染出来。

    1. <template>
    2. <div>
    3. <h1>v3h1>
    4. <h3>
    5. shallowRef_jack: {{ shallowRef_jack }}
    6. <button @click="shallowRef_jack = {}">修改整个对象button>
    7. <button @click="shallowRef_jack.name += '!'">修改对象属性button>
    8. h3>
    9. <h3>
    10. ref_jack: {{ ref_jack }}
    11. <button @click="ref_jack = {}">修改整个对象button>
    12. <button @click="ref_jack.name += '!'">修改对象属性button>
    13. h3>
    14. <h3>
    15. shallowReactive_ben: {{ shallowReactive_ben }}
    16. <button @click="shallowReactive_ben.child.son.name = '!'">
    17. 修改对象的第三层属性
    18. button>
    19. <button @click="shallowReactive_ben.name += '!'">
    20. 修改对象第一层属性
    21. button>
    22. h3>
    23. <h3>
    24. reactive_ben: {{ reactive_ben }}
    25. <button @click="reactive_ben.child.son.name += '!'">
    26. 修改对象的第三层属性
    27. button>
    28. <button @click="reactive_ben.name += '!'">修改对象第一层属性button>
    29. h3>
    30. div>
    31. template>
    32. <style scoped>
    33. h3 {
    34. font-size: 26px;
    35. border: 1px solid #ccc;
    36. padding: 20px;
    37. margin: 20px;
    38. }
    39. button {
    40. float: right;
    41. padding: 10px;
    42. font-size: 20px;
    43. }
    44. style>

     计算属性和监听

     computed 函数

    计算属性有两种写法,作用和vue2一样,通过监听某个值的变化计算出一个新值

    • 只读的写法 :computed(() => xxxxxx),
    • 可读可写的写法:  computed({ get: () => xxxx, set: (val) => { xxxx } })
    1. <template>
    2. <div>
    3. <h1>v3h1>
    4. <h2>
    5. ref 定义的 count: {{ count }} <button @click="count++">count++button>
    6. h2>
    7. <h2>计算属性 num1: {{ num1 }} <button @click="num1++">num1++button>h2>
    8. <h2>计算属性 num2: {{ num2 }} <button @click="num2++">num2++button>h2>
    9. div>
    10. template>
    11. <style scoped>style>

     watch 函数

    watch 函数用来监听数据的变化,和vue2大体上都是相同的。

    参数列表:

    1. 参数1为需要监听的响应式对象(可以是单个对象,也可以是一个数组,也可以是一个getter函数),
    2. 参数2为监听对象发生变化时所执行的回调
    3. 参数3是一些配置项:immediate是否开启立即监听,deep是否开启深度监听,flush回调的触发时机,onTrack / onTrigger用于调试的两个函数

     

    注意点: 

    1. 直接监听 reactive 定义的响应式对象默认开启了深度监听
    2. 通过 getter 形式监听响应式对象默认是浅层监听
    1. <template>
    2. <div>
    3. <h1>v3h1>
    4. <h2>
    5. ref 定义的 count: {{ count }} <button @click="count++">count++button>
    6. h2>
    7. <h2>
    8. reactive 定义的 person: {{ person }}
    9. <button @click="person.name += '!'">修改姓名button>
    10. <button @click="person.child.son.name += '___'">修改儿子姓名button>
    11. h2>
    12. div>
    13. template>
    14. <style scoped>style>

     watchEffect

    watchEffect 函数用于监听传入的函数内访问的所有响应式数据的变化。白话一点就是回调里我用了谁我就监听谁,监听ref定义的响应式数据时,不要忘记 .value ,哥们就是这么智能。

    watch 和 watchEffect 都是监听数据变化的函数,和 react 中的 useState 放入依赖项有着异曲同工之妙。

    例子:切换下拉框中的 name ,模拟请求后台接口 

    1. <template>
    2. <div>
    3. <h1>v3h1>
    4. <el-select v-model="name" placeholder="请选择">
    5. <el-option
    6. v-for="item in info"
    7. :key="item.name"
    8. :label="item.name"
    9. :value="item.name"
    10. >
    11. el-option>
    12. el-select>
    13. <div v-for="item in data" :key="item.id">
    14. {{ item.name }}的个人信息 {{ item }}
    15. div>
    16. div>
    17. template>
    18. <style scoped>style>

    生命周期

    vue3的生命周期稍有变动,增加了 setup 钩子,且销毁前和销毁后的钩子命名更改为 beforeUnmount 和 unmounted,以下代码是验证的一些示例

    App.vue 

    1. <template>
    2. <div>
    3. <h1>
    4. v3
    5. <button @click="isComDestory = false">引入组合式子组件button>
    6. <button @click="isComDestory = true">销毁组合式子组件button>
    7. <button @click="isOptionDestory = false">引入选项式子组件button>
    8. <button @click="isOptionDestory = true">销毁选项式子组件button>
    9. h1>
    10. <Demo v-if="!isComDestory">Demo>
    11. <Demo2 v-if="!isOptionDestory">Demo2>
    12. div>
    13. template>
    14. <style scoped>
    15. button {
    16. padding: 20px;
    17. font-size: 16px;
    18. }
    19. style>

    Demo.vue 

    1. <template>
    2. <div>
    3. <h2>我是子组件1h2>
    4. <h2>{{ sum }} <button @click="sum++">+1button>h2>
    5. div>
    6. template>
    7. <style scoped>
    8. div {
    9. border: 1px solid #ccc;
    10. }
    11. style>

    Demo2.vue

    1. <template>
    2. <div>
    3. <h2>我是子组件2h2>
    4. <h2>{{ sum }} <button @click="sum++">+1button>h2>
    5. div>
    6. template>
    7. <style scoped>
    8. div {
    9. border: 1px solid #ccc;
    10. }
    11. style>

    由于录频录不了控制台,打印结果看下图

     

     响应式数据只读

    vue3提供了两个api,限制响应式数据为只读,不可修改。分别为 readonly(深层只读) 和shallowReadonly (浅层只读)

    1. <template>
    2. <div>
    3. <h1>v3h1>
    4. <h2>readonly: {{ sum }}h2>
    5. <h2>readonly: {{ p1 }}h2>
    6. <h2>shallowReadonly: {{ p2 }}h2>
    7. <button @click="edit">修改深层只读数据button>
    8. <button @click="editShallow">修改浅层只读数据button>
    9. div>
    10. template>
    11. <style scoped>style>

    toRaw 返回代理的源

    toRaw的功能官网的解释很清晰, 可以返回由 reactive()readonly()shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象

    1. <template>
    2. <div>div>
    3. template>
    4. <style scoped>style>

    markRaw 标记对象拒绝代理

    markRaw()将对象标记为不可代理,返回其本身。本身上多了一个 __v_skip 属性表示忽略代理。强行代理代理是无效的,返回的还是其本身而不是响应式对象。

    1. <template>
    2. <div>div>
    3. template>
    4. <style scoped>style>

     provide 与 inject 跨组件传值

    使用 provide 与 inject 进行跨组件传值十分方便。以父子孙为例,父组件 provide ('name',value) 子组件 inject ('name') 即可

     父组件

    1. <template>
    2. <div class="father">
    3. <h1>父组件h1>
    4. <h3>{{ person }}h3>
    5. <Demo>Demo>
    6. div>
    7. template>
    8. <style scoped>
    9. .father {
    10. padding: 10px;
    11. background: orange;
    12. }
    13. style>

     子组件

    1. <template>
    2. <div>
    3. <h2>子组件h2>
    4. <Demo2>Demo2>
    5. div>
    6. template>
    7. <style scoped>
    8. div {
    9. padding: 10px;
    10. background: salmon;
    11. border: 1px solid #ccc;
    12. }
    13. style>

    孙组件

    1. <template>
    2. <div class="sonson">
    3. <h2>孙组件h2>
    4. <h3>{{ person }}h3>
    5. div>
    6. template>
    7. <style scoped>
    8. .sonson {
    9. background: sandybrown;
    10. border: 1px solid #ccc;
    11. }
    12. style>

    判断是否为响应式数据

    • isRef(data)判断data是否是通过ref创建的响应式数据
    • isReactive(data)判断data是否是通过reactive创建的响应式数据
    • isReadonly(data)判断data是否是通过readOnly创建的只读数据
    • isProxy(data)判断data是否为Proxy代理对象

    1. <template>template>
    2. <style scoped>style>

    toRef 和 toRefs 解构响应式数据

    当响应式对象的属性过多且页面用到很多次的时候, toRef 和 toRefs 可以进行响应式解构,解构出来的数据依旧具备响应式的能力。下面的例子是在 

  • <template>
  • <div>
  • <h3>toRef 解构出 person的name ----- {{ personName }}h3>
  • <h3>toRef 解构出 person的age ----- {{ personAge }}h3>
  • <h3>toRef 解构出 person的child的son的name ----- {{ personSonName }}h3>
  • <h3>toRef 解构出 person的name ----- {{ personName }}h3>
  • <h3>toRef 解构出 person的age ----- {{ personAge }}h3>
  • <h3>toRef 解构出 person的child的son的name ----- {{ personSonName }}h3>
  • <h3>toRef 解构出 person的name ----- {{ personName }}h3>
  • <h3>toRef 解构出 person的age ----- {{ personAge }}h3>
  • <h3>toRef 解构出 person的child的son的name ----- {{ personSonName }}h3>
  • <button @click="personName += '!'">修改person的namebutton>
  • <button @click="personAge += 1">修改person的agebutton>
  • <button @click="personSonName += '*'">person的child的son的namebutton>
  • div>
  • template>
  • <style scoped>style>
  •  

     toRefs

    1. <template>
    2. <div>
    3. <h3>toRefs 解构出 person的name ----- {{ name }}h3>
    4. <h3>toRefs 解构出 person的age ----- {{ age }}h3>
    5. <h3>toRefs 解构出 person的child的son的name ----- {{ child.son.name }}h3>
    6. <h3>toRefs 解构出 person的name ----- {{ name }}h3>
    7. <h3>toRefs 解构出 person的age ----- {{ age }}h3>
    8. <h3>toRefs 解构出 person的child的son的name ----- {{ child.son.name }}h3>
    9. <h3>toRefs 解构出 person的name ----- {{ name }}h3>
    10. <h3>toRefs 解构出 person的age ----- {{ age }}h3>
    11. <h3>toRefs 解构出 person的child的son的name ----- {{ child.son.name }}h3>
    12. <button @click="name += '!'">修改person的namebutton>
    13. <button @click="age += 1">修改person的agebutton>
    14. <button @click="child.son.name += '*'">person的child的son的namebutton>
    15. div>
    16. template>
    17. <style scoped>style>

     新组件

    Fragment

    在vue2中模板标签内必须包裹一层根标签,vue3中则不需要。vue3会为多个跟标签包裹一层Fragment。这是写法上的优化。前面很多例子的代码中我都包裹了一层根标签,这是由于我的编辑器的eslint的问题,去掉根标签也可以正常运行。

    有根标签的编译结果

     没有根标签的编译结果

    Teleport 

    Teleport 组件的功能是将元素渲染到任意的页面位置中,直接扣过来官网的例子。

    下列代码主要表达的是:点击按钮将弹框插入到 body 标签下 

     ModalButton.vue

    1. <script>
    2. import { ref } from 'vue'
    3. export default {
    4. name: 'modal-button',
    5. setup() {
    6. const modalOpen = ref(false)
    7. return {
    8. modalOpen,
    9. }
    10. },
    11. }
    12. script>
    13. <style>
    14. .modal {
    15. position: absolute;
    16. top: 0;
    17. right: 0;
    18. bottom: 0;
    19. left: 0;
    20. background-color: rgba(0, 0, 0, 0.5);
    21. display: flex;
    22. flex-direction: column;
    23. align-items: center;
    24. justify-content: center;
    25. }
    26. .modal div {
    27. display: flex;
    28. flex-direction: column;
    29. align-items: center;
    30. justify-content: center;
    31. background-color: white;
    32. width: 300px;
    33. height: 300px;
    34. padding: 5px;
    35. }
    36. style>

     App.vue

    1. <script lang="ts">
    2. import ModalButton from './ModalButton.vue'
    3. export default {
    4. setup() {
    5. return {}
    6. },
    7. components: {
    8. ModalButton,
    9. },
    10. }
    11. script>

     

    Suspense

    Suspense 组件用于将异步组件包裹,提供一个过渡UI在异步完成之前。

    Suspense 组件提供两个插槽:

    1. #default   默认插槽 存放异步组件
    2. #fallback  备用插槽 存放过渡UI

    异步组件:

    1. 带有异步 setup() 钩子的组件。这也包含了使用 

  • <template>
  • <div>
  • <h2>异步组件h2>
  • <h3>{{ res }}h3>
  • div>
  • template>
  • <style scoped>
  • div {
  • padding: 10px;
  • background: salmon;
  • border: 1px solid #ccc;
  • }
  • style>
  • 组合式函数 

    组合式api的优点之一式将单个功能代码组合在一起,如果是可以复用的逻辑,那么可以抽离为一个组合式函数或者称为自定义hook,在需要该逻辑的地方导入即可

    例子:提供一个组合函数,此函数在当前组件中监听鼠标移动事件,并将坐标显示出来,组件卸载前清掉事件。

    App.vue

    1. <script setup>
    2. import { ref } from 'vue'
    3. import Demo from './Demo.vue'
    4. import Demo2 from './Demo2.vue'
    5. const Demo1Visible = ref(true)
    6. const Demo2Visible = ref(true)
    7. script>

    Demo1.vue

    1. <script setup>
    2. import useMouse from './mouse'
    3. const { x, y } = useMouse('.demo_1')
    4. script>
    5. <style scoped>
    6. .demo_1 {
    7. height: 100px;
    8. background: salmon;
    9. }
    10. style>

    Demo2.vue 

    1. <script setup>
    2. import useMouse from './mouse'
    3. const { x, y } = useMouse('.demo_2')
    4. script>
    5. <style scoped>
    6. .demo_2 {
    7. height: 100px;
    8. background: salmon;
    9. }
    10. style>

     

    全局的api及指令的变动

    API 参考 | Vue.js,大家先自行参考,后续深入学习时再进行更新。

    结语

  • 相关阅读:
    DenseNet 浅析
    天猫店铺所有商品数据接口(Tmall.item_search_shop)
    LVS集群(Linux Virtual server)
    4.4 - 数组
    与创新者同行,Apache Doris in 2023
    Linux 命令行——格式化输出
    Vue学习笔记(九):组件化编程
    人工智能、深度学习、机器学习书目推荐
    Linux系统安装Anaconda
    【优化调度】基于改进遗传算法的公交车调度排班优化的研究与实现(Matlab代码实现)
  • 原文地址:https://blog.csdn.net/m0_58239318/article/details/127899020