19

前言

vue3.0 Rc(候选发布版本)已经于7月18上线,离正式版本发布已经不远了,鉴于此,本人就通过@vue/composition-api 这个 Vue Composition API 来事先体验下3.0的新特性,为以后能快速上手新版本做准备。

准备工作

下载与引入
  • 下载体验版api
 npm i @vue/composition-api
  • 引入与使用
 1. 在main.js中要引入全部的api
 
 import VueCompositionApi from '@vue/composition-api'

 Vue.use(VueCompositionApi)
 
 2. 在页面中按需引入api
 
 import { 需要使用的api } from '@vue/composition-api'
tips: main.js和局部页面都需要引入,不能只在页面中引入。

开发与使用

reactive和toRefs

reactive创建响应式数据,toRefs把普通数据转换成响应式数据

<template>
  <div class="home">
    <span>{{name}}</span>
    <span>{{num}}</span>
    <button @click="btn">按钮</button>
    <button @click="btn2">按钮2</button>
  </div>
</template>
<script>
// reactive 创建响应式数据对象 --类似data
import { reactive, toRefs } from '@vue/composition-api'
export default {
  name: 'Home',
  setup () {
    // state对象
    const state = reactive({
      name: 'hello world'
    })
    // modelData 
    const modelData = reactive({
      num: 1
    })
    const btn = () => modelData.num++
    const btn2 = () => {
      state.name = '我是不双向绑定的数据,没有toRefs转换不可更改'
      return state
    }
    return {
      ...state,
      ...toRefs(modelData), //把数据转换为响应式
      btn, // 事件
      btn2
    }
  }
}
</script>

tips:

  1. setup中是没有this
  2. 数据、方法都写在setup里面。
  3. 方法里改变值需return这个值
  4. 用了...运算符后使用reactive创建出来的数据都不是响应式数据了,需要使用toRefs转换为ref()类型的响应式数据
ref(推荐)
    • reactive一样创建响应式数据,但更推荐使用。
    <template>
     <div class="RefCom">
       <span>{{refCount}}</span>
       <button @click="refCount+=1">+1</button>
     </div>
    </template>
    <script>
    
    import { ref, reactive } from '@vue/composition-api'
    export default {
     name: 'RefCom',
     setup (props, { root }) {
       const refCount = ref(0) //创建响应式数据
       console.log(refCount.value)
       const data = reactive({
         refCount
       })
       console.log(data.refCount)
       data.refCount++
       console.log(data.refCount)
       return {
         ...data,
         refCount
       }
     }
    }
    </script>
    • 模板上的ref--获取dom
    //父组件
    <template>
      <div class="Father">
        <h1 ref="h1Ref">父组件</h1>
        <som ref="somRef"></som>
      </div>
    </template>
    <script>
    
    import som from './Som'
    import { ref, onMounted } from '@vue/composition-api'
    export default {
      name: 'Father',
      components: {
        som
      },
      setup (props, { root }) {
        const h1Ref = ref(null) //赋值null
        const somRef = ref(null)
        onMounted(() => {
          console.log(h1Ref.value, 'h1的dom')
          console.log(somRef.value, 'som的dom')
        })
        return {
          h1Ref,  //要和模板上ref值相同
          somRef
        }
      }
    }
    </script>
    
     //子组件
     <template>
      <div class="Som">
        <h3>子组件</h3>
      </div>
    </template>
    
    <script>
    export default {
      name: 'som',
      setup (props, { root }) {}
    }
    </script>

    tips:

    1. ref括号里的值就是refCount的值,括号里的值可以是各种类型的值。
    2. setup要通过xxx.value获取ref转换的值。
    3. 模板中无需通过xxx.value展示数据,直接{{xxx}}即可,在return时已经进行了转换了。
    4. ref包裹创建出来的值是个对象,里面就一个属性value
    5. reactive包裹ref创建的值不需要通过XXX.value访问
    6. 新的ref会覆盖旧的ref的值
    7. 通过isRef可以判断是否是ref创建出来的。
    computed

    计算属性:可创建可读可写的计算属性。

    <template>
      <div class="RefCom">
        <span>原值:{{refCount}}</span> |
        <span>
          计算属性值:{{ onlyReadComputed
          }}
        </span> |
        <button @click="refCount+=1">+1</button>
      </div>
    </template>
    <script>
    
    import { ref, computed } from '@vue/composition-api'
    export default {
      name: 'RefCom',
      setup (props, { root }) {
        const refCount = ref(0)
        // 只读的计算属性
        const onlyReadComputed = computed(() => refCount.value + 1)
        // 可读可写的计算属性
        const rwComputed = computed({
          get: () => refCount.value + 1,
          set: value => {
            refCount.value = value - 1
          }
        })
        console.log(onlyReadComputed, '只读计算属性的值')
        rwComputed.value = 11
        console.log(rwComputed, '可读可写计算属性的值')
        return {
          refCount,
          rwComputed,
          onlyReadComputed
        }
      }
    }
    </script>
    watch

    监听数据的变化

    <template>
     <div class="RefCom">
       <span>{{refCount}}</span>
       <span>{{name}}</span>
       <button @click="stopWatch">停止watch</button>
       <input v-model="inputValue" />
     </div>
    </template>
    <script>
    import { ref, reactive, watch, toRefs } from '@vue/composition-api'
    export default {
     name: 'watch',
     setup (props, { root }) {
       const refCount = ref(0)
       const inputValue = ref('')
       const state = reactive({
         name: '张总'
       })
       /* ---监听单个--- */
    
       // ref
       const stop = watch(
         refCount,
         (newValue, oldValue) => {
           console.log(refCount.value)
           console.log('新值:' + newValue, '旧的值:' + oldValue)
         }
       )
    
       const stopWatch = () => {
         stop()
       }
    
       // reactive
       watch(
         () => state.name,
         (newValue, oldValue) => {
           // console.log(refCount.value)
           console.log('新值:' + newValue, '旧的值:' + oldValue)
         }
       )
    
       /* ---监听多个--- */
       watch(
         [() => refCount, () => state.name],
         ([newRefCount, newName], [oldRefCount, oldName]) => {
           console.log('newRefCount:' + newRefCount.value, 'newName:' + newName)
           console.log('oldRefCount:' + oldRefCount.value, 'oldName:' + oldName)
         }
       )
    
       setTimeout(() => {
         refCount.value++
       }, 1000)
    
       setTimeout(() => {
         state.name = '李总'
       }, 3000)
    
       // 异步打印
       const asyncPrint = (val) => {
         return setTimeout(() => {
           console.log(val)
         }, 1000)
       }
    
       // ref
       watch(
         inputValue,
         (newValue, oldValue, clean) => {
           const timeId = asyncPrint(newValue)
           // 每当数据变化的时候清除定时器
           clean(() => clearTimeout(timeId))
         }
       )
    
       return {
         ...toRefs(state),
         refCount,
         stopWatch,
         inputValue
       }
     }
    }
    </script>

    tips:

    1. refreactive的值的监听方法不同,reactive需用方法返回值,() => xxx,ref可直接使用。
    2. 当监听多个时,不管是ref还是reactive创建的值,都需要用方法返回
    3. 在监听多个值时,用数组来解构新旧值时,新值和旧值分别在不同的数组里,和vue2.x不一样。
    4. watch监听返回新值、旧值时还返回了个函数,当前函数在watch被重复执行stop操作时发生,可做些清除操作。常见应用场景有防抖。
    5. 防抖:就是对于频繁触发的事件添加一个延时同时设定一个最小触发间隔,如果触发间隔小于设定的间隔,则清除原来的定时,重新设定新的定时;如果触发间隔大于设定间隔,则保留原来的定时,并设置新的定时;防抖的结果就是频繁的触发转变为触发一次
    生命周期
    • beforeCreate -> setup()
    • created -> setup
    • beforeMount -> onBeforeMount
    • mounted -> onMounted
    • beforeUpdate -> onBeforeUpdate
    • updated -> onUpdated
    • beforeDestroy -> onBeforeUnmount
    • destroyed -> onUnmounted
    • errorCaptured -> onErrorCaptured
    <template>
      <div class="Father">
     
      </div>
    </template>
    <script>
    
    
    import { onMounted, onUpdated, onBeforeUnmount } from '@vue/composition-api'
    export default {
      name: 'Father',
    
      setup (props, { root }) {
    
        onMounted(() => {
          console.log('onMounted')
        })
    
        onUpdated(() => {
          console.log('onUpdated')
        })
    
        onBeforeUnmount(() => {
          console.log('onBeforeUnmount')
        })
    
      }
    }
    </script>
    

    tips:

    1. 去除了beforeCreatecreated生命周期,直接就在setup中,setup执行顺序 是beforeCreate后,created
    2. 其他生命周期就在原本前加上on,功能没有什么变化,且定义在setup函数中
    3. 推荐请求都放在onMounted
    依赖注入
    • provide
    //父组件
    <template>
      <div class="Father">
        <h1>父组件</h1>
        <button @click="color='red'">红色</button>
        <button @click="color='blue'">蓝色</button>
        <button @click="color='yellow'">黄色</button>
        <som></som>
      </div>
    </template>
    <script>
    
    import som from './Som'
    import { provide, ref } from '@vue/composition-api'
    export default {
      name: 'Father',
      components: {
        som
      },
      setup (props, { root }) {
        const color = ref('red') //响应式的值,父组件修改可影响子孙后代
        //注入值
        provide('color', color)
        return {
          color
        }
      }
    }
    </script>
     //子组件
     <template>
      <div class="Som">
        <h3>子组件</h3>
        <Grandson />
      </div>
    </template>
    
    <script>
    import Grandson from './Grandson'
    export default {
      name: 'som',
      components: {
        Grandson
      },
      setup (props, { root }) { }
    }
    </script>
    • inject
    //孙子组件
    <template>
      <div class="Grandson">
        <h5 :style="{color:color}">孙子组件</h5>
      </div>
    </template>
    
    <script>
    import { inject } from '@vue/composition-api'
    export default {
      name: 'Grandson',
      setup (props, { root }) {
      //接收值
        const color = inject('color') 
        return {
          color
        }
      }
    }
    </script>
    路由跳转
    <template>
      <div class="home">
        <button @click="jump">跳转</button>
      </div>
    </template>
    <script>
    export default {
      name: 'Home',
      setup (props, { root }) {
       const jump = () => root.$router.push('/about')
        return {
          jump
        }
      }
    }
    </script>
    tips: root指代的就是vue对象,即this,且名字是不可更改的。
    props
    
    //父
    <template>
      <div class="about">
        <h1>This is an about page</h1>
        <div>{{num}}</div>
        <button @click="btn">增加</button>
        <HelloWorld msg="我是props传进去的值" />
      </div>
    </template>
    
    
    //子
    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      },
      setup (props) {
        console.log(props)
      }
    }
    </script>
    
    未完待续~~~~

    yaoxfly
    431 声望28 粉丝