详细讲解了vue3.0常用的composition API 包括,setup函数及props与context参数,和用法,讲解了vue3.0生命周期,watch与wacteFFect函数的用法与区别,computed函数及set与get用法,定义参数两种方式ref与reactive详解,定期全局变量globalProperties,调用组件use,只读属性readonly,ref,torefs,isref,unref

Vue 3.0 于 2020-09-18 发布了,使用了 Typescript 进行了大规模的重构,带来了 Composition API RFC 版本,类似 React Hook 一样的写 Vue,可以自定义自己的 hook ,让使用者更加的灵活。

为什么推出3.0?

  • 对 TypeScript 支持不友好(所有属性都放在了 this 对象上,难以推倒组件的数据类型)
  • 大量的 API 挂载在 Vue 对象的原型上,难以实现 TreeShaking
    • TreeShaking:当我引入一个模块的时候,我不引入这个模块的所有代码,我只引入我需要的代码
  • 架构层面对跨平台 dom 渲染开发支持不友好
  • CompositionAPI。受 ReactHook 启发
  • 更方便的支持了 jsx
  • Vue 3 的 Template 支持多个根标签,Vue 2 不支持
  • 对虚拟 DOM 进行了重写、对模板的编译进行了优化操作

一、setup

为我们使用 vue3 的 Composition API 新特性提供了统一的入口, 有些文章说setup 函数会在 beforeCreate 之后、created 之前执行但实践证明这个说法不正确如下图打印顺序, vue3 也是取消了这两个钩子,统一用 setup 代替, 该函数相当于一个生命周期函数,vue 中过去的 data,methods,watch 等全部都用对应的新增 api 写在 setup()函数中。

1、setup参数

  1. props: 组件传入的属性
  2. context:attrs,emit,slots

props

  setup中接受的props是响应式的, 当传入新的props 时,会及时被更新。由于是响应式的, 所以不可以使用ES6解构,解构会消除它的响应式。需要结构可以用toRefs()

context

  setup中不能访问Vue2中最常用的this对象,所以context中就提供了this中最常用的三个属性:attrsslot 和emit,分别对应Vue2.x中的 $attr属性、slot插槽 和$emit发射事件。

简单用法

 

2、reactive、ref与toRefs

 

在vue2.x中, 定义数据都是在data中, 但是Vue3.x 可以使用reactiveref来进行数据定义。

区别

  reactive用于处理对象的双向绑定,ref则处理js基础类型的双向绑定,reactive不能代理基本类型,例如字符串、数字、boolean等。

简单用法

<template>
  <div>
    <span>{{ year }}</span
    ><span>{{ user.name }}</span
    ><span>{{ user.label }}</span>
  </div>
</template>

<script>
import { reactive, ref } from "vue";
export default {
  props: {
    data: {
      type: Object,
      default: {
        id: 1,
        name: "匹配",
      },
    },
  },
  components: {},

  setup(props, con) {
    const year = ref(10);
    const user = reactive({
      name: "夏利",
      label: "",
    });
  //这里ref取值需要加value
if (year.value > 5) { user.label = "牛逼"; } else { user.label = "一般"; } return { year, user, }; }, }; </script>

  不能直接对user进行结构, 这样会消除它的响应式, 这里就和上面我们说props不能使用ES6直接解构就呼应上了。那我们就想使用解构后的数据怎么办,解决办法就是使用toRefs

   return {
        year,
        // 使用reRefs
        ...toRefs(user)
    }

3、生命周期

 

  我们可以看到beforeCreatecreatedsetup替换了(但是Vue3中你仍然可以使用, 因为Vue3是向下兼容的, 也就是你实际使用的是vue2的)。其次,钩子命名都增加了on; Vue3.x还新增用于调试的钩子函数onRenderTriggeredonRenderTricked

<script>
import {
  defineComponent,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onErrorCaptured,
  onRenderTracked,
  onRenderTriggered,
} from "vue";

export default defineComponent({
  // beforeCreate和created是vue2的
  beforeCreate() {
    console.log("------beforeCreate-----");
  },
  created() {
    console.log("------created-----");
  },
  setup() {
    console.log("------setup-----");

    // vue3.x生命周期写在setup中
    onBeforeMount(() => {
      console.log("------onBeforeMount-----");
    });
    onMounted(() => {
      console.log("------onMounted-----");
    });
    // 调试哪些数据发生了变化
    onRenderTriggered((event) => {
      console.log("------onRenderTriggered-----", event);
    });
  },
});
</script>

 4、全局定义属性 globalProperties

  在项目中往往会全局定义公共属性或方法,方便我们组件间调用。

import { createApp } from 'vue'
import App from './App.vue'
import utils from './utils'

// vue2.0写法
let vue=new Vue()
vue.prototype.utils=utils
// vue3.0写法
const app=createApp(App)
app.config.globalProperties.utils=utils
app.mount(
'#app')

vue3.0中使用全局定义的属性

<script>
import { getCurrentInstance } from "vue";
export
default { components: {}, setup(props, con) {
const { ctx } = getCurrentInstance(); console.log(ctx.utils);
}, };
</script>

5、use与plugin

定义一个组件

 通过use引入

 6、readonly 只读属性

 

在使用readonly后重新进行复制是不允许修改的,这个api不常用

7、computed计算api

这里引入了proxy内的类似写法,有了set与get的内容写法,该函数用来创造计算属性,和过去一样,它返回的值是一个 ref 对象。里面可以传方法,或者一个对象,对象中包含 set()、get()方法。

7.1 创建只读的计算属性

<template>
  <div>
    <p>refCount: {{refCount}}</p>
    <p>计算属性的值computedCount : {{computedCount}}</p>
    <button @click="refCount++">refCount + 1</button>
  </div>
</template>

<script>
import { computed, ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(1)
    // 只读
    let computedCount = computed(() => refCount.value + 1) //2
    console.log(computedCount)
    return {
      refCount,
      computedCount
    }
  }
};
</script>

7.2 通过 set()、get()方法创建一个可读可写的计算属性

<template>
  <div>
    <p>refCount: {{refCount}}</p>
    <p>计算属性的值computedCount : {{computedCount}}</p>
    <button @click="refCount++">refCount + 1</button>
  </div>
</template>

<script>
import { computed, ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(1)
    // 可读可写
    let computedCount = computed({
      // 取值函数
      get: () => refCount.value + 1,
      // 赋值函数
      set: val => {
        refCount.value = refCount.value -5
      }
    })
  //触发get函数 console.log(computedCount.value)
// 为计算属性赋值的操作,会触发 set 函数 computedCount.value = 10 console.log(computedCount.value) // 触发 set 函数后,count 的值会被更新 console.log(refCount.value)
return { refCount, computedCount } } }; </script>

8、watch与watchEffect

8.1watch

  watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。

    *   1、需要一个明确的数据资源
         *   2、需要有一个回调函数
         *   3、等到改变才会执行函数
<script>
import { ref, watch } from "vue";
export default {
  components: {},
  setup(props, con) {
    const count = ref(0);
    const name = ref("iuuggu");
    setTimeout(() => {
      (count.value = 1), (name.value = "这一行山");
    }, 200);
    watch(
      () => {
        /*
         *   1、需要一个明确的数据资源
         *   2、需要有一个回调函数
         *   3、等到改变才会执行函数
         *
         */
        return count.value;
      },
      () => {}
    );
    // 4、ref简写 ,reactive不可以
    watch(count, () => {});
    // 5、监听多个
    watch(
      () => {
        return [count, name];
      },
      ([newv, oldv], [newv, oldv]) => {}
    );
    // 6、onTrack,onTrigger
    watch(
      () => {
        return [count, name];
      },
      ([newv, oldv], [newv, oldv]) => {},
      {
        onTrack(e) {},
        onTrigger(e) {},
      }
    );
  },
};
</script>

8.1.1 监听数据源

reactive

setup(props, context) {
    const state = reactive<Person>({ name: 'vue', age: 10 })

    watch(
      () => state.age,
      (age, preAge) => {
        console.log(age); // 100
        console.log(preAge); // 10
      }
    )
    // 修改age 时会触发watch 的回调, 打印变更前后的值
    state.age = 100
    return {
      ...toRefs(state)
    }
  }

ref

setup(props, context) {
    const age = ref<number>(10);

    watch(age, () => console.log(age.value)); // 100

    // 修改age 时会触发watch 的回调, 打印变更后的值
    age.value = 100
    return {
      age
    }
  }

8.1.2监听多个

setup(props, context) {
    const state = reactive<Person>({ name: 'vue', age: 10 })

    watch(
      [
      ()
=> state.age,
      () => state.name
    ], ([newName, newAge], [oldName, oldAge])
=> { console.log(newName); console.log(newAge); console.log(oldName); console.log(oldAge); } ) // 修改age 时会触发watch 的回调, 打印变更前后的值, 此时需要注意, 更改其中一个值, 都会执行watch的回调 state.age = 100 state.name = 'vue3' return { ...toRefs(state) } }

8.1.3stop 停止监听

在 setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可,语法如下:

setup(props, context) {
    const state = reactive<Person>({ name: 'vue', age: 10 })

    const stop =  watch(
      [() => state.age, () => state.name],
      ([newName, newAge], [oldName, oldAge]) => {
        console.log(newName);
        console.log(newAge);

        console.log(oldName);
        console.log(oldAge);
      }
    )
    // 修改age 时会触发watch 的回调, 打印变更前后的值, 此时需要注意, 更改其中一个值, 都会执行watch的回调
    state.age = 100
    state.name = 'vue3'

    setTimeout(()=> {
      stop()
      // 此时修改时, 不会触发watch 回调
      state.age = 1000
      state.name = 'vue3-'
    }, 1000) // 1秒之后讲取消watch的监听

    return {
      ...toRefs(state)
    }
  }

8.2 watchEffect

 该函数有点像update函数,但他执行在update与beforeuodate之前。

       *    1、首次加载会立即执行
         *    2、响应的最终所有依赖监听变化(数据改变)
         *    3、在卸载onUnmounte时自动停止
         *    4、执行stop就会停止监听,否则一直监听
         *    5、异步函数先执行再去监听改变
<script>
import { watchEffect, ref, onMounted } from "vue";
export default {
  components: {},
  //con==context(attrs,emit,slots)
  setup(props, con) {
    const count = ref(0);
    setTimeout(() => {
      count.value = 1;
    }, 2000);
    const stop = watchEffect(
      () => {
        /*
         *    1、首次加载会立即执行
         *    2、响应的最终所有依赖监听变化(数据改变)
         *    3、在卸载onUnmounte时自动停止
         *    4、执行stop就会停止监听,否则一直监听
         *    5、异步函数先执行再去监听改变
         */
      },
      {
        // 6、在update之后执行
        flush: "post",
        // 同步执行
        flush: "async",
      }
    );
    setTimeout(() => {
      stop();
    }, 4000);

    // 7、组件挂在ref
    const myRef = ref(null);
    // 避免监听时先见听到null 在监听到h1
    onMounted(() => {
      watchEffect(() => {
        console.log(myRef.value);
      });
    });
    // 8、debugging  开发模式使用
    watchEffect(
      () => {
        console.log(count.value);
      },
      {
        onTrack(e) {
          // 监听到count和改变count
        },
        onTrigger(e) {
          // count改变了会触发
        },
      }
    );
    return {
      myRef,
      count,
    };
  },
};
</script>

 9、ref,torefs,isref,unref

9.1ref

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

ref数据

<template>
    <div class="mine">
        {{count}} // 10
    </div>
</template>

<script >
import {ref } from 'vue';
export default {
  setup() {
    const count = ref(10)
    // 在js 中获取ref 中定义的值, 需要通过value属性
    console.log(count.value);
    return {
       count
    }
   }
}
</script>

ref 访问dom

 

 

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

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

<template>
    <div class="mine">
        {{count}} -{{t}} // 10 -100
    </div>
</template>

<script >
import  reactive, ref, toRefs } from 'vue';
export default {
  setup() {
    const count = ref(10)
    const obj = reactive({
      t: 100,
      count
    })
    // 通过reactive 来获取ref 的值时,不需要使用.value属性
    console.log(obj.count);
    return {
       ...toRefs(obj)
    }
   }
}
</script>

9.2 toRefs() 函数

toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据

<template>
  <div>
    <p>{{ count }} - {{ name }}</p>
    <button @click="count += 1">+1</button>
    <button @click="add">+1</button>
  </div>
</template>

<script>
import { reactive, toRefs } from "@vue/composition-api";
export default {
  setup() {
    // 响应式数据
    const state = reactive({ count: 0, name: "zs" });
    // 方法
    const add = () => {
      state.count += 1;
    };
    return {
      // 非响应式数据
      // ...state,
      // 响应式数据
      ...toRefs(state),
      add
    };
  }
};
</script>

9.3 isref

isRef() 用来判断某个值是否为 ref() 创建出来的对象

 setup(props, context) {
    const name: string = 'vue'
    const age = ref(18)
    console.log(isRef(age)); // true
    console.log(isRef(name)); // false

    return {
      age,
      name
    }
  }

9.4 unref

unRef() 用来判断某个值是否为 ref() 创建出来的对象有抛出该对象

 setup(props, context) {
    const name: string = 'vue'
    const age = ref(18)
    console.log(unRef(age)); // 18
    console.log(unRef(name)); // vue

    return {
      age,
      name
    }
  }

 

版权声明:本文为ypSharing原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ypSharing/p/14780586.html