Composition API

严峰
2023-12-01

Composition API


Composition API也称为组合式API,实现代码的共享以及重用

Vue3.0 暴露变量必须 return 出来,template中才能使用;

Vue3.2 中 只需要在 script 标签上加上 setup 属性,组件在编译的过程中代码运行的上下文是在 setup() 函数中,无需return,template可直接使用


Composition-api提供的函数


setup

新的 setup 组件选项在创建组件之前执行,一旦 props 被解析,并充当合成 API 的入口点

由于在执行 setup 时尚未创建组件实例,因此在 setup 选项中没有 this。

这意味着,除了props 之外,将无法访问组件中声明的任何属性——本地状态、计算属性或方法。


setup()接收两个参数 propscontext

props是响应的 当传入的props更新时会同步更新

但是因为 props 是响应式的,所以不能使用 ES6 解构,因为它会消除 prop 的响应性。

如果需要解构 prop,可以通过使用 setup 函数中的 toRefs 来安全地完成

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}

context暴露三个属性

export default {
  setup(props, context) {
    // Attribute (非响应式对象)
    // 可以读取所有除了在props当中声明的自定属性以外传递的 自定义属性的值 this.$attrs
    // 即所有从父组件传递过来的属性 除去使用props中声明过的
    console.log(context.attrs)

    // 插槽 (非响应式对象)
    console.log(context.slots)

    // 触发事件 (方法)
    // 用于触发绑定的自定义事件 this.$emit
    console.log(context.emit)
  }
}

执行 setup 时组件实例尚未被创建。因此只能访问props,attrs,slots,emit属性



ref

用来定义响应式的 字符串、 数值、 数组、Bool类型

对于ref对象而言,读写它的值都需要通过它的value属性

但在模板中使用时不需要.value属性,模板会自行的对于它的结构进行解包

export default {
    data() {
        return {
        }
    },
    setup() {
        let msg = ref("这是setup中的msg");
        let list = ref(["1", "2", "3"])
        let updateMsg = () => {
            alert("触发方法");
            msg.value = "改变后的值"
        }
        return {
            msg,
            list,
            updateMsg
        }
    },
}



reactive

用来定义响应式的对象

export default {
    data() {
        return {
        }
    },
    setup() {
        let msg = ref("这是setup中的msg");
        let setupData = reactive({
            title: "reactive定义响应式数据的title",
            userinfo: {
                username: "张三",
                age: 20
            }
        })
        let updateMsg = () => {
            alert("触发方法");
            msg.value = "改变后的值"
        }
        let updateTitle = () => {
            alert("触发方法");
            setupData.title = "我是改变后的title"
        }
        return {
            msg,
            setupData,
            updateMsg,
            updateTitle
        }
    },
}

要改变ref定义的属性名称需要通过 属性名称.value来修改

要改变reactive中定义的对象名称可以直接修改


watchEffect

在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。

与侦听器watch类似 不过在组件创建时会默认执行一次

export default {
    name: "WatchEffect",
    setup() {
        const data = reactive({
            count: 1,
            num: 1
        });
        const stop = watchEffect(() => console.log(`侦听器:${data.count}`));
        	// data.count变化时才会执行
        setInterval(() => {
            data.count++;
        }, 1000);
        return {
            data,
            stop
        };
    },
};



watch

与原先的侦听器类似,接收三个参数,第一个时要监听的状态,第二个是处理函数,第三个是配置项

对比watchEffect,watch允许我们:

懒执行,也就是说仅在侦听的源变更时才执行回调;

更明确哪些状态的改变会触发侦听器重新运行;

访问侦听状态变化前后的值

export default {
    name: "Watch",
    setup() {
    	const a = ref('cc')
        const data = reactive({
            count1: 0,
            count2: 0
        });
        
        // 侦听单个数据源
        // 不能直接侦听data.count1
        const stop1 = watch(data, () =>
            console.log("watch1", data.count1, data.count2)
        );

		// 监听ref属性且进行配置
        watch(a, () => {
		  console.log(d.value)
		}, {
		  immediate: true
		})
        
        // 侦听多个数据源
        const stop2 = watch([data], () => {
            console.log("watch2", data.count1, data.count2);
        });
        setInterval(() => {
            data.count1++;
        }, 1000);
        return {
            data,
            stopAll: () => {
                stop1();
                stop2();
            },
        };
    },
};



computed

与原先的计算属性相同

export default {
    name: "解构响应式对象数据",
    setup() {
        const user = reactive({
            firstName: "",
            lastName: "",
        });
        const fullName = computed(() => {
            return user.firstName + " " + user.lastName
        })
        return {
            ...toRefs(user),
            fullName
        };
    },
};



toRefs

解构响应式对象数据而不丢失响应性

响应式对象数据如果通过es6…运算符解构就会消除响应性

把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref ,和响应式对象 property 一一对应

export default {
    name: "解构响应式对象数据",
    setup() {
        const user = reactive({
            username: "张三",
            age: 10000,
        });
        return {
            ...toRefs(user)
        };
    },
};



readonly

传入一个对象(响应式或普通)或ref,返回一个原始对象的只读代理。一个只读的代理是“深层的”

对象内部任何嵌套的属性也都是只读的

export default {
  name: "Readonly",
  setup() {
    const original = reactive({ count: 0 });
    const copy = readonly(original);
    setInterval(() => {
      original.count++;
      copy.count++; 
      // 报警告,Set operation on key "count" failed: target is readonly. Proxy {count: 1}
    }, 1000);
    return { original, copy };
  },
};



生命周期hooks

选项式 APIHook inside setup
beforeCreateNot needed*
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered

因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。

换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

export default {
  setup() {
    // mounted
    onMounted(() => {
      console.log('Component is mounted!')
    })
  }
}



Provider Inject

provide 和 inject 对父组件可以作为其所有子组件的依赖项提供程序,而不管组件层次结构有多深。

父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这个数据


在非组合式api中

可以传值 但子组件的值改变不能同步到父组件

<!-- src/components/MyMap.vue -->
<template>
  <MyMarker />
</template>

<script>
import MyMarker from './MyMarker.vue'

export default {
  components: {
    MyMarker
  },
  provide: {
    location: 'North Pole',
    geolocation: {
      longitude: 90,
      latitude: 135
    }
  }
}
</script>
<!-- src/components/MyMarker.vue -->
<script>
export default {
  inject: ['location', 'geolocation']
}
</script>

组合式api中

可以传值 且子组件的值改变可以同步到父组件

provide 函数允许通过两个参数定义 property:

property 的 name ( 类型)
property 的 value

<!-- src/components/MyMap.vue -->
<template>
  <MyMarker />
</template>

<script>
import { provide } from 'vue'
import MyMarker from './MyMarker.vue

export default {
  components: {
    MyMarker
  },
  setup() {
    provide('location', 'North Pole')
    provide('geolocation', {
      longitude: 90,
      latitude: 135
    })
  }
}
</script>

inject 函数有两个参数:

要注入的 property 的名称,一个默认的值 (可选)

<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'

export default {
  setup() {
    const userLocation = inject('location', 'The Universe') // 缺省值(默认值):The Universe
    const userGeolocation = inject('geolocation')
    return {
      userLocation,
      userGeolocation
    }
  }
}
</script>
 类似资料:

相关阅读

相关文章

相关问答