IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vue3 常用composition API(组合API)详解与代码 -> 正文阅读

[JavaScript知识库]Vue3 常用composition API(组合API)详解与代码

本篇文章基于该视频: 尚硅谷教学Vue2与Vue3

常用composition API

setup函数

初始

新的配置项 值为一个函数
组件中的所有数据、方法等,均要配置在setup中

setup函数有两个返回值:
①若返回一个对象,则对象中的属性、方法,在模板中均可使用。(重点)
②若返回一个渲染函数,则可以自定义渲染内容。(了解)

?
vue2中可读取setup中的数据,若重名,则setup优先。(切记不可混用)
不能是一个async函数,因为async函数返回值是promise,在模板中看不到return对象中的属性

<template>
  <div>
    <h1>{{ msg }}</h1>
    <h1>{{uname}}</h1>
    <button @click="sayUname">alert</button>
  </div>
</template>

<script>
import {h} from 'vue'
export default {
  name: 'Test',
  props: {
    msg: String
  },
  setup() {
    let uname = 'lyz'
    function sayUname() {
      alert(`我是${uname}`)
    }

    // 返回一个对象(常用)
    return {uname,sayUname}
    // 返回一个函数
    // return () =>h('h1', 'LYZ')
  }
}
</script>

注意点

执行时机:
在beforeCreate(通过配置项)之前执行 此时this为undefined
在这里插入图片描述

参数:
props:值为对象 包含组件外部传递过来的且组件内部声明接收了的属性(父子传值)
context:上下文对象
① attrs:值为对象 包括:组件外部传递过来但未在props配置中声明的属性 相当于this.$attrs
② slots:收到插槽内容 相当于this.$slots
③ emit:分发自定义事件的函数 相当于this.$emit

// 父组件
<template>
  <Test msg="Welcome to My App" @toTest="toTestOne">
    <template v-slot:qwe>
      <span>qwe</span>
    </template>
  </Test>
</template>

<script>
import Test from './components/Test.vue'

export default {
  name: 'App',
  components: {
    Test
  },
  setup() {
    function toTestOne(el) {
      alert(`收到参数${el}`)
    }
    return {toTestOne}
  }
}
</script>

// 子组件
<template>
  <div>
    <button @click="toTest">test</button>
  </div>
</template>

<script>
export default {
  name: 'Test',
  props: {
    msg: String
  },
  emits:['toTest'],
  setup(props, context) {
    console.log(context.slots)
    function toTest() {
      console.log(context)
      context.emit('toTest', 'lyz')
    }
    return {toTest}
  }
}
</script>

<style scoped>
</style>

响应式

ref函数(基本数据类型)

Vue2中所有data都会被捕捉,加入setter、getter 带来性能开销
ref()可以选择哪些数据被捕捉 哪些无需监听

基本数据类型:get set
引用数据类型:Proxy (ref()—>reactive())

<template>
  <div>
    <h1>{{ msg }}</h1>
    <h1>{{uname}}</h1>
    <h2>{{uage}}</h2>
    <h3>{{job.type}}</h3>
    <h3>{{job.salary}}</h3>
    <button @click="editInfo">修改</button>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  name: 'Test',
  props: {
    msg: String
  },
  setup() {
    let uname = ref('lyz')
    let uage = ref('22')
    let job = ref({
        type: '前端工程师',
        salary: '30k'
    })
    function editInfo() {
      console.log(job);
      uname.value = 'my'
      uage.value = 23
      job.value.type = '后端工程师',
      job.value.salary = '50k'
    }

    return {uname, uage, job,editInfo}
  }
}
</script>

reactive函数(引用数据类型)

引用类型的响应式数据
let 代理对象 = reacvtive(源对象)
内部基于Proxy实现,通过代理对象操作源对象内部数据进行操作

<template>
  <div>
    <h1>{{ msg }}</h1>
    <h1>{{person.uname}}</h1>
    <h2>{{person.age}}</h2>
    <h3>{{person.type}}</h3>
    <button @click="editInfo">修改</button>
  </div>
</template>

<script>
import { reactive } from 'vue'
export default {
  name: 'Test',
  props: {
    msg: String
  },
  setup() {
    let person = reactive({
        uname: 'lyz',
        age: 22,
        type: '前端工程师'
    })
    function editInfo() {
        person.uname = 'my'
        person.age = 23
        person.type = '后端工程师'
    }
    return {person, editInfo}
  }
}
</script>

返回一个data对象 类似于vue2

<template>
  <div>
    <h1>{{data.person.uname}}</h1>
  </div>
</template>

<script>
import { reactive } from 'vue'
export default {
  name: 'Test',
  props: {
    msg: String
  },
  setup() {
    let data = reactive({
      person: {
        uname: 'lyz'
      }
    })
    return {data}
  }
}
</script>

<style scoped>
</style>

响应式原理

Vue2.0X响应式原理

对象:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
数组:通过重写数组的一系列方法来完成拦截。(对数组的变更方法进行了包裹)
[...].splice() vue.set() this.$set() vue.delete() this.$delete()

存在问题?
① 新增 删除界面不会更新;
② 直接通过下标修改数组,界面不会自动更新。

        let p = new Proxy(person, {
            // 有人读取某个属性时调用
            get(target, propName) {
                console.log('get')
                return target[propName]
            },
            // 有人 修改 或 追加 某个属性时调用
            set(target, propName, value) {
                console.log('set')
                target[propName] = value
            },
            deleteProperty(target, propName) {
                console.log('delete')
                return delete target[propName]
            }
        })

Vue3.0X响应式原理

通过Proxy(代理)拦截对象中任意属性的变化:读 写 添加 删除等
通过Reflect(反射) 对被代理对象(源对象)的操作

           let p = new Proxy(person, {
            // 有人读取某个属性时调用
            get(target, propName) {
                console.log('get')
                return Reflect.get(target, propName)
            },
            // 有人 修改 或 追加 某个属性时调用
            set(target, propName, value) {
                console.log('set')
                target[propName] = value
                Reflect.set(target, propName, value)
            },
            deleteProperty(target, propName) {
                console.log('delete')
                return Reflect.deleteProperty(target, propName)
            }
        })

computed

与vue2配置功能一致
写在setup中

<template>
  <div>
    <input v-model="person.firstName"/><br>
    <input v-model="person.lastName"/><br>
    <input v-model="person.fullName"/>
  </div>
</template>

<script>
import {reactive,computed} from 'vue'
export default {
  name: 'Test',
  props: {
    msg: String
  },
  setup() {
    let person = reactive({
      firstName: 'lyz',
      lastName: 'my'
    })

    // 简写 只读
    // person.fullName = computed(() =>{
    //   return person.firstName + '-' + person.lastName
    // })

    // 完整
    person.fullName = computed({
      get() {
         return person.firstName + '-' + person.lastName
      },
      set(value) {
        let fullName = value.split('-')
        person.firstName = fullName[0]
        person.lastName = fullName[1]
      }
    })
    return {person}
  }
}
</script>

watch

与vue2配置功能一致
写在setup中

不同情况

情况一 监视ref所定义的一个响应式数据

情况二 监视ref所定义的多个响应式数据

情况三 监视reactive所定义的一个响应式数据
▲此处无法获取正确的oldValue
强制开启了深度监听(deep无效)

情况四 监视reactive所定义的一个响应式数据的某个属性
相当于监视的基本数据类型

情况五 监视reactive所定义的一个响应式数据的某些属性

特殊情况
监视的是reactive定义的对象中的对象属性 则deep有效
此处无法获取正确的oldValue

<template>
  <div>
    <h1>{{sum}}</h1>
    <button @click="sum++">sum+1</button>
    <hr>
    <h1>{{hello}}</h1>
    <button @click="hello+='!'">hello+!</button>
    <hr>
    <h1>{{person.name}}</h1>
    <h1>{{person.age}}</h1>
    <h1>{{person.job.j1.salary}}</h1>
    <button @click="person.name+='!'">姓名+!</button>
    <button @click="person.age++">年龄+1</button>
    <button @click="person.job.j1.salary++">工资+1000</button>
  </div>
</template>

<script>
import {watch, ref, reactive} from 'vue'
export default {
  name: 'Test',
  props: {
    msg: String
  },
  setup() {
    let sum = ref(0),
        hello = ref('hello')
    // 情况一 监视ref所定义的一个响应式数据
    // immediate是否立即监视
    // watch(sum, (newValue, oldValue) =>{
    //   console.log(newValue, oldValue)
    // }, {immediate: true})

    // 情况二 监视ref所定义的多个响应式数据
    // watch([sum, hello], (newValue, oldValue) =>{
    //   console.log(newValue, oldValue)
    // })

    let person = reactive({
      name: 'lyz',
      age: 21,
      job: {
        j1: {
          salary: 10000
        }
      }
    })
    /* 情况三 监视reactive所定义的一个响应式数据 
       ▲此处无法获取正确的oldValue
        强制开启了深度监听(deep无效)
    */
    // watch(person, (newValue, oldValue) =>{
    //   console.log('person被监听了', newValue, oldValue)
    // })

    // 情况四 监视reactive所定义的一个响应式数据的某个属性
    // 相当于监视的基本数据类型
    // watch(() =>person.age, (newValue, oldValue) =>{
    //   console.log('person的age被监听了', newValue, oldValue)
    // })

    // 情况五 监视reactive所定义的一个响应式数据的某些属性
    // watch([() =>person.age, () =>person.name], (newValue, oldValue) =>{
    //   console.log('person的age或name被监听了', newValue, oldValue)
    // })

    /* 特殊情况
       监视的是reactive定义的对象中的对象属性 则deep有效
       此处无法获取正确的oldValue
    */
    watch(() =>person.job, (newValue, oldValue) =>{
      console.log('person的job被监听了', newValue, oldValue)
    }, {deep: true})


    return {sum, hello, person}
  }
}
</script>

监听ref定义的数据(基本或引用数据类型)

基本数据类型:监听的是RefImpl对象
引用数据类型:监听的是Proxy对象 实际为reactive定义的数据

<template>
  <div>
    <h1>{{sum}}</h1>
    <button @click="sum++">sum+1</button>
    <hr>
    <h1>{{hello}}</h1>
    <button @click="hello+='!'">hello+!</button>
    <hr>
    <h1>{{person.name}}</h1>
    <h1>{{person.age}}</h1>
    <h1>{{person.job.j1.salary}}</h1>
    <button @click="person.name+='!'">姓名+!</button>
    <button @click="person.age++">年龄+1</button>
    <button @click="person.job.j1.salary++">工资+1000</button>
  </div>
</template>

<script>
import {watch, ref} from 'vue'
export default {
  name: 'Test',
  props: {
    msg: String
  },
  setup() {
    let sum = ref(0),
        hello = ref('hello'),
        person = ref({
          name: 'lyz',
          age: 21,
          job: {
            j1: {
              salary: 10000
            }
          }
        })
    /* 
      监听的sum为RefImpl对象 
      不能监听sum.value:sum.value为值
    */ 
    watch(sum, (newValue, oldValue) =>{
      console.log('sum值变化', newValue, oldValue)
    })

    /*
      person是由ref定义的响应式数据
      person.value是Proxy对象 为reactive所定义的数据
    */
    // watch(person.value, (newValue, oldValue) =>{
    //   console.log('sum值变化', newValue, oldValue)
    // })

    // person为RefImpl对象 只能检测到地址值 浅层次
    watch(person, (newValue, oldValue) =>{
      console.log('person变化', newValue, oldValue)
    }, {deep: true})

    return {sum, hello, person}
  }
}
</script>

watchEffect函数

与watch区别

watch:既要指名监听的属性。也要指名监听回调函数
watchEffect:无需指名监听那个属性,只监听回调中用哪个属性就监视哪个属性

与computed区别

有点类似,但是
computed:注重计算出的结果,回调函数有返回值
watchEffect:更注重过程,回调函数的而函数体

eg:报销流程、ajax请求

<template>
  <div>
    <h1>{{sum}}</h1>
    <button @click="sum++">sum+1</button>
    <hr>
    <h1>{{hello}}</h1>
    <button @click="hello+='!'">hello+!</button>
    <hr>
    <h1>{{person.name}}</h1>
    <h1>{{person.age}}</h1>
    <h1>{{person.job.j1.salary}}</h1>
    <button @click="person.name+='!'">姓名+!</button>
    <button @click="person.age++">年龄+1</button>
    <button @click="person.job.j1.salary++">工资+1000</button>
  </div>
</template>

<script>
import {ref, reactive, watchEffect} from 'vue'
export default {
  name: 'Test',
  props: {
    msg: String
  },
  setup() {
    let sum = ref(0),
        hello = ref('hello'),
        person = reactive({
          name: 'lyz',
          age: 21,
          job: {
            j1: {
              salary: 10000
            }
          }
        })
    watchEffect(() =>{
      let sum_w = sum.value
      let salary_w = person.job.j1.salary
      console.log('watchEffect监听了', sum_w, salary_w)
    })

    return {sum, hello, person}
  }
}
</script>

生命周期

与vue2相比,修改了最后两个钩子函数,改为beforeUnmount、unmounted

组合式api先于配置项出现,最好用一种形式
beforeCreate/created对应 组合式api setup函数
在这里插入图片描述

配置项

<template>
  <div>
    <h1>{{sum}}</h1>
    <button @click="sum++">sum+1</button>
  </div>
</template>

<script>
import {ref} from 'vue'
export default {
  name: 'Test',
  setup() {
    let sum = ref(0)

    return {sum}
  },
  // 通过配置项使用生命周期钩子函数
   beforeCreate() {
    console.log('-----beforeCreate-----')
  },
  created() {
    console.log('-----created-----')
  },
  beforeMount() {
    console.log('-----beforeMount-----')
  },
  mounted() {
    console.log('-----mounted-----')
  },
  beforeUpdate() {
    console.log('-----beforeUpdate-----')
  },
  updated() {
    console.log('-----updated-----')
  },
  beforeUnmount(){
    console.log('-----beforeUnmount-----')
  },
  unmounted() {
    console.log('-----unmounted-----')
  }
}
</script>

组合式api

<template>
  <div>
    <h1>{{sum}}</h1>
    <button @click="sum++">sum+1</button>
  </div>
</template>

<script>
import {ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from 'vue'
export default {
  name: 'Test',
  setup() {
    let sum = ref(0)
    //组合式api使用生命周期函数
    onBeforeMount(()=>{
      console.log('-----onBeforeMount-----')
    })
    onMounted(()=>{
      console.log('-----onMounted-----')
    })
    onBeforeUpdate(()=>{
      console.log('-----onBeforeUpdate-----')
    })
    onUpdated(()=>{
      console.log('-----onUpdated-----')
    })
    onBeforeUnmount(()=>{
      console.log('-----onBeforeUnmount-----')
    })
    onUnmounted(()=>{
      console.log('-----onUnmounted-----')
    })
    return {sum}
  }
}
</script>

hook函数

本质上是一个函数,把setup函数中使用的Composition API进行封装,类似于Vue2.X中的mixin
自定义hook函数的优势:复用代码,让setup中的逻辑清晰易懂

//src/hook/usePiont
import { reactive, onBeforeMount, onBeforeUnmount} from "vue"

export default function() {
    // '鼠标'位置数据
    let point = reactive({
        x: 0,
        y: 0
    })
    //  '鼠标'位置方法
    function savePiont(e) {
        point.x = e.pageX
        point.y = e.pageY
        console.log(e.pageX, e.pageY)
    }
    //  '鼠标'位置相关生命周期钩子函数
    onBeforeMount(() =>{
        window.addEventListener('click', savePiont)
    }) 
    onBeforeUnmount(() =>{
        window.removeEventListener('click', savePiont)
    })
    return point
}
// src/components/Test.vue
<template>
  <div>
    <h1>{{sum}}</h1>
    <button @click="sum++">sum+1</button>
    <hr>
    <h2>X:{{point.x}} Y:{{point.y}}</h2>
  </div>
</template>

<script>
import {ref} from 'vue'
import usePoint from '../hook/usePoint.js'
export default {
  name: 'Test',
  setup() {
    let sum = ref(0)
    let point = usePoint()
    return {sum, point}
  }
}
</script>

toRef函数

作用:创建一个ref对象 其value指向另一个对象中的某个属性
console.log(toRef(p, 'uname'))
在这里插入图片描述
应用:要将响应式对象中的某个属性单独提供给外部使用
扩展:toRefs于其功能一致,但可批量创建多个ref对象
console.log(toRefs(p, 'uname'))在这里插入图片描述

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-11 17:25:59  更:2021-10-11 17:26:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 19:18:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码