Vue3 组合式API

1.入口

  • 创建实例时,配置setup方法,然后其内部书写组合式API代码,通过组合式API生产的数据和返回,需要暴漏出去才能给HTML使用
<script>
    //组合式(解构赋值)
    const {createApp,ref} = Vue
    var app = Vue.createApp({
        //所有的代码写入到steup方法中
        setup(){
            //创建响应式数据
            const title = ref('实例2')

            function modifyTitle(){
                //在 JavaScript 中需要 .value
                title.value = "实例2被修改"
            }

            //返回定义的数据和方法
            return {
                title,
                modifyTitle
            }
        }
    })
    app.mount('#app2')
</script>
  • 单文件组件:将setup声明写到script标签中,然后直接组合式API代码
<template>
    <div>
        <h1>{{ title }}</h1>
        
    </div>
</template>

<script setup>
import { ref } from 'vue'

const title = ref("组件标题")
</script>

<style scoped>
</style>

2.ref()和reactive()

  • 官方推荐使用ref()函数来声明响应式状态,可以传入支持传入简单数据和引用类型的数据,返回一个数据代理对象。通过对象的value属性读取数值(再模版中使用时可以忽略value属性)
<h2>{{title}}</h2>
<h3>{{person.name}} - {{person.age}}</h3>

const title = ref('实例2')

const person = ref({
      name:"张三", age: 20
})
  • Ref 会使它的值具有深层响应性,传入对象或者数组这种应用类型的数据时,修改对象属性或者数组子元素都可以触发视图更新
const person = ref({
     name:"张三", age: 20
})

const list = ref([1,2,3])

function modifyTitle(){
     //在 JavaScript 中需要 .value
     person.value.age++

     list.value[0] = 5
}
  • reactive() 返回的是一个原始对象的 Proxy,所以他只接受对象类型的数据 (对象、数组和如 Map、Set 这样的集合类型),通过他创建的数据访问时无需通过value属性
const person2 = reactive({
     name:"李四", age: 18
})
const list = reactive([1,2,3])

function modifyTitle(){
    person2.age++
    list[0] = 5
}
  • ref和reactive底层都是调用Proxy()来创建响应式数据,因为Proxy()不支持简单数据,所以ref会对传入的数据用对象进行包裹,用value来存放传入原数据,这样再调用Proxy()就可以返回响应式数据,正因为多了一层包裹,所以他支持简单数据类型而reactive不支持,也正因为如此,所以ref返回的数据需要用value进行操作
  • 打印对象:
const title = ref('实例2')
const person = ref({
      name:"张三", age: 20
})
const list = reactive({
      name:"张三", age: 20
})

  • 嵌套调用:当ref嵌套到reactive内部使用时,会涉及到ref的解包机制,平时估计用不上,使用时再查看文档

3.计算属性 computed

  • 通过computed()来创建并返回一个代理对象
<body>
    <div id="app" v-cloak>
        <div class="form-group">
            <label>姓:</label>
            <input type="text" v-model="person.firstName" class="form-control" placeholder="请输入姓">
        </div>
        <div class="form-group">
            <label>名:</label>
            <input type="text" v-model="person.lastName" class="form-control" placeholder="请输入名">
        </div>
        <div class="form-group">
            <label>姓名:</label>
            <h2>{{fullName}}</h2>
        </div>
    </div>
</body>
<script>
    //组合式(解构赋值)
    const {createApp, ref, reactive,computed} = Vue
    var app = Vue.createApp({
        //所有的代码写入到steup方法中
        setup(){
            const person = ref({
                firstName: "",
                lastName: ""
            })
            const fullName = computed(()=>{
                return person.value.firstName + person.value.lastName
            })
            //返回定义的数据和方法
            return {
                person,
                fullName
            }
        }
    })
    app.mount('#app')
</script>

4.监听器 watch

  • 通过watch()来监听数据变化,并执行对应的回调,参数一为需要监听的响应式数据(同时监听多个数据用数组进行包裹),参数二为需要执行的回调函数
<body>
    <div id="app" v-cloak>
        <input type="text" v-model="title">
    </div>
</body>
<script>
    //组合式(解构赋值)
    const {createApp, ref, reactive,watch} = Vue
    var app = Vue.createApp({
        //所有的代码写入到steup方法中
        setup(){
            const title = ref("测试")

            //注册监听器
            watch(title,(newValue, oldValue)=>{
                console.log('title发送变化,最新值为',title.value)
            })
            
            //返回定义的数据和方法
            return {
                title
            }
        }
    })
    app.mount('#app')
</script>

  • 深度监听:如果监听对象是复杂类型的数据,例如对象,默认不会监听其内部的变化,需要配置deep->true
 //注册监听器
watch(person,(newValue, oldValue)=>{
       console.log('person发生变化,最新值为',person.value)
},{
       deep: true
})
//监听内部某个属性
watch(()=> person.value.age,(newValue, oldValue)=>{
       console.log('person.age发生变化,最新值为',person.value.age)
})
  • 其他配置:一次性监听(once: true),即时回调(immediate: true),触发时机(flush: 'post'->Dom更新后,flush: 'sync'->Dom更新前,默认)
  • watchEffect(): 和计算属性类似,自动识别回调中的依赖对象作为监听,发生改变则触发回调,传入的回调会立即执行一次
<body>
    <div id="app" v-cloak>
        <input type="text" v-model="name">
        <input type="number" v-model="age">
    </div>
</body>
<script>
    //组合式(解构赋值)
    const {createApp, ref, reactive,watchEffect} = Vue
    var app = Vue.createApp({
        //所有的代码写入到steup方法中
        setup(){
            const name = ref("张三")
            const age = ref(20)

            //注册监听器
            watchEffect(()=>{
                var person = {name: name.value,age:age.value}
                console.log('person发生变化',name.value,age.value)
            })
            
            //返回定义的数据和方法
            return {
                name,age
            }
        }
    })
    app.mount('#app')
</script>

5.组件数据交互

  • 父->子:通过defineProps()方法来定义要接收的参数,这些定义的参数就可以使用了,提供数组语法和对象语法(二选一)
<script setup lang="ts">
//声明要接收的字段,(数组语法,提供属性名即可)
//defineProps(['msg','num'])

//声明要接收的字段,(对象语法,可以限制属性值的类型)
const props = defineProps({
    msg: String,
    num: Number
})
</script>
  • 子->父:用法与vue2.x一致,不过在setup作用域中,需要使用 defineEmits() 对事件进行注册,然后使用返回的emit对象触发事件
<script setup lang="ts">
//声明自定义事件
const emit = defineEmits(['sayHello'])
//触发事件
const handleSayHello = ()=>{
    emit('sayHello','someone')
}
</script>
  • 父->后代:通过依赖注入的形式,让所有后代组件都可以访问父组件的数据
//全局提供,所有组件都可以使用
import { createApp } from 'vue'
const app = createApp({})
app.provide()
<script setup lang="ts">
//组件提供,只能给后代组件使用
import { ref, provide } from 'vue'

//将数据暴漏给后代组件(参数一:名称,参数二:值,可以是任意类型,包括响应式的状态)
provide('home_title',"Home title")
provide('sayHello',()=>{
    console.log("sayHello define in Home")
})
const num = ref(100)
//暴漏ref
provide('home_num', num)
</script>
<script setup lang="ts">
//接收方
import { inject } from 'vue'

//接收祖先组件暴漏的数据(注入)
const home_title = inject('home_title')
const sayHello = inject('sayHello')
const home_num = inject('home_num')
console.log('home_title',home_title)
console.log('sayHello',sayHello)
console.log('home_num',home_num)

//不确定,或者不存在的值给个默认值
const _unknown = inject('_unknown',null)
console.log('_unknown',_unknown)

const setNum = () => {
    //不推荐更改祖先数据,如果需要,最好让祖先组件暴漏一个转码修改数据的办法
    home_num.value++
}
</script>

6.模版引用

  • 通过ref访问Dom:给Dom挂载一个ref属性,值为ref(null)返回的对象,则可通过这个ref对象直接访问dom
<body>
    <div id="app" v-cloak>
        <h2 ref="h2">{{title}}</h2>
    </div>
</body>
<script>
    //组合式(解构赋值)
    const {createApp, ref, reactive,onMounted} = Vue
    var app = Vue.createApp({
        //所有的代码写入到steup方法中
        setup(){
            const title = ref("测试")
            const h2 = ref()

            onMounted(()=>{
                //onMounted 测试
                console.log('onMounted',title.value)

                console.log(h2.value)
            })
            //返回定义的数据和方法
            return {
                title,h2
            }
        }
    })
    app.mount('#app')
</script>

  • 在使用了 script setup 的组件是默认私有的,一个父组件无法访问到一个使用了 script setup 的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露
<script setup>
import { ref } from 'vue'

const a = 1
const b = ref(2)

// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
  a,
  b
})
</script>

7.生命周期

  • 通过暴漏出来的API,传入生命周期函数,在不同阶段执行对应的代码
<script>
    //组合式(解构赋值)
    const {createApp, ref, reactive,onMounted} = Vue
    var app = Vue.createApp({
        //所有的代码写入到steup方法中
        setup(){
            const title = ref("测试")

            onMounted(()=>{
                //onMounted 测试
                console.log('onMounted',title.value)
            })
            //返回定义的数据和方法
            return {
                title,
            }
        }
    })
    app.mount('#app')
</script>
  • steup执行时机早于beforeCreate,他能用的生命周期API如下
生命周期钩子 说明
onBeforeMount() 在组件被挂载之前被调用,此时无法操作Dom
onMounted() 在组件挂载完成后执行
onBeforeUpdate() 在组件即将因为响应式状态变更而更新其 DOM 树之前调用
onUpdated() 在组件因为响应式状态变更而更新其 DOM 树之后调用
onBeforeUnmount() 在组件实例被卸载之前调用
onUnmounted() 在组件实例被卸载之后调用
posted @ 2024-06-27 17:31  ---空白---  阅读(47)  评论(0编辑  收藏  举报