300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > vue3.0 - Composition API

vue3.0 - Composition API

时间:2021-11-30 09:58:24

相关推荐

vue3.0 - Composition API

一. 介绍

》使用传统的option配置方法写组件的时候问题,随着业务复杂度越来越高,代码量会不断的加大;由于相关业务的代码需要遵循option的配置写到特定的区域,导致后续维护非常的复杂,同时代码可复用性不高,而composition-api就是为了解决这个问题而生的。

》Composition API字面意思是组合APl,它是为了实现基于函数的逻辑复用机制而产生的。主要思想是,我们将它们定义为从新的 setup函数返回的JavaScript变量,而不是将组件的功能(例如state、method、computed等)定义为对象属性。

体验

1. 首先创建一个vue脚手架, 我们在About组件中进行操作

在export default配置项中,我们不用写传统的vue配置项格式了,而使用setup函数来代替,这里我们创建一个对象,需要写在reactive方法里,reactive方法需要从vue中引入,然后在末尾用return返回

import {reactive} from 'vue'export default {setup(){const data = reactive({count:0})return {data} //返回出去了才能访问到}}

在页面中访问:

<h3>count:{{data.count}}</h3>

2. 如果我们使用一个简单的计算属性(这里是一个方法),可以将计算属性返回的结果直接保存至变量中,注意计算属性也是需要引入的

import {reactive, computed} from 'vue'export default {setup(){const data = reactive({count:0,double:computed(()=>data.count*2)})return {data}}}

<h3>count:{{data.count}}</h3><h3>double:{{data.double}}</h3>

3. 定义方法,方法也是定义在setup里,通过return返回,因为setup本身就是一个方法,这里就不能简写add方法了,完整代码↓

<h3>count:{{data.count}}</h3><h3>double:{{data.double}}</h3><h3><button @click="add">增加</button></h3>

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}}

二. 使用详解

1. setup方法应用

》setup()函数是vue3中专门新增的方法,可以理解为Composition Api的入口.

》执行时机在beforecreate之后,create之前执行.

setup函数在创建组件之前被调用,所以在setup被执行时,组件实例并没有被创建。因此在setup函数中,我们将没有办法获取到this

export default {name:'SubComp',props:{one:{type:String},two:{type:String}}setup(){// 在创建组件之前调用的,没有thisconsole.log('setup');console.log(this); //undifined},beforeCreate() {console.log('beforeCreate--');},created() {console.log('Created--');console.log(this); //Proxy{…}},}

但是setup函数提供了两个参数:props和context,用来访问其他组件传过来的数据

props:在父组件向子组件传值时,子组件用props属性接收到的数据,可以通过setup中的props参数访问

export default {name:'SubComp',props:{one:{type:String},two:{type:String}},setup(props){console.log(props.one+props.two);}}

context:上下文对象,大致包含context.attrs、context.slot、context.parent、context.root、context.emit、context.refs这些属性

export default {name:'SubComp',props:{one:{type:String},two:{type:String}},setup(props,context){console.log(props.one+props.two);//当子组件没有用props接收的时候,可以用attrs访问父组件中的数据console.log(context.attrs.desc);// 获取父组件插槽的内容console.log(context.slots.default());//向父类传数据context.emit('myadd','向父组件传数据')}}

我们也可以把context解构出来方便使用

setup(props,{attrs,slots,emit}){// 在创建组件之前调用的,没有thisconsole.log('setup');console.log(this);console.log(props.one+props.two);//当子组件没有用props接收的时候,可以用attrs访问父组件中的数据console.log(attrs.desc);// 获取父组件插槽的内容console.log(slots.default());emit('myadd','向父组件传数据')},

2. Composition常用的API工具

ref()函数

在setup中声明一个变量num,以及一个函数,在函数中更改num的值,return出去的num的值并没有变化,原因就在于:原生变量的数据是没有响应式的

export default {name:'ComApi',setup(){let num=1; let myfun=()=>{num++;console.log(num); //2}return{num, //1myfun}}}

如果我们要声明一个数据是响应式的变量,需要用到ref()函数 ↓

ref()函数用来给定的值创建一个响应式的数据对象,ref()的返回值是一个对象,这个对象上只包含一个.value属性.

ref的变量:{{num2}}{{myfun2(55)}}

import {ref} from 'vue'export default {name:'ComApi',setup(){//let num=1; //let myfun=()=>{// num++;// console.log(num); //2//}let num2=ref(22);let myfun2=(newvalue)=>{console.log(num2.value); //55num2.value=newvalue;}return{//num, //1//myfun,num2, //55myfun2}}}

注:在setup程序中用的时候需要.value,在模板中直接用变量名即可

reactive()函数:创建响应式对象

如果我们要声明一个对象,就不能用refs了,需要用到reactive函数将对象变成响应式

reactive中的对象:{{user.name}}---{{user.age}}

import {reactive, ref} from 'vue'export default {name:'ComApi',setup(){//声明对象:reactive将对象变成响应式let user=reactive({name:'aaa',age:18,sex:'男'})return{user}}}

此时插值里用user.name调用比较麻烦,那能不能直接用name调用呢,可以考虑把对象展开,一个一个返回,可以使用es6中的三点运算符

toRefs()函数:解构响应式对象

对象展开之后,数据就不是响应式的了,这时可以使用toRefs函数转换成响应式

{{name}}---{{age}}name:<input type="text" v-model="name"><br>age:<input type="text" v-model="age">

import {reactive, ref, toRefs} from 'vue'export default {name:'ComApi',setup(){//声明对象:reactive将对象变成响应式let user=reactive({name:'aaa',age:18,sex:'男'})return{...toRefs(user)}}}

将ref响应式数据挂载到reactive中,当把ref()创建出来值直接挂载到reactive()中时,会自动把响应式数据对象的展开为原始的值,不需要通过.value就可以直接访问到

let num2=ref(2);let user=reactive({name:'aaa',age:18,sex:'男',num2})let myfun2=(newvalue)=>{user.num2=100; //响应式}

readonly()函数(不常用)

如果要将响应式的数据转换为原始数据,可以使用readonly函数

import { readonly } from 'vue' //记得引入let user2=readonly(user)

isRef()函数(不常用)

用来判断变量是原生的还是响应式的,也需要引入

let num3=isRef(num2)?num2.value=44:num2=55 //44

3. Composition的计算属性API

computed()用来创建计算属性,返回值是一个ref的实例。

1. 引入computed,在setup中定义一个响应式user对象,定义计算属性

import {reactive, toRefs, computed} from 'vue'export default {name:'ComputedDemo',setup() {const user = reactive({firstname:'aaa',lastname:'bbb'})let fullname=computed(()=>{return user.firstname+'.'+user.lastname})return{...toRefs(user),fullname}}}

2. 定义完之后,直接在插值里面访问对应的变量就可以了,这里firstname和lastname的值更改之后,fullname也会同步变化

<h2>计算属性</h2>firstname:<input type="text" v-model="firstname"><br>lastname:<input type="text" v-model="lastname"><br>{{fullname}}<br>

4. Composition的侦听器watch

》watch()函数用来监视某些数据项的变化,从而触发某些特定的操作。

》watchEffect立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。

变量侦听

定义(默认会初始化一下,每当值变化的时候就会监听一次):

import { ref } from "vue";import { watch, watchEffect } from 'vue';export default {name: "WatchDemo",setup() {let a = ref(1);let b = ref(2);watch(()=>{console.log(a.value+'------'+b.value);})watchEffect(()=>{console.log(a.value+'####'+b.value);})return {a,b,};},};

watch还有更多用法,比如我们可以监听某个值,当其值变化时进行回调,回调函数中可以传入新值和初始值两个参数;监听某个值时不会自动初始化,通过第三个参数immediate的值是true或false来确认是否以当前的初始值执行回调函数

import { ref } from "vue";import { watch, watchEffect } from 'vue';export default {name: "WatchDemo",setup() {let a = ref(1);let b = ref(2);//监听某个值,不会自动初始化,需要加参数watch([a,b],([newA,oldA],[newB,oldB])=>{console.log(newA+'---------'+oldA);console.log(newB+'#########'+oldB);},{immediate:true})return {a,b,};},};

对象的侦听

import { reactive,toRefs } from "@vue/reactivity";import { watch } from "@vue/runtime-core";export default {name: "WatchDemo",setup() {//监听对象const user = reactive({a:1,b:2})watch([()=>user.a,()=>user.b],([newA,newB],[oldA,oldB])=>{// console.log(user.a+'######'+user.b);console.log(newA+'---------'+oldA);console.log(newB+'#########'+oldB);},{immediate:true})return {...toRefs(user)}},};

5. Composition的生命周期API

在新版的生命周期函数,可以按需导入到组件中,且只能在setup()函数中使用。

import {onMounted} from 'vue'export default {name:'LifeHook',setup() {onMounted(() => {console.log('mounted!')})onUpdated(() => {console.log('updated!')})onUnmounted(() => {console.log('unmounted!')})}}

下面是vue 2.x 的生命周期函数与新版 Composition API 之间的映射关系

// beforeCreate -> use setup()// created -> use setup()beforeMount -> onBeforeMountmounted -> onMountedbeforeUpdate -> onBeforeUpdateupdated -> onUpdatedbeforeDestroy -> onBeforeUnmountdestroyed -> onUnmountederrorCaptured -> onErrorCaptured

6. Composition中provide和inject使用

》父子组件:通过props,$emit,【$root,$parent,$children】

》非父子组件:Vuex实现,父子层层传递、$ref

》Vue官网建议,在正常情况下,这两种方式已经能满足绝大多数甚至所有的业务需求,对于应用程序代码应优先使用它们处理。

》provide/inject这对选项允许一个祖先组件向其所有子孙后代组件注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。

》provide就相当于加强版父组件prop,可以跨越中间组件,inject就相当于加强版子组件的props

首先我们来看一下在data中使用的provide和inject:

//父组件RootApp中import TwoComp from '../components/TwoComp.vue'export default {components: { TwoComp },name:'RootApp',data() {return {title:'这是根组件提供的数据'}},provide(){return{title:this.title}}}//接下来我们跨过子组件,来到孙子组件ThreeComp中接收export default {name:'ThreeComp',inject:['title']}

<template><div><hr><h3>这是孙子组件</h3>{{title}} <!-- 在孙子组件中是可以通过插值的形式访问到的 --></div></template>

这种方式存在一个问题:子组件接收的数据不是响应式的

下面来看在setup中使用响应式数据进行provide和inject传值:

//父组件RootApp中响应式数据import TwoComp from '../components/TwoComp.vue'import {provide, ref, reactive, toRefs} from 'vue'export default {components: { TwoComp },name:'RootApp',setup() {let title=ref('这是根组件提供的数据') const user = reactive({name:'abc',age:18}) provide('title',title)provide('user',user)return{title,...toRefs(user)}}}//孙子组件ThreeCompimport { inject } from 'vue'export default {name:'ThreeComp',// inject:['title']setup() {let title=inject('title')let user=inject('user')return{title,user}}}

并且使用provide和inject传值是可逆的,当我们改变子组件接收的值,父组件也会同步变化

更多API的使用可以参考官方文档:/vue-composition-api

三. Composition API处理路由

》setup和Vue的Composition API的引入,开辟了新的可能性,但为了能够充分利用Vue Router的潜力,我们将需要使用一些新功能来替换对访问this和组件内导航保护的使用。

》由于我们无权访问setup的内部this,因此无法直接访问this.$router或this.$route了,相反,我们使用useRouter和useRoute函数。

》请注意,我们仍然可以访问$router和$route在模板中,因此无需在router或之route内返回setup。

示例:选择左侧菜单,将菜单项的索引传给右侧界面显示出来

1. 创建一个RouterApi页面作为主页,同时创建另一个页面MyPage显示右侧的页面,配置路由,将MyPage嵌套在主页里(路由配置参考:Router路由详解_m0_50744582的博客-CSDN博客)

{path: '/routerapi',name: 'RouterApi',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is ponent: () => import(/* webpackChunkName: "about" */ '../views/RouterApi.vue'),children:[{path:'page',component: () => import('../views/MyPage.vue'),},{path:'page/:id',component: () => import('../views/MyPage.vue'),}]},

然后在主页的的data中定义一些数据并遍历显示出来,作为菜单项,并向访问的页面传参

<template><div><div id="menu"><h5 v-for="item in pages" :key="item"><router-link :to="'/routerapi/page/'+item.id">{{item.title}}</router-link></h5></div><div id="content"><router-view></router-view></div></div></template><script>export default {name:'RouterApi',data() {return {pages:[{id:1,title:'1111111111'},{id:2,title:'2222222222'},{id:3,title:'3333333333'},{id:4,title:'4444444444'},{id:5,title:'5555555555'}]}},}</script><style scoped lang="scss">#menu{width: 30%;background: #f0f0f0;height: 300px;float: left;}#content{width: 70%;background: #ccc;height: 300px;float: right;}a {font-weight: bold;color: #2c3e50;&.router-link-exact-active {color: #42b983;}}</style>

2.在Mypage页中就可以直接使用$route访问传过来的参数

<template><div><h1>MyPage</h1><h3>{{$route.params.id}}</h3></div></template>

运行界面如下:

但是一般插值中我们只写一些简单的数据,如果要将传递过来的参数进一步处理呢?在原来的vue中我们可以定义计算属性,通过this.$route来访问参数

在组合API的setup函数中通过route.params.id可以访问,但是存在一个问题,setup函数只执行一次,接收到的参数不会变化,这时我们想到了监听器

我们可以创建一个监听器来监听当前路由的参数,使用useRoute函数对其进行操作:

<template><div><h1>MyPage</h1><h3>{{$route.params.id}}</h3><h3>id:{{id}}</h3></div></template><script>//需要引入路由中的useRouteimport {useRoute} from 'vue-router' import { ref, watch } from 'vue'export default {name:'MyPage',setup() {const route=useRoute();let id=ref()//监听当前路由的参数watch(()=>route.params,(newid)=>{console.log(newid.id);id.value=newid.id//可以进一步处理})return{id}}}</script>

接下来我们看一下useRouter函数的使用:

1. 在主页写两个跳转链接

<h5><router-link :to="{path:'/routerapi/article',query:{name:'abc',keyword:'hello'}}">文章一</router-link></h5><h5><button @click="$router.push({path:'/routerapi/article',query:{name:'xyz',keyword:'hi'}})">文章二</button></h5>

2. 新建一个MyArticle页面,用userRouter进行页面跳转

<template><div><h1>MyArticle</h1><h3>{{$route.query.name}}</h3><h3>{{$route.query.keyword}}</h3></div></template><script>import {useRouter} from 'vue-router'import { ref, watch } from 'vue'export default {name:'MyArticle',setup() {const router=useRouter();setTimeout(() => {router.push({path:'/routerapi/article',query:{name:'11111',keyword:'2222222'}})}, 5000);}}</script>

》导航守卫:

- 尽管您仍可以将组件内导航保护与setup功能结合使用,但Vue Router会将更新和离开提供CompositionAPI函数:

- onBeforeRouteLeave((to, from) => {})

- onBeforeRouteUpdate(async (to, from) =>{})

onBeforeRouteLeave

onBeforeRouteLeave((to,from)=>{let answer=window.confirm(`你确定要从${from.fullPath}到${to.fullPath}吗`)if(!answer) return false})

更多可以参考官网:Vue Router and the Composition API | Vue Router

四. Composition API结合vuex使用

因为无法访问setup的内部this,所以我们使用useStore函数。

vuex参考:Vuex五大核心概念_m0_50744582的博客-CSDN博客_vuex的五个属性

定义两个num,对num分别进行状态管理,先来温习一下原代码:

//index.jsimport { createStore } from 'vuex'export default createStore({state: {num1:11,num2:22},getters:{double1(state){return state.num1*2}},mutations: {changenum1(state,payload){state.num1=payload;}},actions: {timecnum1({commit,state}){setTimeout(()=>{commit('changenum1',44)},3000)}},modules: {}})

<!-- VuexApi.vue --><template><div><h2>Vuex API useStore</h2><h3>num1:{{$store.state.num1}}</h3><h3>getters-num1:{{$store.getters.double1}}</h3><button @click="cnum1(33)">修改num1:{{$store.state.num1}}</button><button @click="canum1">修改anum1:{{$store.state.num1}}</button></div></template><script>export default {methods: {cnum1(newnum){this.$mit('changenum1',newnum)},canum1(){this.$store.dispatch('timecnum1')}},}</script>

当使用组合API结合vuex时,所有内容都写在setup函数中,以下是完整代码:

// index.jsimport { createStore } from 'vuex'export default createStore({state: {num1:11,num2:22},getters:{double1(state){return state.num1*2},double2(state){return state.num2*2}},mutations: {changenum1(state,payload){state.num1=payload;},changenum2(state,payload){state.num2=payload;}},actions: {timecnum1({commit,state}){setTimeout(()=>{commit('changenum1',44)},3000)},timecnum2({commit,state}){setTimeout(()=>{commit('changenum2',55)},3000)}},modules: {}})

<!-- VuexApi.vue --><template><div><h2>Vuex API useStore</h2><h3>num1:{{$store.state.num1}}</h3><h3>getters-num1:{{$store.getters.double1}}</h3><button @click="cnum1(33)">修改num1:{{$store.state.num1}}</button><button @click="canum1">修改anum1:{{$store.state.num1}}</button><hr><h3>num2:{{num2}}</h3><h3>getters-num2:{{double2}}</h3><button @click="cnum2(33)">修改num2:{{num2}}</button><button @click="canum2">修改anum2:{{num2}}</button></div></template><script>import { useStore } from 'vuex'import { computed } from 'vue'export default {methods: {cnum1(newnum){this.$mit('changenum1',newnum)},canum1(){this.$store.dispatch('timecnum1')}},setup() {const store= useStore();return {num2:computed(()=>store.state.num2),double2:computed(()=>store.getters.double2),cnum2:(newnum)=>{mit('changenum2',newnum)},canum2:()=>{store.dispatch('timecnum2')}}}}</script>

更多可以去官网学习:Composition API | Vuex

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。