美文网首页
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