美文网首页
Vue Composition API学习

Vue Composition API学习

作者: 仔崽06 | 来源:发表于2020-04-24 15:22 被阅读0次

作者提供api的动机是逻辑重用与代码组织,由于vue当前的api所带来编程限制,基本分为两种:
1.随着功能的增长,复杂组件中的代码越来越难以推理,这种情况发生在开发人员阅读自己编写的代码时,根本原因是因为vue现有的api通过强制执行代码组织,在某些情况下,通过逻辑考虑来组织代码更有意义.
2.缺乏用于在多个组件之间提取和重用逻辑

  • 首先利用cli4创建一个demo下载composition-api在main.js中引入使用.
//下载
npm install --save @vue/composition-api
//main.js
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
//引入官方提供的vue-composition-api库
import VueCompositionApi from '@vue/composition-api';
Vue.use(VueCompositionApi)
Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
  • setup函数

vue3中专门为组件提供的新属性.它为我们使用vue3的Composition API 新特新提供了统一的入口.之前定义的data,methods等都统一放入setup中实现。

  • 执行时机

setup函数会在beforeCreate之后、created之前

export default {
   setup(){
     console.log('setup执行')
   },
   beforeCreate(){
     console.log('beforeCreate执行')
   },
   created(){
     console.log('created执行')
   }
}
//输出顺序
//beforeCreate执行
//setup执行
//created执行
  • 接收props

接收外界传入的props,接受的组件必须props里需要声明否则拿不到传值.

//home.vue里
<setup :data="123"></setup>
//setup.vue里
export default {
  setup(props){
     console.log(props.data)
  },
  props:{
     data:Number
  }
}

  • 第二个形参context

指的是上下文对象,在这个对象中包含了一些书信,这些属性在2.x中通过this才能访问到,3.0访问方式如下,3.0中无法访问到this

export default {
   name:'Setup',
   setup(props,context){
       console.log(context)
   },
   props:{
      data:Number
   }
}
//输出结果
{
 root: (...),
 parent: (...),
 refs: (...),
 attrs: (...),
 listeners: (...),
 isServer: (...),
 ssrContext: (...),
 emit: (...),
 slots: {},
}
  • reactive

reactive()函数接受一个普通对象,返回一个响应式的数据对象.类似于2.x的组件里的data()函数,想要使用必须先引入reactive,模板中想要使用该数据必须return出去,此函数只能在setup函数里使用.

<template>
  <div>
       <span style="margin-right:10px">{{state.count}}</span>
       <el-button type="primary" @click="changeCount">点击累加</el-button>
  </div>
 
</template>

<script>
import { reactive } from '@vue/composition-api';
export default {
   name:'Setup',
   setup(){
    //state响应式的数据对象
     const state= reactive({
           count:1
     })
     function changeCount() {
         state.count++;
     }
     return{
        state,
        changeCount
     }
   }
}
//也可以return state 如这样返回在模板直接{{count}}就可以取到值

结果


fehelper-localhost-8081-1587627070334.png
  • ref

ref()函数用来根据给定制创建一个响应式的数据对象,ref()函数调用的返回值是一个对象,这个队形上只包含一个.value的属性.与reactvie类似,但是比reactive合适.

 //注意在setup内部访问值需要.value,在模板直接拿值可以使用
<template>
    <div>{{count}}---{{name}}</div>
</template>
<script>
import {ref} from '@vue/composition-api';
export default {
    name:'Ref',
    setup(){
        const count=ref(0);
        //ref会返回一个对象,并且只有value属性
        console.log(count.value)//0
        console.log(name.value) //undefined
        return {
            count,
            name:ref('zdb')
        }
    }
}
</script>
效果 fehelper-localhost-8081-1587630856799.png

两者区别 reactive创建一个集合响应式数据,ref创建单一响应式数据.

  • 在reactive对象中访问ref创建的响应式数据

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

<template>
    <div>{{state.count}}</div>
</template>
<script>
import { ref ,reactive } from '@vue/composition-api';
export default {
    name:'Ref',
    setup(){
        const count=ref(0);
        //ref会返回一个对象,并且只有value属性
        const state=reactive({
            count
        })
        state.count++;
        return {
           state
        }
    }
}
</script>
  • 新的ref会覆盖旧的ref
<script>
import { ref ,reactive } from '@vue/composition-api';
export default {
    name:'Ref',
    setup(){
      const c1=ref(0)
      const state=reactive({
          c1
      })
      const c2=ref(9)
      state.c1=c2;
      state.c1++;
      console.log(c1.value) //0
      console.log(c2.value) //10
      console.log(state.c1) //10
    }
}
</script>
  • isRef

isRef()用来判断某个值是否是ref()创建出来的,应用场景当展开某个可能为ref创建出来的值:例如:

import {isRef, ref ,reactive} from '@vue/composition-api';
export default {
   name:'isRef',
   setup(){
       const count=reactive({
           name:'zdb'
       })
       const name=ref('zdb')
       console.log(isRef(name)) //true
       const data=isRef(name) ? name.value :name;
       console.log(data) //zdb
       
   }
}
  • toRef

toRef()函数可以将reactive()创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每一个属性节点,都是ref()类型的响应式数据:最长见的引用场景.(reactive创建之后的数据如果返回使用扩展运算符(...),它创建的所有属性不再是响应式的.这是需要toRefs)

<template>
   <div>
       <p>count值为{{count}}</p>
    <el-button type="primary" @click="changeCount">点击累加</el-button>
   </div>
</template>

<script>
import {reactive ,toRefs} from '@vue/composition-api';
export default {
   name:'isRef',
   setup(){
       const state=reactive({
           count:0,
           name:'zdb'
       })
       //定义自增
      const changeCount=()=>{
          state.count++;
      }
      return {
          ...state, //此时页面点击state里的属性不会有任何改变,不再是响应式
          ...toRefs(state),//要想变成响应式需要把state里的每个属性用toRefs转换成响应式
          changeCount
      }
   }
}
</script>
  • computed

computed()用来创建计算属性,computed()函数的返回值是一个ref的实例,使用computed之前需要按需导入
1.创建只读计算属性

<script>
import {ref , computed} from '@vue/composition-api';
export default {
   name:'Computed',
   setup(){
       const count = ref(1);
       const plusOne=computed(()=>{ return count.value++ })
       console.log(plusOne.value) //1
       plusOne.value++ //error
   }
}
</script>

2.创建可读可写的计算属性

<template>
   <div>
        <div>count:{{count}}</div>
        <div>computed:{{countComputed}}</div>
        <el-button type="primary" @click="changeCount">点击累加</el-button>
   </div>
</template>

<script>
import {ref , computed} from '@vue/composition-api';
export default {
   name:'Computed',
   setup(){
       const count = ref(1);
       const countComputed=computed({
           //读取函数
           get:()=> count.value+1,
           //赋值函数
           set:(val)=>count.value=val
       })
       const changeCount=()=>{
           count.value++;
       }
       return{
           count,
           countComputed,
           changeCount
       }
   }
}
</script>

效果


fehelper-localhost-8080-1587645005972.png
  • Watch

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

<template>
  <div>
     <span>count:{{count}}</span>
     <el-button type="primary" @click="changeCount">点击累加</el-button>
  </div>
</template>

<script>
import { watch ,ref } from '@vue/composition-api';
export default {
   name:'Watch',
   setup(){
     const count=ref(0)
     watch(()=>{console.log(count.value)}) //会根据按钮点击count的值每次改变输出新的值
     const changeCount=()=>{
       return count.value++;
     }
     return {
       count,
       changeCount
     }
   },
}
</script>

watch监听指定数据源
1.监听reactive类型的数据源

<template>
  <div>
     <span>count:{{count}}</span>
     <el-button type="primary" @click="changeCount">点击累加</el-button>
  </div>
</template>

<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
   name:'Watch',
   setup(){
     const state=reactive({count:0})
     const changeCount=()=>{
        state.count++;
     }
     //必须指定监听属性,不能监听整个state
     watch(
       ()=>state.count,
       (count,preCount)=>{
          console.log(`新的count${count}----旧的count${preCount}`)
       },
      {
         lazy:true //默认情况下逐渐初始化会执行一次watch,lazy设置为true初始化不会调用watch
      })
      return{
        // state,
        //或者
        ...toRefs(state),
        changeCount
      }
   },
   
}
</script>

效果


image.png

2.监听ref类型的数据源

<template>
  <div>
     <span>count:{{count}}</span>
     <el-button type="primary" @click="changeCount">点击累加</el-button>
  </div>
</template>

<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
   name:'Watch',
   setup(){
     const count=ref(0);
     const changeCount=()=>{
         count.value++;
     }
     watch(
       ()=>count.value,
       (count,preCount)=>{
          console.log(`新的count${count}----旧的count${preCount}`)
       })
      return{
        count,
        changeCount
      }
   },
   
}
</script>

监视多个数据源

//reactvie
<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
   name:'Watch',
   setup(){
     const state=reactive({
       count:0,
       name:'zdb'
     })
     watch(
       [()=>state.count,()=>state.name], //Object.values(toRefs(state))
       ([newCount,newName],[oldCount,oldName])=>{
          console.log(`新的count${newCount}`)
          console.log(`新的name${newName}`)
          console.log(`旧的count${oldCount}`)
          console.log(`旧的name${oldName}`)
       },
    //去掉此项页面会报错
       {
         lazy:true
       }
      )
       setTimeout(()=>{
         state.count+=15;
         state.name+='zs'
       },2000)
      return{
       state
      }
   },
   
}
</script>

//ref

<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
   name:'Watch',
   setup(){
     const count=ref(0);
     const name=ref('zdb');
     watch(
       [count,name],
       ([newCount,newName],[oldCount,oldName])=>{
          console.log(`新的count${newCount}`)
          console.log(`新的name${newName}`)
          console.log(`旧的count${oldCount}`)
          console.log(`旧的name${oldName}`)
       },
       {
         lazy:true
       }
      )
       setTimeout(()=>{
         count.value+=15;
         name.value+='zs'
       },2000)
      return{
        count,
        name
      }
   },
   
}
</script>

效果


image.png
  • 清除watch

在setup()函数内创建watch监听,会在当前组件被销毁才会停止,如果想明确的停止某个监听,可以掉用watch的返回值即可.

<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
   name:'Watch',
   setup(){
     const count =ref(0);
     const stop=watch(count,(newVal,oldVal)=>{
        console.log(newVal)
        console.log(oldVal)
     })
     setInterval(()=>{
       count.value++;
       // stop清除对应的监视
       count.value>10 ? stop(): count.value
     },1000)

     return{
       count
     }
   },
   
}
</script>
  • 在watch中清除无效的异步任务

有时候,当贝watch监视的值发生变化时,或watch本身被stop后,我们希望能够清除那些无效的异步任务(例如当组件调用一个请求,请求结果没有返回,这时用户已经跳转到别的页面,请求没有意义,这时候需要清除异步任务),此时watch回调中提供了一个cleanup registrator function 来执行清除工作,这个清除函数在如下的情况被调用:
1.watch被重复执行了
2.watch被强制stop了

<template>
  <div>
     <el-input v-model="value"></el-input>
  </div>
</template>
<script>
import { watch ,ref, reactive, toRefs } from '@vue/composition-api';
export default {
   name:'Watch',
   setup(){
     let value=ref('');
     //异步 1秒后输出用户的输入
     const asyncPrint=val=>{
        return setTimeout(()=>{
          console.log(val)
       },1000)
     }
     watch(value,(newVal,oldVal,onCleanup)=>{
       //执行异步任务,并得到关闭异步任务的timerId
        const timerId=asyncPrint(newVal);
        //如果watch舰艇被重复执行,则会先清除上次未完成的异步任务.
        onCleanup(()=>clearTimeout(timerId))
      },
      {
        lazy:true
      }
     )
     return {
       value
     }
   },
   
}
</script>
  • 新版的生命周期函数
2.x生命周期选项和Composition API之间的映射

beforeCreate ->使用 setup()
created ->使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
新钩子

除了2.x生命周期等效项之外,Composition API还提供了以下调试挂钩:

onRenderTracked
onRenderTriggered
两个钩子都收到DebuggerEvent类似于onTrack和onTrigger观察者的选项:

export default {
  onRenderTriggered(e) {
    debugger
    // inspect which dependency is causing the component to re-render
  }
}

示例

import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}
  • provide&inject

provide和inject可以时间嵌套组件之间数据传递.这两个函数只能在setup中使用.父组件中使用provide()函数向下传递数据,子组件使用inject()获取上层传递来的数据

//父组件
<template>
  <div class="home">
    父组件:
   <Provide></Provide>
  </div>
</template>

<script>
import Provide from '@/components/provide.vue'
import {provide,ref} from '@vue/composition-api'
export default {
  name: 'Home',
  components: {
    Provide
  },
  setup(){
    const msg=ref('我是父组件传递来的数据');
    //参数一:共享的名称 参数二:共享的数据
    provide('sendMsg',msg)
    return {
      msg
    }
  }
}
</script>
//子组件
<template>
   <div>子组件:{{msg}}</div>
</template>

<script>
import {inject,ref} from '@vue/composition-api'
export default {
   name:'Provide',
   setup(){
     const msg=inject('sendMsg')
     return {
         msg
     }
   }
}
</script>


  • ref

此功能与2.x中的ref类似

<template>
   <div>
      <h3 ref="el">template-ref</h3>
   </div>
</template>

<script>
import {ref,onMounted} from '@vue/composition-api';
export default {
  name:'tplRefs',
  setup(){
    //创建一个dom的引用
    const el=ref(null)
    onMounted(()=>{
       el.value.style.color='red'
    })
    return {
      el
    }
  }
}
</script>

相关文章

网友评论

      本文标题:Vue Composition API学习

      本文链接:https://www.haomeiwen.com/subject/qwiiihtx.html