Vue组合式API,为了解决业务逻辑复杂度变高而生的神器。
Composition API(组合式API)
使用传统option配置方法写组件的时候,随着业务复杂度越来越高,代码量会不断加大,由于相关业务的代码需要遵循option的配置写到特定的区域,导致后续维护非常复杂,同时代码可复用性不高,而composition-api就是为了解决这个问题而生的。
Composition
API是为了实现基于函数的逻辑复用机制而产生的,主要思想是,我们将它们定义为从新的setup函数返回的JavaScript变量,而不是将组件的功能(如state、method、computed等)定义为对象属性。
基本示例
原生Vue的实现形式
1 2 3 4 5 6 7
| <template> <div class="home"> <h3>Count:{{ count }}</h3> <h3>Double count:{{ double }}</h3> <button @click="add">+</button> </div> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <script> export default { name: 'Home', data() { return { count: 0, } }, computed: { double() { return this.count * 2; } }, methods: { add() { this.count++; } } } </script>
|
使用Composition API的实现形式
1 2 3 4 5 6 7 8
| <template> <div class="about"> <h3>Count:{{ data.count }}</h3> <h3>Double count:{{ data.double }}</h3> <button @click="add">+</button>
</div> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script> import {reactive, computed} from 'vue'
export default { setup() { const data = reactive({ count: 0, double: computed(() => data.count * 2), });
function add() { data.count++; }
return {data, add}; } } </script>
|
setup()
:Composition
API的入口函数
setup()
在beforeCreate()
之前执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| export default { name: "SubComp", data() { return { msg: 'Test message.' } }, props: { one: { type: String }, two: { type: String } }, setup(props,context) { console.log('setup is called.'); console.log(this); console.log(props.one + props.two); }, }
|
context对象包含以下属性
1 2 3 4 5 6 7 8 9 10
| const MyComponent = { setup(props,context){ context.attrs context.slots context.parent context.root context.emit context.refs } }
|
常用API
ref()
函数用来为给定的值创建一个响应式的数据对象,ref()
的返回值是一个对象,这个对象只包含一个.value
属性
1 2 3 4 5 6 7 8 9 10 11 12 13
| export default { name: "CommonAPI", setup() { let num2 = ref(22); let myfun2 = (newvalue) => { num2.value = newvalue; } return { num2, myfun2 } } }
|
reactive()
声明对象类型的响应式数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export default { name: "CommonAPI", setup() { let user = reactive({ name: 'zhangsan', age: 100, sex: '男' });
return { user } } }
|
toRefs()
将对象的数据成员拆解成单独的变量,并变成响应式的
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export default { name: "CommonAPI", setup() { let user = reactive({ name: 'zhangsan', age: 100, sex: '男' });
return { ...toRefs(user), } } }
|
readonly()
将响应式数据变为原生数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export default { name: "CommonAPI", setup() { let user = reactive({ name: 'zhangsan', age: 100, sex: '男' });
let user2 = readonly(user);
return { user2 } } }
|
isRef()
判断该变量是否为响应式变量
1 2 3 4 5 6 7 8 9 10 11 12
| export default { name: "CommonAPI", setup() { let num2 = 1; let num3 = isRef(num2) ? num2.value : num2 ;
return { num2, num3 } } }
|
计算属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export default { name: "Computed", setup() { const user = reactive({ firstname: 'san', lastname: 'zhang' });
let fullname = computed(() => { return user.firstname + '·' + user.lastname; });
return { ...toRefs(user), fullname } } }
|
侦听器watch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| export default { name: "Watch", setup() { let a = ref(1); let b = ref(2); watch(() => { console.log(a.value + '---' + b.value); });
watch(a, () => { console.log('a changed'); });
watch(a, () => { console.log('a changed'); }, {immediate: true});
watch(a, (newA, oldA) => { console.log('A changed from ' + oldA + ' to ' + newA); }, {immediate: true});
watch([a, b], ([newA, newB], [oldA, oldB]) => { console.log('A changed from ' + oldA + ' to ' + newA); console.log('B changed from ' + oldB + ' to ' + newB); }, {immediate: true});
watchEffect(() => { console.log(a.value + '###' + b.value); });
return { a, b } } }
|
若要监听对象类型的变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| export default { name: "Watch", setup() { const user = reactive({ a: 1, b: 2 });
watch(user, () => { console.log('User changed'); });
watch(() => user.a, (newx, oldx) => { console.log('User.a changed from ' + newx + ' to ' + oldx); });
watch([() => user.a, () => user.b], ([newA, newB], [oldA, oldB]) => { console.log('User.a changed from ' + newA + ' to ' + oldA); console.log('User.b changed from ' + newB + ' to ' + oldB); });
watchEffect(() => { console.log(user.a) });
return { ...toRefs(user), } } }
|
Composition
API中的生命周期函数
原生生命周期函数与Composition API之间的映射关系
beforeCreate
→setup()
created
→setup()
beforeMount
→onBeforeMount()
mounted
→onMounted()
beforeUpdate
→onBeforeUpdate()
updated
→onUpdated()
beforeUnmount
→onBeforeUnmount()
Unmounted
→onUnmounted()
使用时要注意,是以回调方法的形式调用的
1 2 3 4 5 6 7 8
| export default { name: "LifeHook", setup() { onBeforeMount(() => { console.log('onBeforeMount'); }); }, }
|
provide
和inject
的使用
provide
和inject
这对选项允许祖先组件向其所有子孙后代组件注入一个依赖,不论组件层次有多深,在其上下游关系成立的时间内始终生效
provide
提供变量,相当于加强版父组件prop,可以跨越中间件
inject
注入变量,相当于加强版子组件props
原生Vue方式
祖先组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export default { name: "RootApp", components: {SecondApp}, data() { return { title: "The information is provided by root component." } }, provide() { return { title: this.title, } }, }
|
子孙组件
1 2 3 4
| export default { name: "ThirdApp", inject: ['title'], }
|
这种方式传递的变量不是响应式的
Composition API方式
祖先组件
1 2 3 4 5 6 7 8 9 10 11 12 13
| export default { name: "RootApp", components: {SecondApp}, setup() { let info = ref('The information is provided by root component through setup.');
provide('info', info);
return { info, } } }
|
子孙组件
1 2 3 4 5 6 7 8 9 10
| export default { name: "ThirdApp", setup() { let info = inject('info');
return { info, } } }
|
这种方式传递的变量是响应式的,而且是双向的响应式,即在祖先组件中更改变量,子孙组件中会发生变化;在子孙组件中更改变量,祖先组件中也会发生变化
Composition API处理路由
因为无权访问setup()
中的this
,故使用useRouter()
和useRoute()
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| export default { name: "Page", setup() { const route = useRoute(); const router = useRouter();
let id = ref();
watch(() => route.params, (newId) => { id.value = newId.id; }, {immediate: true})
return { id, } } }
|
组件级的导航守卫则替换为:
onBeforeRouteLeave((to,from)=>{})
onBeforeRouteUpdate(async(to,from)=>{})
1 2 3 4 5 6 7 8
| export default { name: "Page", setup() { onBeforeRouteLeave((to, from) => { return window.confirm(`确定要从${from.fullPath}到${to.fullPath}`); }); } }
|
Composition API结合Vuex
使用useStore()
函数
1 2 3 4 5 6 7 8 9 10 11 12 13
| export default { name: "Vuex", setup() { const store = useStore();
return { num2: computed(() => store.state.num2), double2: computed(() => store.getters.double2), cnum2: (newnum) => store.commit('changenum2', newnum), cnum22: () => store.dispatch('timecnum2'), } } }
|