03.vue3.0-composition API

作者: 东邪_黄药师 | 来源:发表于2021-06-10 10:34 被阅读0次

    setup

    setup() 函数是 vue3 中,专门为组件提供的新属性。它为我们使用 vue3 的 Composition API 新特性提供了统一的入口。

    • 新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次
    • 函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
    • 1.执行时机
      setup 函数会在 beforeCreate 之后、created 之前执行
      1. 接收 props 数据
    setup(props) {
        console.log(props.p1)
    }
    
    • 3.context
      setup 函数的第二个形参是一个上下文对象,这个上下文对象中包含了一些有用的属性,这些属性在 vue 2.x 中需要通过 this 才能访问到,在 vue 3.x 中,它们的访问方式如下:
    const MyComponent = {
      setup(props, context) {
        context.attrs
        context.slots
        context.parent
        context.root
        context.emit
        context.refs
      }
    

    注意:在 setup() 函数中无法访问到 this

    ref

    • 1.基础用法
      ref() 函数用来根据给定的值创建一个响应式的数据对象,ref() 函数调用的返回值是一个对象,这个对象上只包含一个 .value 属性:
    import { ref } from "vue";
    export default {
      name: "App",
      setup() {
        // 创建响应式数据对象 count,初始值为 0
        const count = ref(0);
       // 如果要访问 ref() 创建出来的响应式数据对象的值,必须通过 .value 属性才可以
        console.log(count.value); // 输出 0
        // 让 count 的值 +1
        count.value++;
        // 再次打印 count 的值
        console.log(count.value); // 输出 1
      }
    };
    
    • 2.在 template 中访问 ref 创建的响应式数据
    <template>
     <div>
       {{ count }}
     </div>
     <div>
       <button @click="chang">按钮+1</button>
     </div>
    </template>
    
    <script lang="ts">
    import { ref } from "vue";
    export default {
      name: "App",
      setup() {
        // 创建响应式数据对象 count,初始值为 0
        const count = ref(0);
       // 如果要访问 ref() 创建出来的响应式数据对象的值,必须通过 .value 属性才可以
        console.log(count.value); // 输出 0
        function chang () {
          count.value++;
          console.log(  count.value++ )
        }
        return {
          count,
          chang
        }
      }
    };
    </script>
    

    reactive

    reactive() 函数接收一个普通对象,返回一个响应式的数据对象。

    <template>
      <div>人生依然很美好</div>
      <h2>name:{{ user.name }}</h2>
      <h2>age:{{ user.age }}</h2>
      <h2>wifeName:{{ user.wife.name }}</h2>
      <h2>wifeAge:{{ user.wife.age }}</h2>
      <h2>wife的车:
        <div v-for="item in user.wife.cars" :key="item.index" >{{item}}</div>
      </h2>
       <button @click="updateUser">changs</button>
    </template>
    
    <script lang="ts">
    import { defineComponent, reactive } from "vue";
    export default defineComponent({
      name: "App",
      setup () {
        const user = reactive({
          name: "小米",
          age: 22,
          password: 123456,
          wife: {
            name: "小甜",
            age: 18,
            cars: ["奔驰", "宝马", "桑塔纳"],
          },
        })
        console.log(user)
        // 点击事件
        const updateUser = () => {
          user.name += '--'
          user.age += 1
          user.wife.name += '++'
          user.wife.age += 2
          user.wife.cars[0] = '活路没有了'
        }
        return {
          user,
          updateUser
        }
      }
    })
    </script>
    
    image.png
    • 在 reactive 对象中访问 ref 创建的响应式数据
      当把 ref() 创建出来的响应式数据对象,挂载到 reactive() 上时,会自动把响应式数据对象展开为原始的值,不需通过 .value 就可以直接被访问,例如:
    const count = ref(0)
    const state = reactive({
      count
    })
    console.log(state.count) // 输出 0
    state.count++ // 此处不需要通过 .value 就能直接访问原始值
    console.log(count) // 输出 1
    

    注意:新的 ref 会覆盖旧的 ref,示例代码如下:

    const c1 = ref(0)
    const state = reactive({
      c1
    })
    // 再次创建 ref,命名为 c2
    const c2 = ref(9)
    // 将 旧 ref c1 替换为 新 ref c2
    state.c1 = c2
    state.c1++
    console.log(state.c1) // 输出 10
    console.log(c2.value) // 输出 10
    console.log(c1.value) // 输出 0
    

    computed

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

    import { computed } from 'vue'
    
    • 1 创建只读的计算属性
      在调用 computed() 函数期间,传入一个 function 函数,可以得到一个只读的计算属性,示例代码如下:
    <template>
    <div>
    <p>{{ refCount }}</p>
    <p>{{ plusOne  }}</p>
    <button @click="refCount+=1">加1</button>
    </div>
    </template>
    
    <script lang="ts">
    import { defineComponent, computed, ref } from 'vue'
    export default defineComponent({
       name:'App',
       setup () {
         const refCount = ref(0)
         const plusOne = computed( () => refCount.value + 1 )
         return {
           refCount,
           plusOne  
         }
       }
    })
    </script>
    
    • 2 创建可读可写的计算属性
      在调用 computed() 函数期间,传入一个包含 get 和 set 函数的对象,可以得到一个可读可写的计算属性,示例代码如下:
    <template>
      <div>
        <p>{{ refCount }}</p>
        <p>{{ plusOne }}</p>
        <button @click="refCount += 1">加1</button>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent, computed, ref } from "vue";
    export default defineComponent({
      name: "App",
      setup() {
        const refCount = ref(0);
        const plusOne = computed({
          // 取值函数
          get: () => refCount.value + 1,
          // 赋值函数
          set: (val) => {
            refCount.value = val - 1;
          },
        });
        // 为计算属性赋值的操作,会触发 set 函数
        plusOne.value = 9;
        // 触发 set 函数后,count 的值会被更新
        console.log(refCount.value); // 输出 8
        return {
          refCount,
          plusOne,
        };
      },
    });
    </script>
    

    watch

    watch() 函数用来监视某些数据项的变化,从而触发某些特定的操作,使用之前需要按需导入:

    import { watch } from 'vue'
    
    • 1.基本用法
    const count = ref(0)
    // 定义 watch,只要 count 值变化,就会触发 watch 回调
    // watch 会在创建时会自动调用一次
    watch(() => console.log(count.value))
    // 输出 0
    
    setTimeout(() => {
      count.value++
      // 输出 1
    }, 1000)
    

    2 监视指定的数据源

    监视reactive类型的数据源:

    // 定义数据源
    const state = reactive({ count: 0 })
    // 监视 state.count 这个数据节点的变化
    watch(
      () => state.count,
      (count, prevCount) => {
        /* ... */
      }
    )
    

    监视 ref类型的数据源:

    // 定义数据源
    const count = ref(0)
    // 指定要监视的数据源
    watch(count, (count, prevCount) => {
      /* ... */
    })
    
    • 3 监视多个数据源
      监视 reactive类型的数据源:
    const state = reactive({ count: 0, name: 'zs' })
    
    watch(
      [() => state.count, () => state.name], // Object.values(toRefs(state)),
      ([count, name], [prevCount, prevName]) => {
        console.log(count) // 新的 count 值
        console.log(name) // 新的 name 值
        console.log('------------')
        console.log(prevCount) // 旧的 count 值
        console.log(prevName) // 新的 name 值
      },
      {
        lazy: true // 在 watch 被创建的时候,不执行回调函数中的代码
      }
    )
    
    setTimeout(() => {
      state.count++
      state.name = 'ls'
    }, 1000)
    

    监视 ref 类型的数据源:

    const count = ref(0)
    const name = ref('zs')
    
    watch(
      [count, name], // 需要被监视的多个 ref 数据源
      ([count, name], [prevCount, prevName]) => {
        console.log(count)
        console.log(name)
        console.log('-------------')
        console.log(prevCount)
        console.log(prevName)
      },
      {
        lazy: true
      }
    )
    
    setTimeout(() => {
      count.value++
      name.value = 'xiaomaolv'
    }, 1000)
    
      1. 清除监视
        在 setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可,语法如下:
    // 创建监视,并得到 停止函数
    const stop = watch(() => {
      // >_< 
    })
    // 调用停止函数,清除对应的监视
    stop()
    
    • 5 在 watch 中清除无效的异步任务
      有时候,当被 watch 监视的值发生变化时,或 watch 本身被 stop 之后,我们期望能够清除那些无效的异步任务,此时,watch 回调函数中提供了一个 cleanup registrator function 来执行清除的工作。这个清除函数会在如下情况下被调用:
    • watch 被重复执行了
    • watch 被强制 stop 了
    • Template 中的代码示例如下:
    /* template 中的代码 */ <input type="text" v-model="keywords" />
    

    Script 中的代码示例如下:

    // 定义响应式数据 keywords
    const keywords = ref('')
    
    // 异步任务:打印用户输入的关键词
    const asyncPrint = val => {
      // 延时 1 秒后打印
      return setTimeout(() => {
        console.log(val)
      }, 1000)
    }
    // 定义 watch 监听
    watch(
      keywords,
      (keywords, prevKeywords, onCleanup) => {
        // 执行异步任务,并得到关闭异步任务的 timerId
        const timerId = asyncPrint(keywords)
    
        // 如果 watch 监听被重复执行了,则会先清除上次未完成的异步任务
        onCleanup(() => clearTimeout(timerId))
      },
      // watch 刚被创建的时候不执行
      { lazy: true }
    )
    
    // 把 template 中需要的数据 return 出去
    return {
      keywords
    }
    

    生命周期

    新版的生命周期函数,可以按需导入到组件中,且只能在 setup() 函数中使用,代码示例如下:

    import { onMounted, onUpdated, onUnmounted } from '@vue/composition-api'
    
    const MyComponent = {
      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 -> onBeforeMount
    mounted -> onMounted
    beforeUpdate -> onBeforeUpdate
    updated -> onUpdated
    beforeDestroy -> onBeforeUnmount
    destroyed -> onUnmounted
    errorCaptured -> onErrorCaptured
    

    provide & inject

    provide()inject()可以实现嵌套组件之间的数据传递。这两个函数只能在setup() 函数中使用。父级组件中使用 provide() 函数向下传递数据;子级组件中使用inject()获取上层传递过来的数据。

    父组件
    <template>
      <h1>父组件</h1>
      <p>当前颜色: {{color}}</p>
      <button @click="color='red'">红</button>
      <button @click="color='yellow'">黄</button>
      <button @click="color='blue'">蓝</button>
      
      <hr>
      <Son />
    </template>
    
    <script lang="ts">
    import { provide, ref } from 'vue'
    
    import Son from './Son.vue'
    export default {
      name: 'ProvideInject',
      components: {
        Son
      },
      setup() {
        
        const color = ref('red')
    
        provide('color', color)
    
        return {
          color
        }
      }
    }
    </script>
    
    子组件
    <template>
      <div>
        <h2>子组件</h2>
        <hr>
        <GrandSon />
      </div>
    </template>
    
    <script lang="ts">
    import GrandSon from './GrandSon.vue'
    export default {
      components: {
        GrandSon
      },
    }
    </script>
    
    孙子组件
    <template>
      <h3 :style="{color}">孙子组件: {{color}}</h3>
      
    </template>
    
    <script lang="ts">
    import { inject } from 'vue'
    export default {
      setup() {
        const color = inject('color')
    
        return {
          color
        }
      }
    }
    </script>
    

    ref在元素组件中的引用方式

    通过 ref() 还可以引用页面上的元素或组件。

    <template>
      <div>
        <h3 ref="h3Ref">TemplateRefOne</h3>
      </div>
    </template>
    
    <script>
    import { ref, onMounted } from '@vue/composition-api'
    
    export default {
      setup() {
        // 创建一个 DOM 引用
        const h3Ref = ref(null)
    
        // 在 DOM 首次加载完毕之后,才能获取到元素的引用
        onMounted(() => {
          // 为 dom 元素设置字体颜色
          // h3Ref.value 是原生DOM对象
          h3Ref.value.style.color = 'red'
        })
    
        // 把创建的引用 return 出去
        return {
          h3Ref
        }
      }
    }
    </script>
    

    相关文章

      网友评论

        本文标题:03.vue3.0-composition API

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