Vue 组合式API

yuhuo2022-08-02开发库框架库

Vue3.2 中的 setup 语法糖,保证你看的明明白白! (qq.com)open in new window

一文掌握 vue3.2 setup 语法糖 (qq.com)open in new window

setup

setup 选项

<script>
import { ref } from "vue";
export default {
    props: {
    	title: String
  	},
    // 把选项式APIh和绑定在this的数据对象通过参数提供
    setup(props, { emit, expose, attrs, slots }) {
        // 定义响应式数据
        const count = ref(0);
        const list = reactive([1, 2, 3]);
        
        // 定义方法
        function increment() {
            count.value++;
            list[0]++;
        }
        
        // 访问props
        console.log(props.title);
        
        // 不向外部组件暴露
        expose();
        // 有选择地暴露
    	expose({ count: count });
        
        // 返回值会暴露给模板和其他的选项式 API 钩子
        return {
            count,
            list,
            increment
        };
    },
    mounted() {
        console.log(this.count); // 0
    },
};
</script>
<template>
    <button @click="count++">{{ count }}</button>
</template>

<script setup>

推荐使用 <script setup> 语法,比 setup 选项更加简洁易用。下文均以 <script setup> 语法作为示例。

<script setup>
// 引入对象
import { capitalize } from './helpers'
// 引入组件
import Foo from './Foo.vue'
import Bar from './Bar.vue'
// 变量
const msg = 'Hello!'

// 函数
function log() {
  	console.log(msg)
}
</script>

<template>
  	<div>{{ capitalize('hello') }}</div>
	<button @click="log">{{ msg }}</button>
	<!-- 静态组件 -->
	<Foo />
	<!-- 动态组件 -->
	<component :is="Bar" />
</template>

响应式

其他高级API没有罗列出来,参考:响应性 API:进阶 | Vue.js (vuejs.org)open in new window

ref()

定义响应式基本数据,对应 data 选项。

<template>
	<!-- 模板中正常使用,不用加.value -->
  	<button @click="increment()">{{ num }}</button>
</template>
<script setup>
import { ref } from 'vue'

const num = ref(0);
const str = ref("abc");
const boo = ref(true);
    
function increment() {
    // 使用时需要加.value
    num.value++;
    str.value += num.value;
    boo.value = !boo.value;
}
</script>

获取DOM元素或子组件的引用,对应 this.$refs。

<template>
  <p ref="p">hello</p>
</template>
<script setup>
import { ref } from 'vue'

const p = ref();
</script>

isRef()

检查某个值是否为 ref。

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

const num = ref(1);
const count = 2;

console.log(isRef(num)); // true
console.log(isRef(count)); // false
</script>

unref()

如果参数是 ref,则返回内部值,否则返回参数本身。

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

const num = ref(1);
const count = 2;

console.log(unref(num)); // 1
console.log(unref(count)); // 2
</script>

toRef()

可用于为响应式对象上的 property 创建 ref。这样创建的 ref 与其源 property 保持同步。

<script setup>
import { reactive, toRef } from 'vue'

const state = reactive({
  foo: 1,
  bar: 2
});

const fooRef = toRef(state, 'foo');

// 更改该 ref 会更新源属性
fooRef.value++;
console.log(state.foo); // 2

// 更改源属性也会更新该 ref
state.foo++;
console.log(fooRef.value); // 3
</script>

toRefs()

将一个响应式对象转换为一个普通对象,这个普通对象的每个 property 都是指向源对象相应 property 的 ref。

<script setup>
import { reactive, toRefs } from 'vue'

const state = reactive({
  foo: 1,
  bar: 2
});
// 直接将生成的普通对象解构,每个属性都是ref
const {foo, bar} = toRefs(state);

// 更改该 ref 会更新源属性
foo.value++;
console.log(state.foo); // 2

// 更改源属性也会更新该 ref
state.foo++;
console.log(foo.value); // 3
</script>

reactive()

定义响应式对象,对应 data 选项。

<template>
  	<button @click="increment()">{{ list[0] }}</button>
</template>
<script setup>
import { ref, reactive } from 'vue'

const list = reactive([1, 2, 3]);
const obj = reactive({ a: 1 });
    
function increment() {
    list[0]++;
    obj.a++;
}
</script>

ref 在 reactive 中的解包

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

// 普通对象,ref自动解包,即不需要.value
const obj = reactive({ a: ref(1) });
obj.b = ref(2);
console.log(obj.a); // 1
console.log(obj.b); // 2

// 数组和Map,ref不会解包,需要.value
const books = reactive([ref('Vue 3 Guide')])
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
console.log(map.get('count').value)
</script>

isReactive()

检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。

<script setup>
import { ref, reactive, isReactive } from 'vue'

const a = ref(0);
const b = reactive({ a: 1 });
    
console.log(isReactive(a)); // false
console.log(isReactive(b)); // true
</script>

readonly()

接受一个对象 (不论是响应式还是一般的) 或是一个 ref,返回一个原值的只读代理。

<script setup>
import { reactive, readonly } from 'vue'
    
const original = reactive({ count: 0 })
const copy = readonly(original)

// 源对象的属性值可改
original.count++;

// 只读代理的属性不可更改,更改会失败并会得到一个警告
copy.count++; // warning!
</script>

isReadonly()

检查一个对象是否是由 readonly() 或 shallowReadonly() 创建的代理。

<script setup>
import { ref, readonly, isReadonly } from 'vue'

const a = ref(0);
const b = readonly(a);
    
console.log(isReadonly(a)); // false
console.log(isReadonly(b)); // true
</script>

isProxy()

检查一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。

<script setup>
import { ref, reactive, readonly, isProxy } from 'vue'

const a = ref(0);
const b = reactive({ a: 1 });
const c = readonly(a);
    
console.log(isProxy(a)); // false
console.log(isProxy(b)); // true
console.log(isProxy(c)); // true
</script>

功能方法

defineProps()

对应 props 选项。

<template>
	<!-- 使用props数据 -->
  	<h1>{{ msg }}</h1>
</template>
<script setup>
import { defineProps } from 'vue'

// 无类型推断
const props = defineProps(["msg"]);
    
// vue类型推断
const props = defineProps({
    msg: String,
});
    
// TypeScript类型推断
const props = defineProps<{
    msg: string;
}>();

// 使用props数据
console.log(props.msg);
</script>

defineEmits()

对应 emits 选项。

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

// 无类型推断
const emit = defineEmits(['change', 'update'])

// TypeScript类型推断
const emit = defineEmits<{
    (e: "change", id: number): void;
    (e: "update", value: string): void;
}>();

function handle() {
    emit('change', 1001);
}
</script>

defineExpose()

对应 expose 选项。

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

const a = 1
const b = ref(2)

// 指定暴露的属性方法,默认全部不暴露
defineExpose({ a, b });
</script>

nextTick()

对应 this.$nextTick() 。

<script setup>
import { reactive, nextTick } from 'vue'
    
const state = reactive({ count: 0 });

function increment() {
  state.count++
  nextTick(() => {
    // 访问更新后的 DOM
  });
}
</script>

computed()

计算属性,对应 computed 选项。

<script setup>
import { ref, nextTick } from 'vue'
    
const count = ref(1);
// 只读计算属性
const countDouble = computed(() => count.value + 1, {
  	// 调试事件,只在开发模式下工作
  	onTrack(e) {
    	// 当 count.value 被追踪为依赖时触发
  	},
  	onTrigger(e) {
    	// 当 count.value 被更改时触发
  	}
});
// 可写计算属性
const countPlus = computed({
	get: () => count.value + 1,
	set: (val) => {
    	count.value = val - 1
  	}
})
</script>

watch()

侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。

<script setup>
import { ref, reactive, watch } from 'vue'
    
const foo = ref(0);
const bar = reactive({ count: 0 });
    
// 监听单个数据源
watch(foo, (newFoo, oldFoo, onCleanup) => {
  /* ... */
},// 可选选项
{
    // 是否创建时立即触发回调,默认false
    immediate: false,
    // 数据源是对象是,是否深层级监听变更,默认true
    deep: true,
  	// 刷新时机:pre 组件更新之前(默认),post 组件更新之后,sync 异步
  	flush: 'pre', 
    // 当被追踪为依赖时触发
  	onTrack(e) {},
    // 当依赖更改时触发
  	onTrigger(e) {},
});
    
// 监听多个数据源
watch([foo, bar], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})
</script>

watchEffect()

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

<script setup>
import { ref, watchEffect } from 'vue'
    
const count = ref(1);
// 创建一个侦听器
const unwatch = watchEffect((onCleanup) => { 
    console.log(count.value);
    
    // 清理回调会在该副作用下一次执行前被调用
    onCleanup(()=> {
        console.log("清理回调");
    });
},
// 可选选项
{
  	// 刷新时机:pre 组件更新之前(默认),post 组件更新之后,sync
  	flush: 'pre', 
    // 当被追踪为依赖时触发
  	onTrack(e) {},
    // 当依赖更改时触发
  	onTrigger(e) {}
});
    
// 停止侦听器
unwatch()
</script>

watchPostEffect()

watchEffect() 使用 flush: 'post' 选项时的别名。

watchSyncEffect()

watchEffect() 使用 flush: 'post' 选项时的别名。

provide()

供给一个值,可以被后代组件注入。

<script setup>
import { ref, provide } from 'vue'
import { fooSymbol } from './injectionSymbols'

// 供给静态值
provide('foo', 'bar')

// 供给响应式的值
const count = ref(0)
provide('count', count)

// 供给时将 Symbol 作为 key
provide(fooSymbol, count)
</script>

inject()

<script setup>
import { inject } from 'vue'
import { fooSymbol } from './injectionSymbols'

// 注入值的默认方式
const foo = inject('foo')

// 注入响应式的值
const count = inject('count')

// 通过 Symbol 类型的 key 注入
const foo2 = inject(fooSymbol)

// 注入一个值,若为空则使用提供的默认值
const bar = inject('foo', 'default value')

// 注入一个值,若为空则使用提供的工厂函数
const baz = inject('foo', () => new Map())

// 注入时为了表明提供的默认值是个函数,需要传入第三个参数
const fn = inject('function', () => {}, false)
</script>

useSlots()

对应 this.$slots 。

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

const slots = useSlots();
</script>

useAttrs()

对应 this.$attrs 。

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

const attrs = useAttrs();
</script>

useRoute()

<script setup>
import { useRoute } from 'vue-router'

const route = useRoute();
</script>

useRouter()

<script setup>
import { useRouter } from 'vue-router'

const router = useRouter();
</script>

useStore()

<script setup>
import { useStore } from 'vuex'

const store = useStore();
</script>

生命周期钩子

onBeforeMount()

注册一个钩子,在组件被挂载之前被调用。

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

onBeforeMount(() => {
  	// 组件完成了其响应式状态的设置,但还没有创建 DOM 节点
});
</script>

onMounted()

注册一个回调函数,在组件挂载完成后执行。

<template>
  <div ref="el"></div>
</template>
<script setup>
import { ref, onMounted } from 'vue'

const el = ref()

onMounted(() => {
  	el.value // <div>
})
</script>

onBeforeUpdate()

注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。

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

onBeforeUpdate(() => {
  	// 在 Vue 更新 DOM 之前访问 DOM 状态
});
</script>

onUpdated()

注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。

<template>
  <button id="count" @click="count++">{{ count }}</button>
</template>
<script setup>
import { ref, onUpdated } from 'vue'

const count = ref(0)

onUpdated(() => {
  // 文本内容应该与当前的 `count.value` 一致
  console.log(document.getElementById('count').textContent)
})
</script>

onBeforeUnmount()

注册一个钩子,在组件实例被卸载之前调用。

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

onBeforeUnmount(() => {
  	// 组件实例依然还保有全部的功能
});
</script>

onUnmounted()

注册一个回调函数,在组件实例被卸载之后调用。

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

let timer = setInterval(() => {
    // ...
}, 5000);

onUnmounted(() => {
    // 手动清理一些副作用
    clearInterval(intervalId)
});
</script>

onErrorCaptured()

注册一个钩子,在捕获了后代组件传递的错误时调用。

<script setup>
import { onErrorCaptured } from 'vue'
// err 错误对象
// instance 触发该错误的组件实例
// info 错误来源类型的信息字符串
onErrorCaptured((err, instance, info) => {
});
</script>

onActivated()

注册一个回调函数,若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用。

onDeactivated()

注册一个回调函数,若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用。

onRenderTracked()

注册一个调试钩子,当响应式依赖被组件的渲染作用追踪后调用。Dev only

onRenderTriggered()

注册一个调试钩子,当响应式依赖触发了组件渲染作用的运行之后调用。Dev only

onServerPrefetch()

注册一个异步函数,在组件实例在服务器上被渲染之前调用。 SSR only

Last Updated 2024/3/14 09:51:53